diff -u --recursive --new-file v2.1.62/linux/CREDITS linux/CREDITS --- v2.1.62/linux/CREDITS Mon Nov 3 13:04:25 1997 +++ linux/CREDITS Mon Nov 10 16:12:38 1997 @@ -643,12 +643,13 @@ S: United Kingdom N: Ron Holt -E: ron@novell.com +E: ron@caldera.com +W: http://www.holt.org/ D: Kernel development -D: Contributed to kernel Wabi/Wine support -S: Novell, Inc. -S: 1700 South 122 East, Mailstop CP-1 -S: Provo, Utah 84606 +D: Minor kernel modifications to support Wabi and Wine +S: Caldera, Inc. +S: 240 West Center St. +S: Orem, Utah 84059-1920 S: USA N: Rob W. W. Hooft @@ -867,6 +868,12 @@ S: Am Muehlenweg 38 S: D53424 Remagen S: Germany + +N: Achim Leubner +E: achim@vortex.de +D: GDT SCSI Disk Array Controller driver +S: ICP vortex Computersysteme GmbH +S: Flein, Germany N: Phil Lewis E: beans@bucket.ualr.edu diff -u --recursive --new-file v2.1.62/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.62/linux/Documentation/Configure.help Mon Nov 3 13:04:25 1997 +++ linux/Documentation/Configure.help Sat Nov 8 11:39:12 1997 @@ -419,6 +419,33 @@ here and read Documentation/modules.txt. The module will be called raid0.o. If unsure, say Y. +RAID-1 (mirroring) mode +CONFIG_MD_MIRRORING + A RAID-1 set consists of several disk drives which are exact copies + of each other. In the event of a mirror failure, the RAID driver + will continue to use the operational mirrors in the set, providing + an error free MD device to the higher levels of the kernel. In + a set with N drives, the available space is the capacity of a single + drive, and the set protects against a failure of (N - 1) drives. + raidtools, a set of user-space tools which create and maintain + RAID1/4/5 sets, is available at: + ftp://ftp.kernel.org/pub/linux/daemons/raid + http://luthien.nuclecu.unam.mx/~miguel/raid + +RAID-4/RAID-5 mode +CONFIG_MD_RAID5 + A RAID-5 set of N drives with a capacity of C MB per drive provides + the capacity of C * (N - 1) drives, and protects against a failure + of a single drive. For a given sector (row) number, (N - 1) drives + contain data sectors, and one drive contains the parity protection. + For a RAID-4 set, the parity blocks are present on a single drive, + while a RAID-5 set distributes the parity accross the drives in one + of the available parity distribution methods. + raidtools, a set of user-space tools which create and maintain + RAID1/4/5 sets, is available at: + ftp://ftp.kernel.org/pub/linux/daemons/raid + http://luthien.nuclecu.unam.mx/~miguel/raid + Support for Deskstation RPC44 CONFIG_DESKSTATION_RPC44 This is a machine with a R4400 100 MHz CPU. To compile a Linux @@ -2302,6 +2329,15 @@ and read Documentation/modules.txt. The module will be called AM53C974.o. +GDT SCSI Disk Array Controller support +CONFIG_SCSI_GDTH + This is a driver for all SCSI Disk Array Controllers (EISA/ISA/PCI) + manufactured by ICP vortex. It is documented in the kernel source in + drivers/scsi/gdth.c and drivers/scsi/gdth.h. This driver is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt. + IOMEGA Parallel Port ZIP drive SCSI support CONFIG_SCSI_PPA This driver supports the parallel port version of IOMEGA's ZIP drive @@ -5219,6 +5255,26 @@ removed from the running kernel whenever you want). The module will be called joystick.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. + +Radio support +CONFIG_MISC_RADIO + If you have a radio card (you will probably know if you do!), then + you will want to say "y" here and make a character device file + (usually /dev/radio) with major number 10 and minor 129 using mknod + ("man mknod"). And then, don't forget to pick up some useful tools + to use said device (you _might_ find something at ftp.lmh.ox.ac.uk: + /users/weejock/linux/radio/, but I haven't written anything too + useful yet...) + +AIMSlab RadioTrack card +CONFIG_RADIO_RTRACK + Choose "y" here if you have one of these, and then fill in the port + address below. + +RadioTrack i/o port +CONFIG_RADIO_RTRACK_PORT + Enter either 0x30f or 0x20f here. The card default is 0x30f, if you + haven't changed the jumper setting on the card. ARC console time CONFIG_RTC_ARC diff -u --recursive --new-file v2.1.62/linux/Documentation/devices.tex linux/Documentation/devices.tex --- v2.1.62/linux/Documentation/devices.tex Tue Sep 23 16:48:46 1997 +++ linux/Documentation/devices.tex Mon Nov 10 10:56:26 1997 @@ -4,6 +4,8 @@ % pages to print... :-) If you're actually putting this in print, you % may wish to change these. % +% $Id: devices.tex,v 1.3 1997/11/10 01:29:35 hpa Exp $ +% \oddsidemargin=0in \textwidth=6.5in \topmargin=0in @@ -43,39 +45,29 @@ {\end{tabbing}} % % If you reformat this document, *please* make sure this information -% gets included! +% gets included! This list changes frequently, so it is crucial to +% know the date of the revision. % \title{{\bf Linux Allocated Devices}} \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$} -\date{Last revised: September 11, 1997} +\date{Last revised: November 9, 1997} \maketitle % \noindent -This list is the successor to Rick Miller's Linux Device List, which -he stopped maintaining when he got busy with other things in 1993. It -is a registry of allocated major device numbers, as well as the -recommended {\file /dev} directory nodes for these devices. +This list is the Linux Device List, the official registry of allocated +device numbers and {\file /dev} directory nodes for the Linux +operating system. The latest version of this list is included with the Linux kernel -sources in \LaTeX\ and ASCII form. It is also available separate from -{\url ftp://ftp.kernel.org/pub/linux/docs/device-list/}. In case of -discrepancy, the \LaTeX\ version is authoritative. +sources in \LaTeX\ and ASCII form. It is also available separately +from {\url ftp://ftp.kernel.org/pub/linux/docs/device-list/}. In case +of discrepancy between the text and \LaTeX\ versions, the \LaTeX\ +version is authoritative. This document is included by reference into the Linux Filesystem Standard (FSSTND). The FSSTND is available from {\url ftp://tsx-11.mit.edu/pub/linux/docs/linux-standards/fsstnd/}. -To have a major number allocated, or a minor number in situations -where that applies (e.g.\ busmice), please contact me with the -appropriate device information. I {\em very\/} much appreciate if you -send me a device description in the same format as the ones already in -this file. 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. - -NOTE: When sending me mail, {\em please\/} include the word ``device'' -in the subject so your mail won't accidentally get buried! - Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga platform only. Allocations marked (68k/Atari) apply to Linux/68k on the Atari platform only. @@ -92,6 +84,28 @@ on this list. Any such information requests will be deleted without reply. +\section{How to submit a device entry} + +To have a major number allocated, or a minor number in situations +where that applies (e.g. busmice), please contact me with the +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. +This is necessary for me to be able to keep this list up to date and +correct in a timely manner. First of all, {\em 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. + +Second, please include a description of the device {\em 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. + +Your cooperation is appreciated. + \section{Major numbers} \begin{devicelist} @@ -221,7 +235,12 @@ \major{88}{}{char }{COMX synchronous serial card} \major{89}{}{char }{I$^2$C bus interface} \major{90}{}{char }{Memory Technology Device (RAM, ROM, Flash)} -\major{91}{--119}{}{Unallocated} +\major{91}{}{char }{CAN-Bus devices} +\major{92}{}{char }{Reserved for ith Kommunikationstechnik MIC ISDN card} +\major{93}{}{char }{IBM Smart Capture Card frame grabber} +\major{94}{}{char }{miroVIDEO DC10/30 capture/playback device} +\major{95}{}{char }{IP Filter} +\major{96}{--119}{}{Unallocated} \major{120}{--127}{}{Local/experimental use} \major{128}{--239}{}{Unallocated} \major{240}{--254}{}{Local/experimental use} @@ -1506,7 +1525,43 @@ \end{devicelist} \begin{devicelist} -\major{91}{--119}{}{Unallocated} +\major{91}{}{char }{CAN-Bus controller} + \minor{0}{/dev/can0}{First CAN-Bus controller} + \minor{1}{/dev/can1}{Second CAN-Bus controller} + \minordots +\end{devicelist} + +\begin{devicelist} +\major{92}{}{char }{Reserved for ith Kommunikationstechnik MIC ISDN card} +\end{devicelist} + +\begin{devicelist} +\major{93}{}{char }{IBM Smart Capture Card frame grabber} + \minor{0}{/dev/iscc0}{First Smart Capture Card} + \minor{1}{/dev/iscc1}{Second Smart Capture Card} + \minordots + \minor{128}{/dev/isccctl0}{First Smart Capture Card control} + \minor{129}{/dev/isccctl1}{Second Smart Capture Card control} + \minordots +\end{devicelist} + +\begin{devicelist} +\major{94}{}{char }{miroVIDEO DC10/30 capture/playback device} + \minor{0}{/dev/dcxx0}{First capture card} + \minor{1}{/dev/dcxx1}{Second capture card} + \minordots +\end{devicelist} + +\begin{devicelist} +\major{95}{}{char }{IP filter} + \minor{0}{/dev/ipl}{Filter control device/log file} + \minor{1}{/dev/ipnat}{NAT control device/log file} + \minor{2}{/dev/ipstate}{State information log file} + \minor{3}{/dev/ipauth}{Authentication control device/log file} +\end{devicelist} + +\begin{devicelist} +\major{96}{--119}{}{Unallocated} \end{devicelist} \begin{devicelist} @@ -1593,11 +1648,11 @@ point to the ``cooked'' devices ({\file /dev/st*} and {\file /dev/sr*}, respectively), whereas {\file /dev/cdwriter} and {\file /dev/scanner} should point to the appropriate generic SCSI devices -({\file /dev/sg*}.) +({\file /dev/sg*}). {\file /dev/mouse} may point to a primary serial TTY device, a hardware mouse device, or a socket for a mouse driver program -(e.g. {\file /dev/gpmdata}.) +(e.g. {\file /dev/gpmdata}). \subsection{Sockets and pipes} diff -u --recursive --new-file v2.1.62/linux/Documentation/devices.txt linux/Documentation/devices.txt --- v2.1.62/linux/Documentation/devices.txt Tue Sep 23 16:48:46 1997 +++ linux/Documentation/devices.txt Mon Nov 10 10:56:26 1997 @@ -1,38 +1,27 @@ LINUX ALLOCATED DEVICES Maintained by H. Peter Anvin - Last revised: September 11, 1997 + Last revised: November 9, 1997 -This list is the successor to Rick Miller's Linux Device List, which -he stopped maintaining when he got busy with other things in 1993. It -is a registry of allocated major device numbers, as well as the -recommended /dev directory nodes for these devices. +This list is the Linux Device List, the official registry of allocated +device numbers and /dev directory nodes for the Linux operating +system. The latest version of this list is included with the Linux kernel sources in LaTeX and ASCII form. It is also available separately from ftp://ftp.kernel.org/pub/linux/docs/device-list/. In case of -discrepancy, the LaTeX version is authoritative. +discrepancy between the text and LaTeX versions, the LaTeX version is +authoritative. This document is included by reference into the Linux Filesystem Standard (FSSTND). The FSSTND is available from ftp://tsx-11.mit.edu/pub/linux/docs/linux-standards/fsstnd/. -To have a major number allocated, or a minor number in situations -where that applies (e.g. busmice), please contact me with the -appropriate device information. I *very* much appreciate if you send -me a device description in the same format as the ones already in this -file. 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. - -NOTE: When sending me mail, *please* include the word "device" in the -subject so your mail won't accidentally get buried! - Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga -platform only. Allocations marked (68k/Atari) apply to Linux/68k on +platform only. Allocations marked (68k/Atari) apply to Linux/68k on the Atari platform only. -This document is in the public domain. The author requests, however, +This document is in the public domain. The author requests, however, that semantically altered versions are not distributed without permission of the author, assuming the author can be contacted without an unreasonable effort. @@ -44,11 +33,34 @@ on this list. Any such information requests will be deleted without reply. - 0 Unnamed devices (e.g. non-device mounts) + **** PLEASE READ THIS BEFORE SUBMITTING A DEVICE ENTRY **** + +To have a major number allocated, or a minor number in situations +where that applies (e.g. busmice), please contact me with the +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. +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. + +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. + +Your cooperation is appreciated. + + + 0 Unnamed devices (e.g. non-device mounts) 0 = reserved as null device number 1 char Memory devices - 1 = /dev/mem Physical memory access + 1 = /dev/mem Physical memory access 2 = /dev/kmem Kernel virtual memory access 3 = /dev/null Null device 4 = /dev/port I/O port access @@ -97,7 +109,7 @@ 8 = /dev/fd?h1200 5.25" 1200K in a 1200K drive(1) 40 = /dev/fd?h1440 5.25" 1440K in a 1200K drive(1) 56 = /dev/fd?h1476 5.25" 1476K in a 1200K drive - 72 = /dev/fd?h1494 5.25" 1494K in a 1200K drive + 72 = /dev/fd?h1494 5.25" 1494K in a 1200K drive 92 = /dev/fd?h1600 5.25" 1600K in a 1200K drive(1) 12 = /dev/fd?u360 3.5" 360K Double Density @@ -128,7 +140,7 @@ NOTE: The letter in the device name (d, q, h or u) signifies the type of drive: 5.25" Double Density (d), 5.25" Quad Density (q), 5.25" High Density (h) or 3.5" - (any model, u). The use of the capital letters D, H + (any model, u). The use of the capital letters D, H and E for the 3.5" models have been deprecated, since the drive type is insignificant for these devices. @@ -139,7 +151,7 @@ 256 = /dev/ttyef 256th PTY slave block First MFM, RLL and IDE hard disk/CD-ROM interface - 0 = /dev/hda Master: whole disk (or CD-ROM) + 0 = /dev/hda Master: whole disk (or CD-ROM) 64 = /dev/hdb Slave: whole disk (or CD-ROM) For partitions, add to the whole disk device number: @@ -155,7 +167,7 @@ appropriate to their respective architectures. 4 char TTY devices - 0 = /dev/tty0 Current virtual console + 0 = /dev/tty0 Current virtual console 1 = /dev/tty1 First virtual console ... @@ -178,7 +190,7 @@ devices. 5 char Alternate TTY devices - 0 = /dev/tty Current TTY device + 0 = /dev/tty Current TTY device 1 Reserved for console device 64 = /dev/cua0 Callout device corresponding to ttyS0 ... @@ -188,7 +200,7 @@ /dev/console in a future version of Linux. 6 char Parallel printer devices - 0 = /dev/lp0 First parallel printer (0x3bc) + 0 = /dev/lp0 First parallel printer (0x3bc) 1 = /dev/lp1 Second parallel printer (0x378) 2 = /dev/lp2 Third parallel printer (0x278) @@ -197,7 +209,7 @@ /dev/lp1. 7 char Virtual console capture devices - 0 = /dev/vcs Current vc text contents + 0 = /dev/vcs Current vc text contents 1 = /dev/vcs1 tty1 text contents ... 63 = /dev/vcs63 tty63 text contents @@ -214,14 +226,14 @@ ... The loopback devices are used to mount filesystems not - associated with block devices. The binding to the + associated with block devices. The binding to the loopback devices is handled by mount(8) or losetup(8). 8 block SCSI disk devices - 0 = /dev/sda First SCSI disk whole disk + 0 = /dev/sda First SCSI disk whole disk 16 = /dev/sdb Second SCSI disk whole disk 32 = /dev/sdc Third SCSI disk whole disk - ... + ... 240 = /dev/sdp Sixteenth SCSI disk whole disk Partitions are handled in the same way as for IDE @@ -229,16 +241,16 @@ partitions is 15. 9 char SCSI tape devices - 0 = /dev/st0 First SCSI tape, mode 0 + 0 = /dev/st0 First SCSI tape, mode 0 1 = /dev/st1 Second SCSI tape, mode 0 ... - 32 = /dev/st0l First SCSI tape, mode 1 + 32 = /dev/st0l First SCSI tape, mode 1 33 = /dev/st1l Second SCSI tape, mode 1 ... - 64 = /dev/st0m First SCSI tape, mode 2 + 64 = /dev/st0m First SCSI tape, mode 2 65 = /dev/st1m Second SCSI tape, mode 2 ... - 96 = /dev/st0a First SCSI tape, mode 3 + 96 = /dev/st0a First SCSI tape, mode 3 97 = /dev/st1a Second SCSI tape, mode 3 ... 128 = /dev/nst0 First SCSI tape, mode 0, no rewind @@ -310,7 +322,7 @@ The raw keyboard device is used on Linux/SPARC only. block SCSI CD-ROM devices - 0 = /dev/sr0 First SCSI CD-ROM + 0 = /dev/sr0 First SCSI CD-ROM 1 = /dev/sr1 Second SCSI CD-ROM ... @@ -438,7 +450,7 @@ 1 = /dev/ttyD1 Second Digiboard port ... block Second IDE hard disk/CD-ROM interface - 0 = /dev/hdc Master: whole disk (or CD-ROM) + 0 = /dev/hdc Master: whole disk (or CD-ROM) 64 = /dev/hdd Slave: whole disk (or CD-ROM) Partitions are handled the same way as for the first @@ -538,7 +550,7 @@ 0 = /dev/fb0 First frame buffer 1 = /dev/fb0autodetect 24 = /dev/fb0user0 - ... + ... 31 = /dev/fb0user7 32 = /dev/fb1 Second frame buffer 33 = /dev/fb1autodetect @@ -547,7 +559,7 @@ 63 = /dev/fb1user7 The universal frame buffer device is currently only - supported on Linux/68k and Linux/SPARC. The plain + supported on Linux/68k and Linux/SPARC. The plain device accesses the frame buffer at current resolution (Linux/68k calls this device "current", e.g. /dev/fb0current); the "autodetect" one at bootup @@ -614,7 +626,7 @@ 1 = /dev/cux1 Callout device corresponding to ttyX1 ... block Third IDE hard disk/CD-ROM interface - 0 = /dev/hde Master: whole disk (or CD-ROM) + 0 = /dev/hde Master: whole disk (or CD-ROM) 64 = /dev/hdf Slave: whole disk (or CD-ROM) Partitions are handled the same way as for the first @@ -632,7 +644,7 @@ on. block Fourth IDE hard disk/CD-ROM interface - 0 = /dev/hdg Master: whole disk (or CD-ROM) + 0 = /dev/hdg Master: whole disk (or CD-ROM) 64 = /dev/hdh Slave: whole disk (or CD-ROM) Partitions are handled the same way as for the first @@ -643,10 +655,10 @@ 1 = /dev/midi1 Second MIDI port, kernel timed 2 = /dev/midi2 Third MIDI port, kernel timed 3 = /dev/midi3 Fourth MIDI port, kernel timed - 64 = /dev/rmidi0 First MIDI port, untimed - 65 = /dev/rmidi1 Second MIDI port, untimed - 66 = /dev/rmidi2 Third MIDI port, untimed - 67 = /dev/rmidi3 Fourth MIDI port, untimed + 64 = /dev/rmidi0 First MIDI port, untimed + 65 = /dev/rmidi1 Second MIDI port, untimed + 66 = /dev/rmidi2 Third MIDI port, untimed + 67 = /dev/rmidi3 Fourth MIDI port, untimed 128 = /dev/smpte0 First MIDI port, SMPTE timed 129 = /dev/smpte1 Second MIDI port, SMPTE timed 130 = /dev/smpte2 Third MIDI port, SMPTE timed @@ -809,8 +821,8 @@ 53 char BDM interface for remote debugging MC683xx microcontrollers 0 = /dev/pd_bdm0 PD BDM interface on lp0 - 1 = /dev/pd_bdm1 PD BDM interface on lp1 - 2 = /dev/pd_bdm2 PD BDM interface on lp2 + 1 = /dev/pd_bdm1 PD BDM interface on lp1 + 2 = /dev/pd_bdm2 PD BDM interface on lp2 4 = /dev/icd_bdm0 ICD BDM interface on lp0 5 = /dev/icd_bdm1 ICD BDM interface on lp1 6 = /dev/icd_bdm2 ICD BDM interface on lp2 @@ -1014,18 +1026,18 @@ ... The driver and documentation may be obtained from - http://www.proximity.com.au/~brian/winradio/ + http://www.proximity.com.au/~brian/winradio/ 83 char Teletext/videotext interfaces - 0 = /dev/vtx Teletext decoder - 16 = /dev/vttuner TV tuner on teletext interface + 0 = /dev/vtx Teletext decoder + 16 = /dev/vttuner TV tuner on teletext interface - Devices for the driver contained in the VideoteXt package. - More information on http://home.pages.de/~videotext/ + Devices for the driver contained in the VideoteXt package. + More information on http://home.pages.de/~videotext/ 84 char Ikon 1011[57] Versatec Greensheet Interface - 0 = /dev/ihcp0 First Greensheet port - 1 = /dev/ihcp1 Second Greensheet port + 0 = /dev/ihcp0 First Greensheet port + 1 = /dev/ihcp1 Second Greensheet port 85 char Linux/SGI shared memory input queue 0 = /dev/shmiq Master shared input queue @@ -1060,7 +1072,33 @@ 30 = /dev/mtd15 16th MTD (rw) 31 = /dev/mtdr15 16th MTD (ro) - 91-119 UNALLOCATED + 91 char CAN-Bus devices + 0 = /dev/can0 First CAN-Bus controller + 1 = /dev/can1 Second CAN-Bus controller + ... + + 92 char Reserved for ith Kommunikationstechnik MIC ISDN card + + 93 char IBM Smart Capture Card frame grabber + 0 = /dev/iscc0 First Smart Capture Card + 1 = /dev/iscc1 Second Smart Capture Card + ... + 128 = /dev/isccctl0 First Smart Capture Card control + 129 = /dev/isccctl1 Second Smart Capture Card control + ... + + 94 char miroVIDEO DC10/30 capture/playback device + 0 = /dev/dcxx0 First capture card + 1 = /dev/dcxx1 Second capture card + ... + + 95 char IP filter + 0 = /dev/ipl Filter control device/log file + 1 = /dev/ipnat NAT control device/log file + 2 = /dev/ipstate State information log file + 3 = /dev/ipauth Authentication control device/log file + + 96-119 UNALLOCATED 120-127 LOCAL/EXPERIMENTAL USE diff -u --recursive --new-file v2.1.62/linux/MAINTAINERS linux/MAINTAINERS --- v2.1.62/linux/MAINTAINERS Sat Nov 1 11:04:27 1997 +++ linux/MAINTAINERS Tue Nov 4 09:17:30 1997 @@ -204,6 +204,13 @@ L: linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu S: Maintained +GDT SCSI DISK ARRAY CONTROLLER DRIVER +P: Achim Leubner +M: achim@vortex.de +L: linux-scsi@vger.rutgers.edu +W: http://www.icp-vortex.com/ +S: Supported + FILE LOCKING (flock() and fcntl()/lockf()) P: Andy Walker M: andy@lysaker.kvaerner.no diff -u --recursive --new-file v2.1.62/linux/Makefile linux/Makefile --- v2.1.62/linux/Makefile Mon Nov 3 13:04:25 1997 +++ linux/Makefile Tue Nov 4 09:18:04 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 62 +SUBLEVEL = 63 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) diff -u --recursive --new-file v2.1.62/linux/arch/alpha/kernel/time.c linux/arch/alpha/kernel/time.c --- v2.1.62/linux/arch/alpha/kernel/time.c Mon Nov 3 13:04:25 1997 +++ linux/arch/alpha/kernel/time.c Fri Nov 7 15:24:01 1997 @@ -50,8 +50,6 @@ */ #define FIX_SHIFT 48 -static unsigned long round_ticks; - /* lump static variables together for more efficient access: */ static struct { __u32 last_time; /* cycle counter last time it got invoked */ @@ -81,7 +79,12 @@ now = rpcc(); delta = now - state.last_time; state.last_time = now; - nticks = ((delta * state.scaled_ticks_per_cycle+round_ticks) >> FIX_SHIFT); + if(hwrpb->cycle_freq) { + nticks = (delta * state.scaled_ticks_per_cycle) >> (FIX_SHIFT-1); + nticks = (nticks+1) >> 1; + } + else nticks=1; /* No way to estimate lost ticks if we don't know + the cycle frequency. */ for (i = 0; i < nticks; ++i) { do_timer(regs); } @@ -147,14 +150,15 @@ /* read RTC exactly on falling edge of update flag */ /* Wait for rise.... (may take up to 1 second) */ - while(!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)); + do {} while(!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)); - /* Wait for fall.... */ +/* Jay Estabook : + * Wait for the Update Done Interrupt bit (0x10) in reg C (12) to be set, + * which (hopefully) indicates that the update is really done. + */ - while(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); + do {} while(!CMOS_READ(RTC_REG_C) & RTC_UIP); - __delay(1000000); - sec = CMOS_READ(RTC_SECONDS); min = CMOS_READ(RTC_MINUTES); hour = CMOS_READ(RTC_HOURS); @@ -189,8 +193,8 @@ __you_loose(); } state.last_time = rpcc(); + if(hwrpb->cycle_freq) state.scaled_ticks_per_cycle = ((unsigned long) HZ << FIX_SHIFT) / hwrpb->cycle_freq; - round_ticks=(unsigned long) 1 << (FIX_SHIFT-1); state.last_rtc_update = 0; #ifdef CONFIG_RTC diff -u --recursive --new-file v2.1.62/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.1.62/linux/arch/i386/defconfig Sat Nov 1 11:04:27 1997 +++ linux/arch/i386/defconfig Wed Nov 5 13:21:12 1997 @@ -137,11 +137,15 @@ # CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_NCR53C7xx is not set # CONFIG_SCSI_NCR53C8XX is not set # CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_SEAGATE is not set @@ -247,6 +251,7 @@ # CONFIG_RTC is not set # CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set +# CONFIG_MISC_RADIO is not set # # Sound diff -u --recursive --new-file v2.1.62/linux/arch/i386/kernel/ioport.c linux/arch/i386/kernel/ioport.c --- v2.1.62/linux/arch/i386/kernel/ioport.c Tue Feb 4 06:44:24 1997 +++ linux/arch/i386/kernel/ioport.c Wed Nov 5 08:57:51 1997 @@ -75,17 +75,15 @@ * code. */ -asmlinkage int sys_iopl(long ebx,long ecx,long edx, - long esi, long edi, long ebp, long eax, long ds, - long es, long orig_eax, long eip, long cs, - long eflags, long esp, long ss) +asmlinkage int sys_iopl(unsigned long unused) { - unsigned int level = ebx; + struct pt_regs * regs = (struct pt_regs *) &unused; + unsigned int level = regs->ebx; if (level > 3) return -EINVAL; if (!suser()) return -EPERM; - *(&eflags) = (eflags & 0xffffcfff) | (level << 12); + regs->eflags = (regs->eflags & 0xffffcfff) | (level << 12); return 0; } diff -u --recursive --new-file v2.1.62/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.1.62/linux/arch/i386/kernel/setup.c Tue Sep 23 16:48:46 1997 +++ linux/arch/i386/kernel/setup.c Wed Nov 12 11:09:56 1997 @@ -42,6 +42,7 @@ char x86_mask = 0; /* set by kernel/head.S */ int x86_capability = 0; /* set by kernel/head.S */ int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */ +int pentium_f00f_bug = 0; /* set if Pentium(TM) with F00F bug */ int have_cpuid = 0; /* set if CPUID instruction works */ char x86_vendor_id[13] = "unknown"; @@ -359,6 +360,7 @@ "fdiv_bug\t: %s\n" "hlt_bug\t\t: %s\n" "sep_bug\t\t: %s\n" + "pentium_f00f_bug\t\t: %s\n" "fpu\t\t: %s\n" "fpu_exception\t: %s\n" "cpuid\t\t: %s\n" @@ -367,6 +369,7 @@ CD(fdiv_bug) ? "yes" : "no", CD(hlt_works_ok) ? "no" : "yes", sep_bug ? "yes" : "no", + pentium_f00f_bug ? "yes" : "no", CD(hard_math) ? "yes" : "no", (CD(hard_math) && ignore_irq13) ? "yes" : "no", diff -u --recursive --new-file v2.1.62/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c --- v2.1.62/linux/arch/i386/kernel/traps.c Sun Sep 7 13:10:42 1997 +++ linux/arch/i386/kernel/traps.c Wed Nov 12 11:09:56 1997 @@ -413,6 +413,51 @@ #endif /* CONFIG_MATH_EMULATION */ +static struct +{ + short limit __attribute__((packed)); + void * addr __attribute__((packed)); + short __pad __attribute__((packed)); +} idt_d; + +void * idt2; + +__initfunc(void trap_init_f00f_bug(void)) +{ + pgd_t * pgd; + pmd_t * pmd; + pte_t * pte; + unsigned long twopage; + + printk("moving IDT ... "); + + twopage = (unsigned long) vmalloc (2*PAGE_SIZE); + + idt2 = (void *)(twopage + 4096-7*8); + + memcpy(idt2,&idt,sizeof(idt)); + + idt_d.limit = 256*8-1; + idt_d.addr = idt2; + idt_d.__pad = 0; + + __asm__ __volatile__("\tlidt %0": "=m" (idt_d)); + + /* + * Unmap lower page: + */ + pgd = pgd_offset(current->mm, twopage); + pmd = pmd_offset(pgd, twopage); + pte = pte_offset(pmd, twopage); + + pte_clear(pte); + flush_tlb_all(); + + printk(" ... done\n"); +} + + + __initfunc(void trap_init(void)) { int i; diff -u --recursive --new-file v2.1.62/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c --- v2.1.62/linux/arch/i386/mm/fault.c Wed Oct 15 16:04:23 1997 +++ linux/arch/i386/mm/fault.c Wed Nov 12 11:09:55 1997 @@ -74,6 +74,25 @@ return 0; } +asmlinkage void divide_error(void); +asmlinkage void debug(void); +asmlinkage void nmi(void); +asmlinkage void int3(void); +asmlinkage void overflow(void); +asmlinkage void bounds(void); +asmlinkage void invalid_op(void); + +asmlinkage void do_divide_error (struct pt_regs *, unsigned long); +asmlinkage void do_debug (struct pt_regs *, unsigned long); +asmlinkage void do_nmi (struct pt_regs *, unsigned long); +asmlinkage void do_int3 (struct pt_regs *, unsigned long); +asmlinkage void do_overflow (struct pt_regs *, unsigned long); +asmlinkage void do_bounds (struct pt_regs *, unsigned long); +asmlinkage void do_invalid_op (struct pt_regs *, unsigned long); + +extern int * idt2; +extern int pentium_f00f_bug; + /* * This routine handles page faults. It determines the address, * and the problem, and then passes it off to one of the appropriate @@ -170,6 +189,46 @@ goto out; } + printk("<%p/%p>\n", idt2, (void *)address); + /* + * Pentium F0 0F C7 C8 bug workaround: + */ + if ( pentium_f00f_bug && (address >= (unsigned long)idt2) && + (address < (unsigned long)idt2+256*8) ) { + + void (*handler) (void); + int nr = (address-(unsigned long)idt2)/8; + unsigned long low, high; + + low = idt[nr].a; + high = idt[nr].b; + + handler = (void (*) (void)) ((low&0x0000ffff) | (high&0xffff0000)); + printk("\n"); + goto out; + } + /* Are we prepared to handle this kernel fault? */ if ((fixup = search_exception_table(regs->eip)) != 0) { printk(KERN_DEBUG "%s: Exception at [<%lx>] cr2=%lx (fixup: %lx)\n", @@ -193,6 +252,7 @@ flush_tlb(); goto out; } + if (address < PAGE_SIZE) printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); else diff -u --recursive --new-file v2.1.62/linux/arch/sparc/config.in linux/arch/sparc/config.in --- v2.1.62/linux/arch/sparc/config.in Sat Oct 25 02:44:14 1997 +++ linux/arch/sparc/config.in Sat Nov 8 11:39:12 1997 @@ -68,7 +68,8 @@ if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then tristate ' Linear (append) mode' CONFIG_MD_LINEAR tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED -# tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING + tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING + tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM diff -u --recursive --new-file v2.1.62/linux/drivers/block/Config.in linux/drivers/block/Config.in --- v2.1.62/linux/drivers/block/Config.in Wed Apr 23 19:01:17 1997 +++ linux/drivers/block/Config.in Sat Nov 8 11:39:12 1997 @@ -53,6 +53,8 @@ if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then tristate ' Linear (append) mode' CONFIG_MD_LINEAR tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED + tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING + tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then diff -u --recursive --new-file v2.1.62/linux/drivers/block/Makefile linux/drivers/block/Makefile --- v2.1.62/linux/drivers/block/Makefile Mon Aug 18 18:19:45 1997 +++ linux/drivers/block/Makefile Sat Nov 8 11:39:12 1997 @@ -200,25 +200,21 @@ endif endif -#ifeq ($(CONFIG_MD_RAID1),y) -#L_OBJS += raid1.o -#else -# ifeq ($(CONFIG_MD_SUPPORT_RAID1),y) -# ifeq ($(CONFIG_MD_RAID1),m) -# M_OBJS += raid1.o -# endif -# endif -#endif -# -#ifeq ($(CONFIG_MD_RAID5),y) -#L_OBJS += raid5.o -#else -# ifeq ($(CONFIG_MD_SUPPORT_RAID5),y) -# ifeq ($(CONFIG_MD_RAID5),m) -# M_OBJS += raid5.o -# endif -# endif -#endif +ifeq ($(CONFIG_MD_MIRRORING),y) +L_OBJS += raid1.o +else + ifeq ($(CONFIG_MD_MIRRORING),m) + M_OBJS += raid1.o + endif +endif + +ifeq ($(CONFIG_MD_RAID5),y) +L_OBJS += raid5.o +else + ifeq ($(CONFIG_MD_RAID5),m) + M_OBJS += raid5.o + endif +endif endif diff -u --recursive --new-file v2.1.62/linux/drivers/block/linear.c linux/drivers/block/linear.c --- v2.1.62/linux/drivers/block/linear.c Tue May 13 22:41:05 1997 +++ linux/drivers/block/linear.c Sat Nov 8 11:39:12 1997 @@ -163,6 +163,7 @@ sz+=sprintf (page+sz, "\n"); #endif + sz+=sprintf (page+sz, " %dk rounding", 1<next; + + if (!next) + return; + if (req->sector + req->nr_sectors != next->sector) + return; + if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS) + return; + req->bhtail->b_reqnext = next->bh; + req->bhtail = next->bhtail; + req->nr_sectors += next->nr_sectors; + next->rq_status = RQ_INACTIVE; + req->next = next->next; + wake_up (&wait_for_request); +} + +void make_request(int major,int rw, struct buffer_head * bh) { unsigned int sector, count; struct request * req; @@ -313,7 +338,7 @@ if (blk_size[major]) if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) { - bh->b_state &= (1 << BH_Lock) | (1 << BH_FreeOnIO); + bh->b_state &= (1 << BH_Lock); /* This may well happen - the kernel calls bread() without checking the size of the device, e.g., when mounting a device. */ @@ -323,8 +348,7 @@ kdevname(bh->b_rdev), rw, (sector + count)>>1, blk_size[major][MINOR(bh->b_rdev)]); - unlock_buffer(bh); - return; + goto end_io; } rw_ahead = 0; /* normal case; gets changed below for READA/WRITEA */ @@ -333,10 +357,8 @@ rw_ahead = 1; rw = READ; /* drop into READ */ case READ: - if (buffer_uptodate(bh)) { - unlock_buffer(bh); /* Hmmph! Already have it */ - return; - } + if (buffer_uptodate(bh)) /* Hmmph! Already have it */ + goto end_io; kstat.pgpgin++; max_req = NR_REQUEST; /* reads take precedence */ break; @@ -344,10 +366,8 @@ rw_ahead = 1; rw = WRITE; /* drop into WRITE */ case WRITE: - if (!buffer_dirty(bh)) { - unlock_buffer(bh); /* Hmmph! Nothing to write */ - return; - } + if (!buffer_dirty(bh)) /* Hmmph! Nothing to write */ + goto end_io; /* We don't allow the write-requests to fill up the * queue completely: we want some room for reads, * as they take precedence. The last third of the @@ -359,8 +379,7 @@ default: printk(KERN_ERR "make_request: bad block dev cmd," " must be R/W/RA/WA\n"); - unlock_buffer(bh); - return; + goto end_io; } /* look for a free request. */ @@ -409,7 +428,7 @@ continue; if (req->cmd != rw) continue; - if (req->nr_sectors >= 244) + if (req->nr_sectors >= MAX_SECTORS) continue; if (req->rq_dev != bh->b_rdev) continue; @@ -417,6 +436,9 @@ if (req->sector + req->nr_sectors == sector) { req->bhtail->b_reqnext = bh; req->bhtail = bh; + req->nr_sectors += count; + /* Can we now merge this req with the next? */ + attempt_merge(req); /* or to the beginning? */ } else if (req->sector - count == sector) { bh->b_reqnext = req->bh; @@ -424,10 +446,10 @@ req->buffer = bh->b_data; req->current_nr_sectors = count; req->sector = sector; + req->nr_sectors += count; } else continue; - req->nr_sectors += count; mark_buffer_clean(bh); sti(); return; @@ -440,10 +462,8 @@ /* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */ if (!req) { - if (rw_ahead) { - unlock_buffer(bh); - return; - } + if (rw_ahead) + goto end_io; req = __get_request_wait(max_req, bh->b_rdev); } @@ -459,6 +479,10 @@ req->bhtail = bh; req->next = NULL; add_request(major+blk_dev,req); + return; + +end_io: + bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); } /* This function can be used to request a number of buffers from a block @@ -530,6 +554,12 @@ for (i = 0; i < nr; i++) { if (bh[i]) { set_bit(BH_Req, &bh[i]->b_state); +#ifdef CONFIG_BLK_DEV_MD + if (MAJOR(bh[i]->b_dev) == MD_MAJOR) { + md_make_request(MINOR (bh[i]->b_dev), rw, bh[i]); + continue; + } +#endif make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]); } } @@ -654,6 +684,7 @@ req->next = NULL; } memset(ro_bits,0,sizeof(ro_bits)); + memset(max_readahead, 0, sizeof(max_readahead)); #ifdef CONFIG_AMIGA_Z2RAM z2_init(); #endif diff -u --recursive --new-file v2.1.62/linux/drivers/block/md.c linux/drivers/block/md.c --- v2.1.62/linux/drivers/block/md.c Sat Oct 25 02:44:15 1997 +++ linux/drivers/block/md.c Sat Nov 8 11:39:12 1997 @@ -9,6 +9,9 @@ kerneld support by Boris Tobotras + RAID-1/RAID-5 extensions by: + Ingo Molnar, Miguel de Icaza, Gadi Oxman + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) @@ -19,6 +22,13 @@ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* + * Current RAID-1,4,5 parallel reconstruction speed limit is 1024 KB/sec, so + * the extra system load does not show up that much. Increase it if your + * system can take more. + */ +#define SPEED_LIMIT 1024 + #include #include #include @@ -31,20 +41,31 @@ #include #include #include +#include #ifdef CONFIG_KERNELD #include #endif #include #include +#define __KERNEL_SYSCALLS__ +#include + #define MAJOR_NR MD_MAJOR #define MD_DRIVER #include #include +#include +#include static struct hd_struct md_hd_struct[MAX_MD_DEV]; static int md_blocksizes[MAX_MD_DEV]; +int md_maxreadahead[MAX_MD_DEV]; +static struct md_thread md_threads[MAX_MD_THREADS]; +#if SUPPORT_RECONSTRUCTION +static struct md_thread *md_sync_thread = NULL; +#endif /* SUPPORT_RECONSTRUCTION */ int md_size[MAX_MD_DEV]={0, }; @@ -66,7 +87,6 @@ }; static struct md_personality *pers[MAX_PERSONALITY]={NULL, }; - struct md_dev md_dev[MAX_MD_DEV]; static struct gendisk *find_gendisk (kdev_t dev) @@ -84,7 +104,6 @@ return (NULL); } - char *partition_name (kdev_t dev) { static char name[40]; /* This should be long @@ -93,49 +112,318 @@ if (!hd) { - printk ("No gendisk entry for dev %s\n", kdevname(dev)); - sprintf (name, "dev %s", kdevname(dev)); + sprintf (name, "[dev %s]", kdevname(dev)); return (name); } return disk_name (hd, MINOR(dev), name); /* routine in genhd.c */ } +static int legacy_raid_sb (int minor, int pnum) +{ + int i, factor; + + factor = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); + + /***** + * do size and offset calculations. + */ + for (i=0; i> PERSONALITY_SHIFT) + md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD * md_dev[minor].nb_dev; + return 0; +} -static void set_ra (void) +static void free_sb (struct md_dev *mddev) { - int i, j, minra=INT_MAX; + int i; + struct real_dev *realdev; - for (i=0; isb) { + free_page((unsigned long) mddev->sb); + mddev->sb = NULL; + } + for (i = 0; i nb_dev; i++) { + realdev = mddev->devices + i; + if (realdev->sb) { + free_page((unsigned long) realdev->sb); + realdev->sb = NULL; + } + } } +/* + * Check one RAID superblock for generic plausibility + */ + +#define BAD_MAGIC KERN_ERR \ +"md: %s: invalid raid superblock magic (%x) on block %u\n" + +#define OUT_OF_MEM KERN_ALERT \ +"md: out of memory.\n" + +#define NO_DEVICE KERN_ERR \ +"md: disabled device %s\n" + +#define SUCCESS 0 +#define FAILURE -1 + +static int analyze_one_sb (struct real_dev * rdev) +{ + int ret = FAILURE; + struct buffer_head *bh; + kdev_t dev = rdev->dev; + md_superblock_t *sb; + + /* + * Read the superblock, it's at the end of the disk + */ + rdev->sb_offset = MD_NEW_SIZE_BLOCKS (blk_size[MAJOR(dev)][MINOR(dev)]); + set_blocksize (dev, MD_SB_BYTES); + bh = bread (dev, rdev->sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); + + if (bh) { + sb = (md_superblock_t *) bh->b_data; + if (sb->md_magic != MD_SB_MAGIC) { + printk (BAD_MAGIC, kdevname(dev), + sb->md_magic, rdev->sb_offset); + goto abort; + } + rdev->sb = (md_superblock_t *) __get_free_page(GFP_KERNEL); + if (!rdev->sb) { + printk (OUT_OF_MEM); + goto abort; + } + memcpy (rdev->sb, bh->b_data, MD_SB_BYTES); + + rdev->size = sb->size; + } else + printk (NO_DEVICE,kdevname(rdev->dev)); + ret = SUCCESS; +abort: + if (bh) + brelse (bh); + return ret; +} + +#undef SUCCESS +#undef FAILURE + +#undef BAD_MAGIC +#undef OUT_OF_MEM +#undef NO_DEVICE + +/* + * Check a full RAID array for plausibility + */ + +#define INCONSISTENT KERN_ERR \ +"md: superblock inconsistency -- run ckraid\n" + +#define OUT_OF_DATE KERN_ERR \ +"md: superblock update time inconsistenty -- using the most recent one\n" + +#define OLD_VERSION KERN_ALERT \ +"md: %s: unsupported raid array version %d.%d.%d\n" + +#define NOT_CLEAN KERN_ERR \ +"md: %s: raid array is not clean -- run ckraid\n" + +#define NOT_CLEAN_IGNORE KERN_ERR \ +"md: %s: raid array is not clean -- reconstructing parity\n" + +#define UNKNOWN_LEVEL KERN_ERR \ +"md: %s: unsupported raid level %d\n" + +static int analyze_sbs (int minor, int pnum) +{ + struct md_dev *mddev = md_dev + minor; + int i, N = mddev->nb_dev, out_of_date = 0; + struct real_dev * disks = mddev->devices; + md_superblock_t *sb, *freshest = NULL; + + /* + * RAID-0 and linear don't use a RAID superblock + */ + if (pnum == RAID0 >> PERSONALITY_SHIFT || + pnum == LINEAR >> PERSONALITY_SHIFT) + return legacy_raid_sb (minor, pnum); + + /* + * Verify the RAID superblock on each real device + */ + for (i = 0; i < N; i++) + if (analyze_one_sb(disks+i)) + goto abort; + + /* + * The superblock constant part has to be the same + * for all disks in the array. + */ + sb = NULL; + for (i = 0; i < N; i++) { + if (!disks[i].sb) + continue; + if (!sb) { + sb = disks[i].sb; + continue; + } + if (memcmp(sb, + disks[i].sb, MD_SB_GENERIC_CONSTANT_WORDS * 4)) { + printk (INCONSISTENT); + goto abort; + } + } + + /* + * Ok, we have all disks and the array is ready to run. Lets + * find the freshest superblock, that one will be the superblock + * that represents the whole array. + */ + if ((sb = mddev->sb = (md_superblock_t *) __get_free_page (GFP_KERNEL)) == NULL) + goto abort; + freshest = NULL; + for (i = 0; i < N; i++) { + if (!disks[i].sb) + continue; + if (!freshest) { + freshest = disks[i].sb; + continue; + } + /* + * Find the newest superblock version + */ + if (disks[i].sb->utime != freshest->utime) { + out_of_date = 1; + if (disks[i].sb->utime > freshest->utime) + freshest = disks[i].sb; + } + } + if (out_of_date) + printk(OUT_OF_DATE); + memcpy (sb, freshest, sizeof(*freshest)); + + /* + * Check if we can support this RAID array + */ + if (sb->major_version != MD_MAJOR_VERSION || + sb->minor_version > MD_MINOR_VERSION) { + + printk (OLD_VERSION, kdevname(MKDEV(MD_MAJOR, minor)), + sb->major_version, sb->minor_version, + sb->patch_version); + goto abort; + } + + /* + * We need to add this as a superblock option. + */ +#if SUPPORT_RECONSTRUCTION + if (sb->state != (1 << MD_SB_CLEAN)) { + if (sb->level == 1) { + printk (NOT_CLEAN, kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } else + printk (NOT_CLEAN_IGNORE, kdevname(MKDEV(MD_MAJOR, minor))); + } +#else + if (sb->state != (1 << MD_SB_CLEAN)) { + printk (NOT_CLEAN, kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } +#endif /* SUPPORT_RECONSTRUCTION */ + + switch (sb->level) { + case 1: + md_size[minor] = sb->size; + md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD; + break; + case 4: + case 5: + md_size[minor] = sb->size * (sb->raid_disks - 1); + md_maxreadahead[minor] = MD_DEFAULT_DISK_READAHEAD * (sb->raid_disks - 1); + break; + default: + printk (UNKNOWN_LEVEL, kdevname(MKDEV(MD_MAJOR, minor)), + sb->level); + goto abort; + } + return 0; +abort: + free_sb(mddev); + return 1; +} + +#undef INCONSISTENT +#undef OUT_OF_DATE +#undef OLD_VERSION +#undef NOT_CLEAN +#undef OLD_LEVEL + +int md_update_sb(int minor) +{ + struct md_dev *mddev = md_dev + minor; + struct buffer_head *bh; + md_superblock_t *sb = mddev->sb; + struct real_dev *realdev; + kdev_t dev; + int i; + u32 sb_offset; + + sb->utime = CURRENT_TIME; + for (i = 0; i < mddev->nb_dev; i++) { + realdev = mddev->devices + i; + if (!realdev->sb) + continue; + dev = realdev->dev; + sb_offset = realdev->sb_offset; + set_blocksize(dev, MD_SB_BYTES); + printk("md: updating raid superblock on device %s, sb_offset == %u\n", kdevname(dev), sb_offset); + bh = getblk(dev, sb_offset / MD_SB_BLOCKS, MD_SB_BYTES); + if (bh) { + sb = (md_superblock_t *) bh->b_data; + memcpy(sb, mddev->sb, MD_SB_BYTES); + memcpy(&sb->descriptor, sb->disks + realdev->sb->descriptor.number, MD_SB_DESCRIPTOR_WORDS * 4); + mark_buffer_uptodate(bh, 1); + mark_buffer_dirty(bh, 1); + ll_rw_block(WRITE, 1, &bh); + wait_on_buffer(bh); + bforget(bh); + fsync_dev(dev); + invalidate_buffers(dev); + } else + printk(KERN_ERR "md: getblk failed for device %s\n", kdevname(dev)); + } + return 0; +} static int do_md_run (int minor, int repart) { - int pnum, i, min, current_ra, err; - + int pnum, i, min, factor, err; + if (!md_dev[minor].nb_dev) return -EINVAL; if (md_dev[minor].pers) return -EBUSY; - + md_dev[minor].repartition=repart; - if ((pnum=PERSONALITY(md_dev+minor) >> (PERSONALITY_SHIFT)) + if ((pnum=PERSONALITY(&md_dev[minor]) >> (PERSONALITY_SHIFT)) >= MAX_PERSONALITY) return -EINVAL; - + + /* Only RAID-1 and RAID-5 can have MD devices as underlying devices */ + if (pnum != (RAID1 >> PERSONALITY_SHIFT) && pnum != (RAID5 >> PERSONALITY_SHIFT)){ + for (i = 0; i < md_dev [minor].nb_dev; i++) + if (MAJOR (md_dev [minor].devices [i].dev) == MD_MAJOR) + return -EINVAL; + } if (!pers[pnum]) { #ifdef CONFIG_KERNELD @@ -147,7 +435,7 @@ return -EINVAL; } - min=1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); + factor = min = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor))); for (i=0; irun (minor, md_dev+minor))) { md_dev[minor].pers=NULL; + free_sb(md_dev + minor); return (err); } - + + if (pnum != RAID0 >> PERSONALITY_SHIFT && pnum != LINEAR >> PERSONALITY_SHIFT) + { + md_dev[minor].sb->state &= ~(1 << MD_SB_CLEAN); + md_update_sb(minor); + } + /* FIXME : We assume here we have blocks that are twice as large as sectors. THIS MAY NOT BE TRUE !!! */ md_hd_struct[minor].start_sect=0; md_hd_struct[minor].nr_sects=md_size[minor]<<1; - /* It would be better to have a per-md-dev read_ahead. Currently, - we only use the smallest read_ahead among md-attached devices */ - - current_ra=read_ahead[MD_MAJOR]; - - for (i=0; iread_ahead[MAJOR(md_dev[minor].devices[i].dev)]) - current_ra=read_ahead[MAJOR(md_dev[minor].devices[i].dev)]; - - read_ahead[MD_MAJOR]=current_ra; - - printk ("START_DEV md%x %s\n", minor, md_dev[minor].pers->name); + read_ahead[MD_MAJOR] = 128; return (0); } - static int do_md_stop (int minor, struct inode *inode) { - int i; + int i; - if (inode->i_count > 1 || md_dev[minor].busy>1) /* ioctl : one open channel */ - { - printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor, - inode->i_count, md_dev[minor].busy); - return -EBUSY; - } - - if (md_dev[minor].pers) - { - /* The device won't exist anymore -> flush it now */ - fsync_dev (inode->i_rdev); - invalidate_buffers (inode->i_rdev); - md_dev[minor].pers->stop (minor, md_dev+minor); - } - - /* Remove locks. */ - for (i=0; ii_count>1 || md_dev[minor].busy>1) { + /* + * ioctl : one open channel + */ + printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", + minor, inode->i_count, md_dev[minor].busy); + return -EBUSY; + } + + if (md_dev[minor].pers) { + /* + * It is safe to call stop here, it only frees private + * data. Also, it tells us if a device is unstoppable + * (eg. resyncing is in progress) + */ + if (md_dev[minor].pers->stop (minor, md_dev+minor)) + return -EBUSY; + /* + * The device won't exist anymore -> flush it now + */ + fsync_dev (inode->i_rdev); + invalidate_buffers (inode->i_rdev); + if (md_dev[minor].sb) { + md_dev[minor].sb->state |= 1 << MD_SB_CLEAN; + md_update_sb(minor); + } + } + + /* Remove locks. */ + if (md_dev[minor].sb) + free_sb(md_dev + minor); + for (i=0; ii_dev=dev; /* don't care about - other fields */ - insert_inode_hash (md_dev[minor].devices[i].inode); - - /* Sizes are now rounded at run time */ - - md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)]; + int i; + int hot_add=0; + struct real_dev *realdev; + + if (md_dev[minor].nb_dev==MAX_REAL) + return -EINVAL; + + if (blk_size[MAJOR(dev)] == NULL || blk_size[MAJOR(dev)][MINOR(dev)] == 0) { + printk("md_add(): zero device size, huh, bailing out.\n"); + return -EINVAL; + } + + if (md_dev[minor].pers) { + /* + * The array is already running, hot-add the drive, or + * bail out: + */ + if (!md_dev[minor].pers->hot_add_disk) + return -EBUSY; + else + hot_add=1; + } + + /* + * Careful. We cannot increase nb_dev for a running array. + */ + i=md_dev[minor].nb_dev; + realdev = &md_dev[minor].devices[i]; + realdev->dev=dev; + + /* Lock the device by inserting a dummy inode. This doesn't + smell very good, but I need to be consistent with the + mount stuff, specially with fs_may_mount. If someone have + a better idea, please help ! */ + + realdev->inode=get_empty_inode (); + realdev->inode->i_dev=dev; /* don't care about other fields */ + insert_inode_hash (realdev->inode); + + /* Sizes are now rounded at run time */ + +/* md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)]; HACKHACK*/ + + realdev->size=blk_size[MAJOR(dev)][MINOR(dev)]; + + if (hot_add) { + /* + * Check the superblock for consistency. + * the personality itself has to check wether it's getting + * added with the proper flags ... also, personality has to + * be checked too ;) + */ + if (analyze_one_sb (realdev)) + return -EINVAL; + /* + * hot_add has to bump up nb_dev itself + */ + if (md_dev[minor].pers->hot_add_disk (&md_dev[minor], dev)) { + /* + * FIXME: here we should free up the inode and stuff + */ + printk ("FIXME\n"); + return -EINVAL; + } + } else + md_dev[minor].nb_dev++; - printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor); - return (0); + printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor); + return (0); } - static int md_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -354,7 +691,6 @@ return (0); } - static int md_open (struct inode *inode, struct file *file) { int minor=MINOR(inode->i_rdev); @@ -427,6 +763,30 @@ return (md_dev[minor].pers->map(md_dev+minor, rdev, rsector, size)); } +int md_make_request (int minor, int rw, struct buffer_head * bh) +{ + if (md_dev [minor].pers->make_request) { + if (buffer_locked(bh)) + return 0; + set_bit(BH_Lock, &bh->b_state); + if (rw == WRITE || rw == WRITEA) { + if (!buffer_dirty(bh)) { + bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); + return 0; + } + } + if (rw == READ || rw == READA) { + if (buffer_uptodate(bh)) { + bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); + return 0; + } + } + return (md_dev[minor].pers->make_request(md_dev+minor, rw, bh)); + } else { + make_request (MAJOR(bh->b_rdev), rw, bh); + return 0; + } +} static void do_md_request (void) { @@ -434,10 +794,51 @@ return; } +/* + * We run MAX_MD_THREADS from md_init() and arbitrate them in run time. + * This is not so elegant, but how can we use kernel_thread() from within + * loadable modules? + */ +struct md_thread *md_register_thread (void (*run) (void *), void *data) +{ + int i; + for (i = 0; i < MAX_MD_THREADS; i++) { + if (md_threads[i].run == NULL) { + md_threads[i].run = run; + md_threads[i].data = data; + return md_threads + i; + } + } + return NULL; +} + +void md_unregister_thread (struct md_thread *thread) +{ + thread->run = NULL; + thread->data = NULL; + thread->flags = 0; +} + +void md_wakeup_thread(struct md_thread *thread) +{ + set_bit(THREAD_WAKEUP, &thread->flags); + wake_up(&thread->wqueue); +} + + EXPORT_SYMBOL(md_size); +EXPORT_SYMBOL(md_maxreadahead); EXPORT_SYMBOL(register_md_personality); EXPORT_SYMBOL(unregister_md_personality); EXPORT_SYMBOL(partition_name); +EXPORT_SYMBOL(md_dev); +EXPORT_SYMBOL(md_error); +EXPORT_SYMBOL(md_register_thread); +EXPORT_SYMBOL(md_unregister_thread); +EXPORT_SYMBOL(md_update_sb); +EXPORT_SYMBOL(md_map); +EXPORT_SYMBOL(md_wakeup_thread); +EXPORT_SYMBOL(md_do_sync); static struct proc_dir_entry proc_md = { PROC_MD, 6, "mdstat", @@ -451,16 +852,36 @@ for(i=0;i MAX_MD_DEV) + panic ("md_error gets unknown device\n"); + if (!md_dev [minor].pers) + panic ("md_error gets an error for an unknown device\n"); + if (md_dev [minor].pers->error_handler) { + rc = md_dev [minor].pers->error_handler (md_dev+minor, rdev); +#if SUPPORT_RECONSTRUCTION + md_wakeup_thread(md_sync_thread); +#endif /* SUPPORT_RECONSTRUCTION */ + return rc; + } + return 0; +} int get_md_status (char *page) { @@ -493,9 +914,13 @@ partition_name(md_dev[i].devices[j].dev)); size+=md_dev[i].devices[j].size; } - - if (md_dev[i].nb_dev) - sz+=sprintf (page+sz, " %d blocks", size); + + if (md_dev[i].nb_dev) { + if (md_dev[i].pers) + sz+=sprintf (page+sz, " %d blocks", md_size[i]); + else + sz+=sprintf (page+sz, " %d blocks", size); + } if (!md_dev[i].pers) { @@ -506,11 +931,8 @@ if (md_dev[i].pers->max_invalid_dev) sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i)); - sz+=sprintf (page+sz, " %dk %s\n", 1<>PERSONALITY_SHIFT] ? - "rounding" : "chunks"); - sz+=md_dev[i].pers->status (page+sz, i, md_dev+i); + sz+=sprintf (page+sz, "\n"); } return (sz); @@ -543,6 +965,198 @@ return 0; } +int md_thread(void * arg) +{ + struct md_thread *thread = arg; + + current->session = 1; + current->pgrp = 1; + sprintf(current->comm, "md_thread"); + + lock_kernel(); + for (;;) { + sti(); + clear_bit(THREAD_WAKEUP, &thread->flags); + if (thread->run) { + thread->run(thread->data); + run_task_queue(&tq_disk); + } + cli(); + if (!test_bit(THREAD_WAKEUP, &thread->flags)) { + do { + current->signal = 0; + interruptible_sleep_on(&thread->wqueue); + } while (current->signal); + } + } +} + +static md_descriptor_t *get_spare(struct md_dev *mddev) +{ + int i; + md_superblock_t *sb = mddev->sb; + md_descriptor_t *descriptor; + struct real_dev *realdev; + + for (i = 0; i < mddev->nb_dev; i++) { + realdev = &mddev->devices[i]; + if (!realdev->sb) + continue; + descriptor = &sb->disks[realdev->sb->descriptor.number]; + if (descriptor->state & (1 << MD_FAULTY_DEVICE)) + continue; + if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) + continue; + return descriptor; + } + return NULL; +} + +/* + * parallel resyncing thread. + * + * FIXME: - make it abort with a dirty array on mdstop, now it just blocks + * - fix read error handing + */ + +int md_do_sync(struct md_dev *mddev) +{ + struct buffer_head *bh; + int max_blocks, blocksize, curr_bsize, percent=1, j; + kdev_t read_disk = MKDEV(MD_MAJOR, mddev - md_dev); + int major = MAJOR(read_disk), minor = MINOR(read_disk); + unsigned long starttime; + + blocksize = blksize_size[major][minor]; + max_blocks = blk_size[major][minor] / (blocksize >> 10); + + printk("... resync log\n"); + printk(" .... mddev->nb_dev: %d\n", mddev->nb_dev); + printk(" .... raid array: %s\n", kdevname(read_disk)); + printk(" .... max_blocks: %d blocksize: %d\n", max_blocks, blocksize); + printk("md: syncing RAID array %s\n", kdevname(read_disk)); + + mddev->busy++; + + starttime=jiffies; + for (j = 0; j < max_blocks; j++) { + + /* + * B careful. When some1 mounts a non-'blocksize' filesystem + * then we get the blocksize changed right under us. Go deal + * with it transparently, recalculate 'blocksize', 'j' and + * 'max_blocks': + */ + curr_bsize = blksize_size[major][minor]; + if (curr_bsize != blocksize) { +diff_blocksize: + if (curr_bsize > blocksize) + /* + * this is safe, rounds downwards. + */ + j /= curr_bsize/blocksize; + else + j *= blocksize/curr_bsize; + + blocksize = curr_bsize; + max_blocks = blk_size[major][minor] / (blocksize >> 10); + } + if ((bh = breada (read_disk, j, blocksize, j * blocksize, + max_blocks * blocksize)) != NULL) { + mark_buffer_dirty(bh, 1); + brelse(bh); + } else { + /* + * FIXME: Ugly, but set_blocksize() isnt safe ... + */ + curr_bsize = blksize_size[major][minor]; + if (curr_bsize != blocksize) + goto diff_blocksize; + + /* + * It's a real read problem. FIXME, handle this + * a better way. + */ + printk ( KERN_ALERT + "read error, stopping reconstruction.\n"); + mddev->busy--; + return 1; + } + + /* + * Lets sleep some if we are faster than our speed limit: + */ + while (blocksize*j/(jiffies-starttime+1)*HZ/1024 > SPEED_LIMIT) + { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies+1; + schedule(); + } + + /* + * FIXME: put this status bar thing into /proc + */ + if (!(j%(max_blocks/100))) { + if (!(percent%10)) + printk (" %03d%% done.\n",percent); + else + printk ("."); + percent++; + } + } + fsync_dev(read_disk); + printk("md: %s: sync done.\n", kdevname(read_disk)); + mddev->busy--; + return 0; +} + +/* + * This is a kernel thread which: syncs a spare disk with the active array + * + * the amount of foolproofing might seem to be a tad excessive, but an + * early (not so error-safe) version of raid1syncd synced the first 0.5 gigs + * of my root partition with the first 0.5 gigs of my /home partition ... so + * i'm a bit nervous ;) + */ +void mdsyncd (void *data) +{ + int i; + struct md_dev *mddev; + md_superblock_t *sb; + md_descriptor_t *spare; + unsigned long flags; + + for (i = 0, mddev = md_dev; i < MAX_MD_DEV; i++, mddev++) { + if ((sb = mddev->sb) == NULL) + continue; + if (sb->active_disks == sb->raid_disks) + continue; + if (!sb->spare_disks) + continue; + if ((spare = get_spare(mddev)) == NULL) + continue; + if (!mddev->pers->mark_spare) + continue; + if (mddev->pers->mark_spare(mddev, spare, SPARE_WRITE)) + continue; + if (md_do_sync(mddev) || (spare->state & (1 << MD_FAULTY_DEVICE))) { + mddev->pers->mark_spare(mddev, spare, SPARE_INACTIVE); + continue; + } + save_flags(flags); + cli(); + mddev->pers->mark_spare(mddev, spare, SPARE_ACTIVE); + spare->state |= (1 << MD_SYNC_DEVICE); + spare->state |= (1 << MD_ACTIVE_DEVICE); + sb->spare_disks--; + sb->active_disks++; + mddev->sb_dirty = 1; + md_update_sb(mddev - md_dev); + restore_flags(flags); + } + +} + void linear_init (void); void raid0_init (void); void raid1_init (void); @@ -550,7 +1164,11 @@ __initfunc(int md_init (void)) { - printk ("md driver %s MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_VERSION, MAX_MD_DEV, MAX_REAL); + int i; + + printk ("md driver %d.%d.%d MAX_MD_DEV=%d, MAX_REAL=%d\n", + MD_MAJOR_VERSION, MD_MINOR_VERSION, MD_PATCHLEVEL_VERSION, + MAX_MD_DEV, MAX_REAL); if (register_blkdev (MD_MAJOR, "md", &md_fops)) { @@ -558,18 +1176,39 @@ return (-1); } + memset(md_threads, 0, MAX_MD_THREADS * sizeof(struct md_thread)); + printk("md: starting %d kernel threads\n", MAX_MD_THREADS); + for (i = 0; i < MAX_MD_THREADS; i++) { + md_threads[i].run = NULL; + init_waitqueue(&md_threads[i].wqueue); + md_threads[i].flags = 0; + kernel_thread (md_thread, md_threads + i, 0); + } + blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST; blk_dev[MD_MAJOR].current_request=NULL; read_ahead[MD_MAJOR]=INT_MAX; + memset(md_dev, 0, MAX_MD_DEV * sizeof (struct md_dev)); md_gendisk.next=gendisk_head; gendisk_head=&md_gendisk; +#if SUPPORT_RECONSTRUCTION + if ((md_sync_thread = md_register_thread(mdsyncd, NULL)) == NULL) + printk("md: bug: md_sync_thread == NULL\n"); +#endif /* SUPPORT_RECONSTRUCTION */ + #ifdef CONFIG_MD_LINEAR linear_init (); #endif #ifdef CONFIG_MD_STRIPED raid0_init (); +#endif +#ifdef CONFIG_MD_MIRRORING + raid1_init (); +#endif +#ifdef CONFIG_MD_RAID5 + raid5_init (); #endif return (0); diff -u --recursive --new-file v2.1.62/linux/drivers/block/raid0.c linux/drivers/block/raid0.c --- v2.1.62/linux/drivers/block/raid0.c Sun Sep 7 13:10:42 1997 +++ linux/drivers/block/raid0.c Sat Nov 8 11:39:12 1997 @@ -250,6 +250,7 @@ data->strip_zone[j].size); } #endif + sz+=sprintf (page+sz, " %dk chunks", 1< +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR MD_MAJOR +#define MD_DRIVER +#define MD_PERSONALITY + +/* + * The following can be used to debug the driver + */ +/*#define RAID1_DEBUG*/ +#ifdef RAID1_DEBUG +#define PRINTK(x) do { printk x; } while (0); +#else +#define PRINTK(x) do { ; } while (0); +#endif + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +static struct md_personality raid1_personality; +static struct md_thread *raid1_thread = NULL; +struct buffer_head *raid1_retry_list = NULL; + +static int __raid1_map (struct md_dev *mddev, kdev_t *rdev, + unsigned long *rsector, unsigned long size) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + int i, n = raid_conf->raid_disks; + + /* + * Later we do read balancing on the read side + * now we use the first available disk. + */ + + PRINTK(("raid1_map().\n")); + + for (i=0; imirrors[i].operational) { + *rdev = raid_conf->mirrors[i].dev; + return (0); + } + } + + printk (KERN_ERR "raid1_map(): huh, no more operational devices?\n"); + return (-1); +} + +static int raid1_map (struct md_dev *mddev, kdev_t *rdev, + unsigned long *rsector, unsigned long size) +{ + return 0; +} + +void raid1_reschedule_retry (struct buffer_head *bh) +{ + struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->b_dev_id); + + PRINTK(("raid1_reschedule_retry().\n")); + + r1_bh->next_retry = raid1_retry_list; + raid1_retry_list = bh; + md_wakeup_thread(raid1_thread); +} + +/* + * raid1_end_buffer_io() is called when we have finished servicing a mirrored + * operation and are ready to return a success/failure code to the buffer + * cache layer. + */ +static inline void raid1_end_buffer_io(struct raid1_bh *r1_bh, int uptodate) +{ + struct buffer_head *bh = r1_bh->master_bh; + + bh->b_end_io(bh, uptodate); + kfree(r1_bh); +} + +int raid1_one_error=0; + +void raid1_end_request (struct buffer_head *bh, int uptodate) +{ + struct raid1_bh * r1_bh = (struct raid1_bh *)(bh->b_dev_id); + unsigned long flags; + + save_flags(flags); + cli(); + PRINTK(("raid1_end_request().\n")); + + if (raid1_one_error) { + raid1_one_error=0; + uptodate=0; + } + /* + * this branch is our 'one mirror IO has finished' event handler: + */ + if (!uptodate) + md_error (bh->b_dev, bh->b_rdev); + else { + /* + * Set BH_Uptodate in our master buffer_head, so that + * we will return a good error code for to the higher + * levels even if IO on some other mirrored buffer fails. + * + * The 'master' represents the complex operation to + * user-side. So if something waits for IO, then it will + * wait for the 'master' buffer_head. + */ + set_bit (BH_Uptodate, &r1_bh->state); + } + + /* + * We split up the read and write side, imho they are + * conceptually different. + */ + + if ( (r1_bh->cmd == READ) || (r1_bh->cmd == READA) ) { + + PRINTK(("raid1_end_request(), read branch.\n")); + + /* + * we have only one buffer_head on the read side + */ + if (uptodate) { + PRINTK(("raid1_end_request(), read branch, uptodate.\n")); + raid1_end_buffer_io(r1_bh, uptodate); + restore_flags(flags); + return; + } + /* + * oops, read error: + */ + printk(KERN_ERR "raid1: %s: rescheduling block %lu\n", + kdevname(bh->b_dev), bh->b_blocknr); + raid1_reschedule_retry (bh); + restore_flags(flags); + return; + } + + /* + * WRITE or WRITEA. + */ + PRINTK(("raid1_end_request(), write branch.\n")); + + /* + * lets see if all mirrored write operations have finished + * already [we have irqs off, so we can decrease]: + */ + + if (!--r1_bh->remaining) { + struct md_dev *mddev = r1_bh->mddev; + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + int i, n = raid_conf->raid_disks; + + PRINTK(("raid1_end_request(), remaining == 0.\n")); + + for ( i=0; imirror_bh[i]) kfree(r1_bh->mirror_bh[i]); + + raid1_end_buffer_io(r1_bh, test_bit(BH_Uptodate, &r1_bh->state)); + } + else PRINTK(("raid1_end_request(), remaining == %u.\n", r1_bh->remaining)); + restore_flags(flags); +} + +/* This routine checks if the undelying device is an md device and in that + * case it maps the blocks before putting the request on the queue + */ +static inline void +map_and_make_request (int rw, struct buffer_head *bh) +{ + if (MAJOR (bh->b_rdev) == MD_MAJOR) + md_map (MINOR (bh->b_rdev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9); + clear_bit(BH_Lock, &bh->b_state); + make_request (MAJOR (bh->b_rdev), rw, bh); +} + +static int +raid1_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) +{ + + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + struct buffer_head *mirror_bh[MD_SB_DISKS], *bh_req; + struct raid1_bh * r1_bh; + int n = raid_conf->raid_disks, i, sum_bhs = 0, switch_disks = 0, sectors; + struct mirror_info *mirror; + + PRINTK(("raid1_make_request().\n")); + + while (!( /* FIXME: now we are rather fault tolerant than nice */ + r1_bh = kmalloc (sizeof (struct raid1_bh), GFP_KERNEL) + ) ) + printk ("raid1_make_request(#1): out of memory\n"); + memset (r1_bh, 0, sizeof (struct raid1_bh)); + +/* + * make_request() can abort the operation when READA or WRITEA are being + * used and no empty request is available. + * + * Currently, just replace the command with READ/WRITE. + */ + if (rw == READA) rw = READ; + if (rw == WRITEA) rw = WRITE; + + if (rw == WRITE || rw == WRITEA) + mark_buffer_clean(bh); /* Too early ? */ + +/* + * i think the read and write branch should be separated completely, since we want + * to do read balancing on the read side for example. Comments? :) --mingo + */ + + r1_bh->master_bh=bh; + r1_bh->mddev=mddev; + r1_bh->cmd = rw; + + if (rw==READ || rw==READA) { + int last_used = raid_conf->last_used; + PRINTK(("raid1_make_request(), read branch.\n")); + mirror = raid_conf->mirrors + last_used; + bh->b_rdev = mirror->dev; + sectors = bh->b_size >> 9; + if (bh->b_blocknr * sectors == raid_conf->next_sect) { + raid_conf->sect_count += sectors; + if (raid_conf->sect_count >= mirror->sect_limit) + switch_disks = 1; + } else + switch_disks = 1; + raid_conf->next_sect = (bh->b_blocknr + 1) * sectors; + if (switch_disks) { + PRINTK(("read-balancing: switching %d -> %d (%d sectors)\n", last_used, mirror->next, raid_conf->sect_count)); + raid_conf->sect_count = 0; + last_used = raid_conf->last_used = mirror->next; + /* + * Do not switch to write-only disks ... resyncing + * is in progress + */ + while (raid_conf->mirrors[last_used].write_only) + raid_conf->last_used = raid_conf->mirrors[last_used].next; + } + PRINTK (("raid1 read queue: %d %d\n", MAJOR (bh->b_rdev), MINOR (bh->b_rdev))); + bh_req = &r1_bh->bh_req; + memcpy(bh_req, bh, sizeof(*bh)); + bh_req->b_end_io = raid1_end_request; + bh_req->b_dev_id = r1_bh; + map_and_make_request (rw, bh_req); + return 0; + } + + /* + * WRITE or WRITEA. + */ + PRINTK(("raid1_make_request(n=%d), write branch.\n",n)); + + for (i = 0; i < n; i++) { + + if (!raid_conf->mirrors [i].operational) { + /* + * the r1_bh->mirror_bh[i] pointer remains NULL + */ + mirror_bh[i] = NULL; + continue; + } + + /* + * We should use a private pool (size depending on NR_REQUEST), + * to avoid writes filling up the memory with bhs + * + * Such pools are much faster than kmalloc anyways (so we waste almost + * nothing by not using the master bh when writing and win alot of cleanness) + * + * but for now we are cool enough. --mingo + * + * It's safe to sleep here, buffer heads cannot be used in a shared + * manner in the write branch. Look how we lock the buffer at the beginning + * of this function to grok the difference ;) + */ + while (!( /* FIXME: now we are rather fault tolerant than nice */ + mirror_bh[i] = kmalloc (sizeof (struct buffer_head), GFP_KERNEL) + ) ) + printk ("raid1_make_request(#2): out of memory\n"); + memset (mirror_bh[i], 0, sizeof (struct buffer_head)); + + /* + * prepare mirrored bh (fields ordered for max mem throughput): + */ + mirror_bh [i]->b_blocknr = bh->b_blocknr; + mirror_bh [i]->b_dev = bh->b_dev; + mirror_bh [i]->b_rdev = raid_conf->mirrors [i].dev; + mirror_bh [i]->b_rsector = bh->b_rsector; + mirror_bh [i]->b_state = (1<b_count = 1; + mirror_bh [i]->b_size = bh->b_size; + mirror_bh [i]->b_data = bh->b_data; + mirror_bh [i]->b_list = BUF_LOCKED; + mirror_bh [i]->b_end_io = raid1_end_request; + mirror_bh [i]->b_dev_id = r1_bh; + + r1_bh->mirror_bh[i] = mirror_bh[i]; + sum_bhs++; + } + + r1_bh->remaining = sum_bhs; + + PRINTK(("raid1_make_request(), write branch, sum_bhs=%d.\n",sum_bhs)); + + /* + * We have to be a bit careful about the semaphore above, thats why we + * start the requests separately. Since kmalloc() could fail, sleep and + * make_request() can sleep too, this is the safer solution. Imagine, + * end_request decreasing the semaphore before we could have set it up ... + * We could play tricks with the semaphore (presetting it and correcting + * at the end if sum_bhs is not 'n' but we have to do end_request by hand + * if all requests finish until we had a chance to set up the semaphore + * correctly ... lots of races). + */ + for (i = 0; i < n; i++) + if (mirror_bh [i] != NULL) + map_and_make_request (rw, mirror_bh [i]); + + return (0); +} + +static int raid1_status (char *page, int minor, struct md_dev *mddev) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + int sz = 0, i; + + sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks); + for (i = 0; i < raid_conf->raid_disks; i++) + sz += sprintf (page+sz, "%s", raid_conf->mirrors [i].operational ? "U" : "_"); + sz += sprintf (page+sz, "]"); + return sz; +} + +static void raid1_fix_links (struct raid1_data *raid_conf, int failed_index) +{ + int disks = raid_conf->raid_disks; + int j; + + for (j = 0; j < disks; j++) + if (raid_conf->mirrors [j].next == failed_index) + raid_conf->mirrors [j].next = raid_conf->mirrors [failed_index].next; +} + +#define LAST_DISK KERN_ALERT \ +"raid1: only one disk left and IO error.\n" + +#define NO_SPARE_DISK KERN_ALERT \ +"raid1: no spare disk left, degrading mirror level by one.\n" + +#define DISK_FAILED KERN_ALERT \ +"raid1: Disk failure on %s, disabling device. \n" \ +" Operation continuing on %d devices\n" + +#define START_SYNCING KERN_ALERT \ +"raid1: start syncing spare disk.\n" + +#define ALREADY_SYNCING KERN_INFO \ +"raid1: syncing already in progress.\n" + +static int raid1_error (struct md_dev *mddev, kdev_t dev) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + struct mirror_info *mirror; + md_superblock_t *sb = mddev->sb; + int disks = raid_conf->raid_disks; + int i; + + PRINTK(("raid1_error called\n")); + + if (raid_conf->working_disks == 1) { + /* + * Uh oh, we can do nothing if this is our last disk, but + * first check if this is a queued request for a device + * which has just failed. + */ + for (i = 0, mirror = raid_conf->mirrors; i < disks; + i++, mirror++) + if (mirror->dev == dev && !mirror->operational) + return 0; + printk (LAST_DISK); + } else { + /* Mark disk as unusable */ + for (i = 0, mirror = raid_conf->mirrors; i < disks; + i++, mirror++) { + if (mirror->dev == dev && mirror->operational){ + mirror->operational = 0; + raid1_fix_links (raid_conf, i); + sb->disks[mirror->number].state |= + (1 << MD_FAULTY_DEVICE); + sb->disks[mirror->number].state &= + ~(1 << MD_SYNC_DEVICE); + sb->disks[mirror->number].state &= + ~(1 << MD_ACTIVE_DEVICE); + sb->active_disks--; + sb->working_disks--; + sb->failed_disks++; + mddev->sb_dirty = 1; + md_wakeup_thread(raid1_thread); + raid_conf->working_disks--; + printk (DISK_FAILED, kdevname (dev), + raid_conf->working_disks); + } + } + } + return 0; +} + +#undef LAST_DISK +#undef NO_SPARE_DISK +#undef DISK_FAILED +#undef START_SYNCING + +/* + * This is the personality-specific hot-addition routine + */ + +#define NO_SUPERBLOCK KERN_ERR \ +"raid1: cannot hot-add disk to the array with no RAID superblock\n" + +#define WRONG_LEVEL KERN_ERR \ +"raid1: hot-add: level of disk is not RAID-1\n" + +#define HOT_ADD_SUCCEEDED KERN_INFO \ +"raid1: device %s hot-added\n" + +static int raid1_hot_add_disk (struct md_dev *mddev, kdev_t dev) +{ + unsigned long flags; + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + struct mirror_info *mirror; + md_superblock_t *sb = mddev->sb; + struct real_dev * realdev; + int n; + + /* + * The device has it's superblock already read and it was found + * to be consistent for generic RAID usage, now we check wether + * it's usable for RAID-1 hot addition. + */ + + n = mddev->nb_dev++; + realdev = &mddev->devices[n]; + if (!realdev->sb) { + printk (NO_SUPERBLOCK); + return -EINVAL; + } + if (realdev->sb->level != 1) { + printk (WRONG_LEVEL); + return -EINVAL; + } + /* FIXME: are there other things left we could sanity-check? */ + + /* + * We have to disable interrupts, as our RAID-1 state is used + * from irq handlers as well. + */ + save_flags(flags); + cli(); + + raid_conf->raid_disks++; + mirror = raid_conf->mirrors+n; + + mirror->number=n; + mirror->raid_disk=n; + mirror->dev=dev; + mirror->next=0; /* FIXME */ + mirror->sect_limit=128; + + mirror->operational=0; + mirror->spare=1; + mirror->write_only=0; + + sb->disks[n].state |= (1 << MD_FAULTY_DEVICE); + sb->disks[n].state &= ~(1 << MD_SYNC_DEVICE); + sb->disks[n].state &= ~(1 << MD_ACTIVE_DEVICE); + sb->nr_disks++; + sb->spare_disks++; + + restore_flags(flags); + + md_update_sb(MINOR(dev)); + + printk (HOT_ADD_SUCCEEDED, kdevname(realdev->dev)); + + return 0; +} + +#undef NO_SUPERBLOCK +#undef WRONG_LEVEL +#undef HOT_ADD_SUCCEEDED + +/* + * Insert the spare disk into the drive-ring + */ +static void add_ring(struct raid1_data *raid_conf, struct mirror_info *mirror) +{ + int j, next; + struct mirror_info *p = raid_conf->mirrors; + + for (j = 0; j < raid_conf->raid_disks; j++, p++) + if (p->operational && !p->write_only) { + next = p->next; + p->next = mirror->raid_disk; + mirror->next = next; + return; + } + printk("raid1: bug: no read-operational devices\n"); +} + +static int raid1_mark_spare(struct md_dev *mddev, md_descriptor_t *spare, + int state) +{ + int i = 0, failed_disk = -1; + struct raid1_data *raid_conf = mddev->private; + struct mirror_info *mirror = raid_conf->mirrors; + md_descriptor_t *descriptor; + unsigned long flags; + + for (i = 0; i < MD_SB_DISKS; i++, mirror++) { + if (mirror->spare && mirror->number == spare->number) + goto found; + } + return 1; +found: + for (i = 0, mirror = raid_conf->mirrors; i < raid_conf->raid_disks; + i++, mirror++) + if (!mirror->operational) + failed_disk = i; + + save_flags(flags); + cli(); + switch (state) { + case SPARE_WRITE: + mirror->operational = 1; + mirror->write_only = 1; + raid_conf->raid_disks = MAX(raid_conf->raid_disks, + mirror->raid_disk + 1); + break; + case SPARE_INACTIVE: + mirror->operational = 0; + mirror->write_only = 0; + break; + case SPARE_ACTIVE: + mirror->spare = 0; + mirror->write_only = 0; + raid_conf->working_disks++; + add_ring(raid_conf, mirror); + + if (failed_disk != -1) { + descriptor = &mddev->sb->disks[raid_conf->mirrors[failed_disk].number]; + i = spare->raid_disk; + spare->raid_disk = descriptor->raid_disk; + descriptor->raid_disk = i; + } + break; + default: + printk("raid1_mark_spare: bug: state == %d\n", state); + restore_flags(flags); + return 1; + } + restore_flags(flags); + return 0; +} + +/* + * This is a kernel thread which: + * + * 1. Retries failed read operations on working mirrors. + * 2. Updates the raid superblock when problems encounter. + */ +void raid1d (void *data) +{ + struct buffer_head *bh; + kdev_t dev; + unsigned long flags; + struct raid1_bh * r1_bh; + struct md_dev *mddev; + + PRINTK(("raid1d() active\n")); + save_flags(flags); + cli(); + while (raid1_retry_list) { + bh = raid1_retry_list; + r1_bh = (struct raid1_bh *)(bh->b_dev_id); + raid1_retry_list = r1_bh->next_retry; + restore_flags(flags); + + mddev = md_dev + MINOR(bh->b_dev); + if (mddev->sb_dirty) { + printk("dirty sb detected, updating.\n"); + mddev->sb_dirty = 0; + md_update_sb(MINOR(bh->b_dev)); + } + dev = bh->b_rdev; + __raid1_map (md_dev + MINOR(bh->b_dev), &bh->b_rdev, &bh->b_rsector, bh->b_size >> 9); + if (bh->b_rdev == dev) { + printk (KERN_ALERT + "raid1: %s: unrecoverable I/O read error for block %lu\n", + kdevname(bh->b_dev), bh->b_blocknr); + raid1_end_buffer_io(r1_bh, 0); + } else { + printk (KERN_ERR "raid1: %s: redirecting sector %lu to another mirror\n", + kdevname(bh->b_dev), bh->b_blocknr); + map_and_make_request (r1_bh->cmd, bh); + } + cli(); + } + restore_flags(flags); +} + +/* + * This will catch the scenario in which one of the mirrors was + * mounted as a normal device rather than as a part of a raid set. + */ +static int __check_consistency (struct md_dev *mddev, int row) +{ + struct raid1_data *raid_conf = mddev->private; + kdev_t dev; + struct buffer_head *bh = NULL; + int i, rc = 0; + char *buffer = NULL; + + for (i = 0; i < raid_conf->raid_disks; i++) { + if (!raid_conf->mirrors[i].operational) + continue; + dev = raid_conf->mirrors[i].dev; + set_blocksize(dev, 4096); + if ((bh = bread(dev, row / 4, 4096)) == NULL) + break; + if (!buffer) { + buffer = (char *) __get_free_page(GFP_KERNEL); + if (!buffer) + break; + memcpy(buffer, bh->b_data, 4096); + } else if (memcmp(buffer, bh->b_data, 4096)) { + rc = 1; + break; + } + bforget(bh); + fsync_dev(dev); + invalidate_buffers(dev); + bh = NULL; + } + if (buffer) + free_page((unsigned long) buffer); + if (bh) { + dev = bh->b_dev; + bforget(bh); + fsync_dev(dev); + invalidate_buffers(dev); + } + return rc; +} + +static int check_consistency (struct md_dev *mddev) +{ + int size = mddev->sb->size; + int row; + + for (row = 0; row < size; row += size / 8) + if (__check_consistency(mddev, row)) + return 1; + return 0; +} + +static int raid1_run (int minor, struct md_dev *mddev) +{ + struct raid1_data *raid_conf; + int i, j, raid_disk; + md_superblock_t *sb = mddev->sb; + md_descriptor_t *descriptor; + struct real_dev *realdev; + + MOD_INC_USE_COUNT; + + if (sb->level != 1) { + printk("raid1: %s: raid level not set to mirroring (%d)\n", + kdevname(MKDEV(MD_MAJOR, minor)), sb->level); + MOD_DEC_USE_COUNT; + return -EIO; + } + /**** + * copy the now verified devices into our private RAID1 bookkeeping + * area. [whatever we allocate in raid1_run(), should be freed in + * raid1_stop()] + */ + + while (!( /* FIXME: now we are rather fault tolerant than nice */ + mddev->private = kmalloc (sizeof (struct raid1_data), GFP_KERNEL) + ) ) + printk ("raid1_run(): out of memory\n"); + raid_conf = mddev->private; + memset(raid_conf, 0, sizeof(*raid_conf)); + + PRINTK(("raid1_run(%d) called.\n", minor)); + + for (i = 0; i < mddev->nb_dev; i++) { + realdev = &mddev->devices[i]; + if (!realdev->sb) { + printk(KERN_ERR "raid1: disabled mirror %s (couldn't access raid superblock)\n", kdevname(realdev->dev)); + continue; + } + + /* + * This is important -- we are using the descriptor on + * the disk only to get a pointer to the descriptor on + * the main superblock, which might be more recent. + */ + descriptor = &sb->disks[realdev->sb->descriptor.number]; + if (descriptor->state & (1 << MD_FAULTY_DEVICE)) { + printk(KERN_ERR "raid1: disabled mirror %s (errors detected)\n", kdevname(realdev->dev)); + continue; + } + if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) { + if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) { + printk(KERN_ERR "raid1: disabled mirror %s (not in sync)\n", kdevname(realdev->dev)); + continue; + } + raid_disk = descriptor->raid_disk; + if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) { + printk(KERN_ERR "raid1: disabled mirror %s (inconsistent descriptor)\n", kdevname(realdev->dev)); + continue; + } + if (raid_conf->mirrors[raid_disk].operational) { + printk(KERN_ERR "raid1: disabled mirror %s (mirror %d already operational)\n", kdevname(realdev->dev), raid_disk); + continue; + } + printk(KERN_INFO "raid1: device %s operational as mirror %d\n", kdevname(realdev->dev), raid_disk); + raid_conf->mirrors[raid_disk].number = descriptor->number; + raid_conf->mirrors[raid_disk].raid_disk = raid_disk; + raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev; + raid_conf->mirrors[raid_disk].operational = 1; + raid_conf->mirrors[raid_disk].sect_limit = 128; + raid_conf->working_disks++; + } else { + /* + * Must be a spare disk .. + */ + printk(KERN_INFO "raid1: spare disk %s\n", kdevname(realdev->dev)); + raid_disk = descriptor->raid_disk; + raid_conf->mirrors[raid_disk].number = descriptor->number; + raid_conf->mirrors[raid_disk].raid_disk = raid_disk; + raid_conf->mirrors[raid_disk].dev = mddev->devices [i].dev; + raid_conf->mirrors[raid_disk].sect_limit = 128; + + raid_conf->mirrors[raid_disk].operational = 0; + raid_conf->mirrors[raid_disk].write_only = 0; + raid_conf->mirrors[raid_disk].spare = 1; + } + } + if (!raid_conf->working_disks) { + printk(KERN_ERR "raid1: no operational mirrors for %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + kfree(raid_conf); + mddev->private = NULL; + MOD_DEC_USE_COUNT; + return -EIO; + } + + raid_conf->raid_disks = sb->raid_disks; + raid_conf->mddev = mddev; + + for (j = 0; !raid_conf->mirrors[j].operational; j++); + raid_conf->last_used = j; + for (i = raid_conf->raid_disks - 1; i >= 0; i--) { + if (raid_conf->mirrors[i].operational) { + PRINTK(("raid_conf->mirrors[%d].next == %d\n", i, j)); + raid_conf->mirrors[i].next = j; + j = i; + } + } + + if (check_consistency(mddev)) { + printk(KERN_ERR "raid1: detected mirror differences -- run ckraid\n"); + sb->state |= 1 << MD_SB_ERRORS; + kfree(raid_conf); + mddev->private = NULL; + MOD_DEC_USE_COUNT; + return -EIO; + } + + /* + * Regenerate the "device is in sync with the raid set" bit for + * each device. + */ + for (i = 0; i < sb->nr_disks ; i++) { + sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE); + for (j = 0; j < sb->raid_disks; j++) { + if (!raid_conf->mirrors[j].operational) + continue; + if (sb->disks[i].number == raid_conf->mirrors[j].number) + sb->disks[i].state |= 1 << MD_SYNC_DEVICE; + } + } + sb->active_disks = raid_conf->working_disks; + + printk("raid1: raid set %s active with %d out of %d mirrors\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks); + /* Ok, everything is just fine now */ + return (0); +} + +static int raid1_stop (int minor, struct md_dev *mddev) +{ + struct raid1_data *raid_conf = (struct raid1_data *) mddev->private; + + kfree (raid_conf); + mddev->private = NULL; + MOD_DEC_USE_COUNT; + return 0; +} + +static struct md_personality raid1_personality= +{ + "raid1", + raid1_map, + raid1_make_request, + raid1_end_request, + raid1_run, + raid1_stop, + raid1_status, + NULL, /* no ioctls */ + 0, + raid1_error, + raid1_hot_add_disk, + /* raid1_hot_remove_drive */ NULL, + raid1_mark_spare +}; + +int raid1_init (void) +{ + if ((raid1_thread = md_register_thread(raid1d, NULL)) == NULL) + return -EBUSY; + return register_md_personality (RAID1, &raid1_personality); +} + +#ifdef MODULE +int init_module (void) +{ + return raid1_init(); +} + +void cleanup_module (void) +{ + md_unregister_thread (raid1_thread); + unregister_md_personality (RAID1); +} +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/block/raid5.c linux/drivers/block/raid5.c --- v2.1.62/linux/drivers/block/raid5.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/block/raid5.c Sat Nov 8 11:39:12 1997 @@ -0,0 +1,1652 @@ +/***************************************************************************** + * raid5.c : Multiple Devices driver for Linux + * Copyright (C) 1996, 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman + * + * RAID-5 management functions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct md_personality raid5_personality; + +/* + * Stripe cache + */ +#define NR_STRIPES 128 +#define HASH_PAGES 1 +#define HASH_PAGES_ORDER 0 +#define NR_HASH (HASH_PAGES * PAGE_SIZE / sizeof(struct stripe_head *)) +#define HASH_MASK (NR_HASH - 1) +#define stripe_hash(raid_conf, sect, size) ((raid_conf)->stripe_hashtbl[((sect) / (size >> 9)) & HASH_MASK]) + +/* + * The following can be used to debug the driver + */ +#define RAID5_DEBUG 0 + +#if RAID5_DEBUG +#define PRINTK(x) do { printk x; } while (0); +#else +#define PRINTK(x) do { ; } while (0) +#endif + +static inline int stripe_locked(struct stripe_head *sh) +{ + return test_bit(STRIPE_LOCKED, &sh->state); +} + +static inline int stripe_error(struct stripe_head *sh) +{ + return test_bit(STRIPE_ERROR, &sh->state); +} + +/* + * Stripes are locked whenever new buffers can't be added to them. + */ +static inline void lock_stripe(struct stripe_head *sh) +{ + struct raid5_data *raid_conf = sh->raid_conf; + if (!test_and_set_bit(STRIPE_LOCKED, &sh->state)) { + PRINTK(("locking stripe %lu\n", sh->sector)); + raid_conf->nr_locked_stripes++; + } +} + +static inline void unlock_stripe(struct stripe_head *sh) +{ + struct raid5_data *raid_conf = sh->raid_conf; + if (test_and_clear_bit(STRIPE_LOCKED, &sh->state)) { + PRINTK(("unlocking stripe %lu\n", sh->sector)); + raid_conf->nr_locked_stripes--; + wake_up(&sh->wait); + } +} + +static inline void finish_stripe(struct stripe_head *sh) +{ + struct raid5_data *raid_conf = sh->raid_conf; + unlock_stripe(sh); + sh->cmd = STRIPE_NONE; + sh->phase = PHASE_COMPLETE; + raid_conf->nr_pending_stripes--; + raid_conf->nr_cached_stripes++; + wake_up(&raid_conf->wait_for_stripe); +} + +void __wait_on_stripe(struct stripe_head *sh) +{ + struct wait_queue wait = { current, NULL }; + + PRINTK(("wait_on_stripe %lu\n", sh->sector)); + sh->count++; + add_wait_queue(&sh->wait, &wait); +repeat: + current->state = TASK_UNINTERRUPTIBLE; + if (stripe_locked(sh)) { + schedule(); + goto repeat; + } + PRINTK(("wait_on_stripe %lu done\n", sh->sector)); + remove_wait_queue(&sh->wait, &wait); + sh->count--; + current->state = TASK_RUNNING; +} + +static inline void wait_on_stripe(struct stripe_head *sh) +{ + if (stripe_locked(sh)) + __wait_on_stripe(sh); +} + +static inline void remove_hash(struct raid5_data *raid_conf, struct stripe_head *sh) +{ + PRINTK(("remove_hash(), stripe %lu\n", sh->sector)); + + if (sh->hash_pprev) { + if (sh->hash_next) + sh->hash_next->hash_pprev = sh->hash_pprev; + *sh->hash_pprev = sh->hash_next; + sh->hash_pprev = NULL; + raid_conf->nr_hashed_stripes--; + } +} + +static inline void insert_hash(struct raid5_data *raid_conf, struct stripe_head *sh) +{ + struct stripe_head **shp = &stripe_hash(raid_conf, sh->sector, sh->size); + + PRINTK(("insert_hash(), stripe %lu, nr_hashed_stripes %d\n", sh->sector, raid_conf->nr_hashed_stripes)); + + if ((sh->hash_next = *shp) != NULL) + (*shp)->hash_pprev = &sh->hash_next; + *shp = sh; + sh->hash_pprev = shp; + raid_conf->nr_hashed_stripes++; +} + +static struct buffer_head *get_free_buffer(struct stripe_head *sh, int b_size) +{ + struct buffer_head *bh; + unsigned long flags; + + save_flags(flags); + cli(); + if ((bh = sh->buffer_pool) == NULL) + return NULL; + sh->buffer_pool = bh->b_next; + bh->b_size = b_size; + restore_flags(flags); + return bh; +} + +static struct buffer_head *get_free_bh(struct stripe_head *sh) +{ + struct buffer_head *bh; + unsigned long flags; + + save_flags(flags); + cli(); + if ((bh = sh->bh_pool) == NULL) + return NULL; + sh->bh_pool = bh->b_next; + restore_flags(flags); + return bh; +} + +static void put_free_buffer(struct stripe_head *sh, struct buffer_head *bh) +{ + unsigned long flags; + + save_flags(flags); + cli(); + bh->b_next = sh->buffer_pool; + sh->buffer_pool = bh; + restore_flags(flags); +} + +static void put_free_bh(struct stripe_head *sh, struct buffer_head *bh) +{ + unsigned long flags; + + save_flags(flags); + cli(); + bh->b_next = sh->bh_pool; + sh->bh_pool = bh; + restore_flags(flags); +} + +static struct stripe_head *get_free_stripe(struct raid5_data *raid_conf) +{ + struct stripe_head *sh; + unsigned long flags; + + save_flags(flags); + cli(); + if ((sh = raid_conf->free_sh_list) == NULL) { + restore_flags(flags); + return NULL; + } + raid_conf->free_sh_list = sh->free_next; + raid_conf->nr_free_sh--; + if (!raid_conf->nr_free_sh && raid_conf->free_sh_list) + printk ("raid5: bug: free_sh_list != NULL, nr_free_sh == 0\n"); + restore_flags(flags); + if (sh->hash_pprev || sh->nr_pending || sh->count) + printk("get_free_stripe(): bug\n"); + return sh; +} + +static void put_free_stripe(struct raid5_data *raid_conf, struct stripe_head *sh) +{ + unsigned long flags; + + save_flags(flags); + cli(); + sh->free_next = raid_conf->free_sh_list; + raid_conf->free_sh_list = sh; + raid_conf->nr_free_sh++; + restore_flags(flags); +} + +static void shrink_buffers(struct stripe_head *sh, int num) +{ + struct buffer_head *bh; + + while (num--) { + if ((bh = get_free_buffer(sh, -1)) == NULL) + return; + free_page((unsigned long) bh->b_data); + kfree(bh); + } +} + +static void shrink_bh(struct stripe_head *sh, int num) +{ + struct buffer_head *bh; + + while (num--) { + if ((bh = get_free_bh(sh)) == NULL) + return; + kfree(bh); + } +} + +static int grow_buffers(struct stripe_head *sh, int num, int b_size, int priority) +{ + struct buffer_head *bh; + + while (num--) { + if ((bh = kmalloc(sizeof(struct buffer_head), priority)) == NULL) + return 1; + memset(bh, 0, sizeof (struct buffer_head)); + bh->b_data = (char *) __get_free_page(priority); + if (!bh->b_data) { + kfree(bh); + return 1; + } + bh->b_size = b_size; + put_free_buffer(sh, bh); + } + return 0; +} + +static int grow_bh(struct stripe_head *sh, int num, int priority) +{ + struct buffer_head *bh; + + while (num--) { + if ((bh = kmalloc(sizeof(struct buffer_head), priority)) == NULL) + return 1; + memset(bh, 0, sizeof (struct buffer_head)); + put_free_bh(sh, bh); + } + return 0; +} + +static void raid5_kfree_buffer(struct stripe_head *sh, struct buffer_head *bh) +{ + unsigned long flags; + + save_flags(flags); + cli(); + put_free_buffer(sh, bh); + restore_flags(flags); +} + +static void raid5_kfree_bh(struct stripe_head *sh, struct buffer_head *bh) +{ + unsigned long flags; + + save_flags(flags); + cli(); + put_free_bh(sh, bh); + restore_flags(flags); +} + +static void raid5_kfree_old_bh(struct stripe_head *sh, int i) +{ + if (!sh->bh_old[i]) { + printk("raid5_kfree_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i); + return; + } + raid5_kfree_buffer(sh, sh->bh_old[i]); + sh->bh_old[i] = NULL; +} + +static void raid5_update_old_bh(struct stripe_head *sh, int i) +{ + PRINTK(("stripe %lu, idx %d, updating cache copy\n", sh->sector, i)); + if (!sh->bh_copy[i]) { + printk("raid5_update_old_bh: bug: sector %lu, index %d not present\n", sh->sector, i); + return; + } + if (sh->bh_old[i]) + raid5_kfree_old_bh(sh, i); + sh->bh_old[i] = sh->bh_copy[i]; + sh->bh_copy[i] = NULL; +} + +static void kfree_stripe(struct stripe_head *sh) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int disks = raid_conf->raid_disks, j; + + PRINTK(("kfree_stripe called, stripe %lu\n", sh->sector)); + if (sh->phase != PHASE_COMPLETE || stripe_locked(sh) || sh->count) { + printk("raid5: kfree_stripe(), sector %lu, phase %d, locked %d, count %d\n", sh->sector, sh->phase, stripe_locked(sh), sh->count); + return; + } + for (j = 0; j < disks; j++) { + if (sh->bh_old[j]) + raid5_kfree_old_bh(sh, j); + if (sh->bh_new[j] || sh->bh_copy[j]) + printk("raid5: bug: sector %lu, new %p, copy %p\n", sh->sector, sh->bh_new[j], sh->bh_copy[j]); + } + remove_hash(raid_conf, sh); + put_free_stripe(raid_conf, sh); +} + +static int shrink_stripe_cache(struct raid5_data *raid_conf, int nr) +{ + struct stripe_head *sh; + int i, count = 0; + + PRINTK(("shrink_stripe_cache called, %d/%d, clock %d\n", nr, raid_conf->nr_hashed_stripes, raid_conf->clock)); + for (i = 0; i < NR_HASH; i++) { +repeat: + sh = raid_conf->stripe_hashtbl[(i + raid_conf->clock) & HASH_MASK]; + for (; sh; sh = sh->hash_next) { + if (sh->phase != PHASE_COMPLETE) + continue; + if (stripe_locked(sh)) + continue; + if (sh->count) + continue; + kfree_stripe(sh); + if (++count == nr) { + PRINTK(("shrink completed, nr_hashed_stripes %d\n", raid_conf->nr_hashed_stripes)); + raid_conf->clock = (i + raid_conf->clock) & HASH_MASK; + return nr; + } + goto repeat; + } + } + PRINTK(("shrink completed, nr_hashed_stripes %d\n", raid_conf->nr_hashed_stripes)); + return count; +} + +static struct stripe_head *find_stripe(struct raid5_data *raid_conf, unsigned long sector, int size) +{ + struct stripe_head *sh; + + if (raid_conf->buffer_size != size) { + PRINTK(("switching size, %d --> %d\n", raid_conf->buffer_size, size)); + shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes); + raid_conf->buffer_size = size; + } + + PRINTK(("find_stripe, sector %lu\n", sector)); + for (sh = stripe_hash(raid_conf, sector, size); sh; sh = sh->hash_next) + if (sh->sector == sector && sh->raid_conf == raid_conf) { + if (sh->size == size) { + PRINTK(("found stripe %lu\n", sector)); + return sh; + } else { + PRINTK(("switching size for %lu, %d --> %d\n", sector, sh->size, size)); + kfree_stripe(sh); + break; + } + } + PRINTK(("stripe %lu not in cache\n", sector)); + return NULL; +} + +static int grow_stripes(struct raid5_data *raid_conf, int num, int priority) +{ + struct stripe_head *sh; + + while (num--) { + if ((sh = kmalloc(sizeof(struct stripe_head), priority)) == NULL) + return 1; + memset(sh, 0, sizeof(*sh)); + if (grow_buffers(sh, 2 * raid_conf->raid_disks, PAGE_SIZE, priority)) { + shrink_buffers(sh, 2 * raid_conf->raid_disks); + kfree(sh); + return 1; + } + if (grow_bh(sh, raid_conf->raid_disks, priority)) { + shrink_buffers(sh, 2 * raid_conf->raid_disks); + shrink_bh(sh, raid_conf->raid_disks); + kfree(sh); + return 1; + } + put_free_stripe(raid_conf, sh); + raid_conf->nr_stripes++; + } + return 0; +} + +static void shrink_stripes(struct raid5_data *raid_conf, int num) +{ + struct stripe_head *sh; + + while (num--) { + sh = get_free_stripe(raid_conf); + if (!sh) + break; + shrink_buffers(sh, raid_conf->raid_disks * 2); + shrink_bh(sh, raid_conf->raid_disks); + kfree(sh); + raid_conf->nr_stripes--; + } +} + +static struct stripe_head *kmalloc_stripe(struct raid5_data *raid_conf, unsigned long sector, int size) +{ + struct stripe_head *sh = NULL, *tmp; + struct buffer_head *buffer_pool, *bh_pool; + + PRINTK(("kmalloc_stripe called\n")); + + while ((sh = get_free_stripe(raid_conf)) == NULL) { + shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes / 8); + if ((sh = get_free_stripe(raid_conf)) != NULL) + break; + if (!raid_conf->nr_pending_stripes) + printk("raid5: bug: nr_free_sh == 0, nr_pending_stripes == 0\n"); + md_wakeup_thread(raid_conf->thread); + PRINTK(("waiting for some stripes to complete\n")); + sleep_on(&raid_conf->wait_for_stripe); + } + + /* + * The above might have slept, so perhaps another process + * already created the stripe for us.. + */ + if ((tmp = find_stripe(raid_conf, sector, size)) != NULL) { + put_free_stripe(raid_conf, sh); + wait_on_stripe(tmp); + return tmp; + } + if (sh) { + buffer_pool = sh->buffer_pool; + bh_pool = sh->bh_pool; + memset(sh, 0, sizeof(*sh)); + sh->buffer_pool = buffer_pool; + sh->bh_pool = bh_pool; + sh->phase = PHASE_COMPLETE; + sh->cmd = STRIPE_NONE; + sh->raid_conf = raid_conf; + sh->sector = sector; + sh->size = size; + raid_conf->nr_cached_stripes++; + insert_hash(raid_conf, sh); + } else printk("raid5: bug: kmalloc_stripe() == NULL\n"); + return sh; +} + +static struct stripe_head *get_stripe(struct raid5_data *raid_conf, unsigned long sector, int size) +{ + struct stripe_head *sh; + + PRINTK(("get_stripe, sector %lu\n", sector)); + sh = find_stripe(raid_conf, sector, size); + if (sh) + wait_on_stripe(sh); + else + sh = kmalloc_stripe(raid_conf, sector, size); + return sh; +} + + +static struct buffer_head *raid5_kmalloc_buffer(struct stripe_head *sh, int b_size) +{ + struct buffer_head *bh; + + if ((bh = get_free_buffer(sh, b_size)) == NULL) + printk("raid5: bug: raid5_kmalloc_buffer() == NULL\n"); + return bh; +} + +static struct buffer_head *raid5_kmalloc_bh(struct stripe_head *sh) +{ + struct buffer_head *bh; + + if ((bh = get_free_bh(sh)) == NULL) + printk("raid5: bug: raid5_kmalloc_bh() == NULL\n"); + return bh; +} + +static inline void raid5_end_buffer_io (struct stripe_head *sh, int i, int uptodate) +{ + struct buffer_head *bh = sh->bh_new[i]; + + sh->bh_new[i] = NULL; + raid5_kfree_bh(sh, sh->bh_req[i]); + sh->bh_req[i] = NULL; + bh->b_end_io(bh, uptodate); + if (!uptodate) + printk(KERN_ALERT "raid5: %s: unrecoverable I/O error for " + "block %lu\n", kdevname(bh->b_dev), bh->b_blocknr); +} + +static inline void raid5_mark_buffer_uptodate (struct buffer_head *bh, int uptodate) +{ + if (uptodate) + set_bit(BH_Uptodate, &bh->b_state); + else + clear_bit(BH_Uptodate, &bh->b_state); +} + +static void raid5_end_request (struct buffer_head * bh, int uptodate) +{ + struct stripe_head *sh = bh->b_dev_id; + struct raid5_data *raid_conf = sh->raid_conf; + int disks = raid_conf->raid_disks, i; + unsigned long flags; + + PRINTK(("end_request %lu, nr_pending %d\n", sh->sector, sh->nr_pending)); + save_flags(flags); + cli(); + raid5_mark_buffer_uptodate(bh, uptodate); + --sh->nr_pending; + if (!sh->nr_pending) { + md_wakeup_thread(raid_conf->thread); + atomic_inc(&raid_conf->nr_handle); + } + if (!uptodate) + md_error(bh->b_dev, bh->b_rdev); + if (raid_conf->failed_disks) { + for (i = 0; i < disks; i++) { + if (raid_conf->disks[i].operational) + continue; + if (bh != sh->bh_old[i] && bh != sh->bh_req[i] && bh != sh->bh_copy[i]) + continue; + if (bh->b_rdev != raid_conf->disks[i].dev) + continue; + set_bit(STRIPE_ERROR, &sh->state); + } + } + restore_flags(flags); +} + +static int raid5_map (struct md_dev *mddev, kdev_t *rdev, + unsigned long *rsector, unsigned long size) +{ + /* No complex mapping used: the core of the work is done in the + * request routine + */ + return 0; +} + +static void raid5_build_block (struct stripe_head *sh, struct buffer_head *bh, int i) +{ + struct raid5_data *raid_conf = sh->raid_conf; + struct md_dev *mddev = raid_conf->mddev; + int minor = (int) (mddev - md_dev); + char *b_data; + kdev_t dev = MKDEV(MD_MAJOR, minor); + int block = sh->sector / (sh->size >> 9); + + b_data = ((volatile struct buffer_head *) bh)->b_data; + memset (bh, 0, sizeof (struct buffer_head)); + init_buffer(bh, dev, block, raid5_end_request, sh); + ((volatile struct buffer_head *) bh)->b_data = b_data; + + bh->b_rdev = raid_conf->disks[i].dev; + bh->b_rsector = sh->sector; + + bh->b_state = (1 << BH_Req); + bh->b_size = sh->size; + bh->b_list = BUF_LOCKED; +} + +static int raid5_error (struct md_dev *mddev, kdev_t dev) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + md_superblock_t *sb = mddev->sb; + struct disk_info *disk; + int i; + + PRINTK(("raid5_error called\n")); + raid_conf->resync_parity = 0; + for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++) + if (disk->dev == dev && disk->operational) { + disk->operational = 0; + sb->disks[disk->number].state |= (1 << MD_FAULTY_DEVICE); + sb->disks[disk->number].state &= ~(1 << MD_SYNC_DEVICE); + sb->disks[disk->number].state &= ~(1 << MD_ACTIVE_DEVICE); + sb->active_disks--; + sb->working_disks--; + sb->failed_disks++; + mddev->sb_dirty = 1; + raid_conf->working_disks--; + raid_conf->failed_disks++; + md_wakeup_thread(raid_conf->thread); + printk (KERN_ALERT + "RAID5: Disk failure on %s, disabling device." + "Operation continuing on %d devices\n", + kdevname (dev), raid_conf->working_disks); + } + return 0; +} + +/* + * Input: a 'big' sector number, + * Output: index of the data and parity disk, and the sector # in them. + */ +static inline unsigned long +raid5_compute_sector (int r_sector, unsigned int raid_disks, unsigned int data_disks, + unsigned int * dd_idx, unsigned int * pd_idx, + struct raid5_data *raid_conf) +{ + unsigned int stripe; + int chunk_number, chunk_offset; + unsigned long new_sector; + int sectors_per_chunk = raid_conf->chunk_size >> 9; + + /* First compute the information on this sector */ + + /* + * Compute the chunk number and the sector offset inside the chunk + */ + chunk_number = r_sector / sectors_per_chunk; + chunk_offset = r_sector % sectors_per_chunk; + + /* + * Compute the stripe number + */ + stripe = chunk_number / data_disks; + + /* + * Compute the data disk and parity disk indexes inside the stripe + */ + *dd_idx = chunk_number % data_disks; + + /* + * Select the parity disk based on the user selected algorithm. + */ + if (raid_conf->level == 4) + *pd_idx = data_disks; + else switch (raid_conf->algorithm) { + case ALGORITHM_LEFT_ASYMMETRIC: + *pd_idx = data_disks - stripe % raid_disks; + if (*dd_idx >= *pd_idx) + (*dd_idx)++; + break; + case ALGORITHM_RIGHT_ASYMMETRIC: + *pd_idx = stripe % raid_disks; + if (*dd_idx >= *pd_idx) + (*dd_idx)++; + break; + case ALGORITHM_LEFT_SYMMETRIC: + *pd_idx = data_disks - stripe % raid_disks; + *dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks; + break; + case ALGORITHM_RIGHT_SYMMETRIC: + *pd_idx = stripe % raid_disks; + *dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks; + break; + default: + printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm); + } + + /* + * Finally, compute the new sector number + */ + new_sector = stripe * sectors_per_chunk + chunk_offset; + +#if 0 + if ( *dd_idx > data_disks || *pd_idx > data_disks || + chunk_offset + bh->b_size / 512 > sectors_per_chunk ) + + printk ("raid5: bug: dd_idx == %d, pd_idx == %d, chunk_offset == %d\n", + *dd_idx, *pd_idx, chunk_offset); +#endif + + return new_sector; +} + +static unsigned long compute_blocknr(struct stripe_head *sh, int i) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int raid_disks = raid_conf->raid_disks, data_disks = raid_disks - 1; + unsigned long new_sector = sh->sector, check; + int sectors_per_chunk = raid_conf->chunk_size >> 9; + unsigned long stripe = new_sector / sectors_per_chunk; + int chunk_offset = new_sector % sectors_per_chunk; + int chunk_number, dummy1, dummy2, dd_idx = i; + unsigned long r_sector, blocknr; + + switch (raid_conf->algorithm) { + case ALGORITHM_LEFT_ASYMMETRIC: + case ALGORITHM_RIGHT_ASYMMETRIC: + if (i > sh->pd_idx) + i--; + break; + case ALGORITHM_LEFT_SYMMETRIC: + case ALGORITHM_RIGHT_SYMMETRIC: + if (i < sh->pd_idx) + i += raid_disks; + i -= (sh->pd_idx + 1); + break; + default: + printk ("raid5: unsupported algorithm %d\n", raid_conf->algorithm); + } + + chunk_number = stripe * data_disks + i; + r_sector = chunk_number * sectors_per_chunk + chunk_offset; + blocknr = r_sector / (sh->size >> 9); + + check = raid5_compute_sector (r_sector, raid_disks, data_disks, &dummy1, &dummy2, raid_conf); + if (check != sh->sector || dummy1 != dd_idx || dummy2 != sh->pd_idx) { + printk("compute_blocknr: map not correct\n"); + return 0; + } + return blocknr; +} + +static void xor_block(struct buffer_head *dest, struct buffer_head *source) +{ + int lines = dest->b_size / (sizeof (int)) / 8, i; + int *destp = (int *) dest->b_data, *sourcep = (int *) source->b_data; + + for (i = lines; i > 0; i--) { + *(destp + 0) ^= *(sourcep + 0); + *(destp + 1) ^= *(sourcep + 1); + *(destp + 2) ^= *(sourcep + 2); + *(destp + 3) ^= *(sourcep + 3); + *(destp + 4) ^= *(sourcep + 4); + *(destp + 5) ^= *(sourcep + 5); + *(destp + 6) ^= *(sourcep + 6); + *(destp + 7) ^= *(sourcep + 7); + destp += 8; + sourcep += 8; + } +} + +static void compute_block(struct stripe_head *sh, int dd_idx) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int i, disks = raid_conf->raid_disks; + + PRINTK(("compute_block, stripe %lu, idx %d\n", sh->sector, dd_idx)); + + if (sh->bh_old[dd_idx] == NULL) + sh->bh_old[dd_idx] = raid5_kmalloc_buffer(sh, sh->size); + raid5_build_block(sh, sh->bh_old[dd_idx], dd_idx); + + memset(sh->bh_old[dd_idx]->b_data, 0, sh->size); + for (i = 0; i < disks; i++) { + if (i == dd_idx) + continue; + if (sh->bh_old[i]) { + xor_block(sh->bh_old[dd_idx], sh->bh_old[i]); + continue; + } else + printk("compute_block() %d, stripe %lu, %d not present\n", dd_idx, sh->sector, i); + } + raid5_mark_buffer_uptodate(sh->bh_old[dd_idx], 1); +} + +static void compute_parity(struct stripe_head *sh, int method) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int i, pd_idx = sh->pd_idx, disks = raid_conf->raid_disks; + + PRINTK(("compute_parity, stripe %lu, method %d\n", sh->sector, method)); + for (i = 0; i < disks; i++) { + if (i == pd_idx || !sh->bh_new[i]) + continue; + if (!sh->bh_copy[i]) + sh->bh_copy[i] = raid5_kmalloc_buffer(sh, sh->size); + raid5_build_block(sh, sh->bh_copy[i], i); + mark_buffer_clean(sh->bh_new[i]); + memcpy(sh->bh_copy[i]->b_data, sh->bh_new[i]->b_data, sh->size); + } + if (sh->bh_copy[pd_idx] == NULL) + sh->bh_copy[pd_idx] = raid5_kmalloc_buffer(sh, sh->size); + raid5_build_block(sh, sh->bh_copy[pd_idx], sh->pd_idx); + + if (method == RECONSTRUCT_WRITE) { + memset(sh->bh_copy[pd_idx]->b_data, 0, sh->size); + for (i = 0; i < disks; i++) { + if (i == sh->pd_idx) + continue; + if (sh->bh_new[i]) { + xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]); + continue; + } + if (sh->bh_old[i]) { + xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]); + continue; + } + } + } else if (method == READ_MODIFY_WRITE) { + memcpy(sh->bh_copy[pd_idx]->b_data, sh->bh_old[pd_idx]->b_data, sh->size); + for (i = 0; i < disks; i++) { + if (i == sh->pd_idx) + continue; + if (sh->bh_new[i] && sh->bh_old[i]) { + xor_block(sh->bh_copy[pd_idx], sh->bh_copy[i]); + xor_block(sh->bh_copy[pd_idx], sh->bh_old[i]); + continue; + } + } + } + raid5_mark_buffer_uptodate(sh->bh_copy[pd_idx], 1); +} + +static void add_stripe_bh (struct stripe_head *sh, struct buffer_head *bh, int dd_idx, int rw) +{ + struct raid5_data *raid_conf = sh->raid_conf; + struct buffer_head *bh_req; + + if (sh->bh_new[dd_idx]) + printk("raid5: bug: stripe->bh_new[%d], sector %lu exists\n", dd_idx, sh->sector); + + set_bit(BH_Lock, &bh->b_state); + + bh_req = raid5_kmalloc_bh(sh); + raid5_build_block(sh, bh_req, dd_idx); + bh_req->b_data = bh->b_data; + + if (sh->phase == PHASE_COMPLETE && sh->cmd == STRIPE_NONE) { + sh->phase = PHASE_BEGIN; + sh->cmd = (rw == READ) ? STRIPE_READ : STRIPE_WRITE; + raid_conf->nr_pending_stripes++; + atomic_inc(&raid_conf->nr_handle); + } + sh->bh_new[dd_idx] = bh; + sh->bh_req[dd_idx] = bh_req; + sh->cmd_new[dd_idx] = rw; + sh->new[dd_idx] = 1; +} + +static void complete_stripe(struct stripe_head *sh) +{ + struct raid5_data *raid_conf = sh->raid_conf; + int disks = raid_conf->raid_disks; + int i, new = 0; + + PRINTK(("complete_stripe %lu\n", sh->sector)); + for (i = 0; i < disks; i++) { + if (sh->cmd == STRIPE_WRITE && i == sh->pd_idx) + raid5_update_old_bh(sh, i); + if (sh->bh_new[i]) { + if (!sh->new[i]) { +#if 0 + if (sh->cmd == STRIPE_WRITE) { + if (memcmp(sh->bh_new[i]->b_data, sh->bh_copy[i]->b_data, sh->size)) { + printk("copy differs, %s, sector %lu ", + test_bit(BH_Dirty, &sh->bh_new[i]->b_state) ? "dirty" : "clean", + sh->sector); + } else if (test_bit(BH_Dirty, &sh->bh_new[i]->b_state)) + printk("sector %lu dirty\n", sh->sector); + } +#endif + if (sh->cmd == STRIPE_WRITE) + raid5_update_old_bh(sh, i); + raid5_end_buffer_io(sh, i, 1); + continue; + } else + new++; + } + if (new && sh->cmd == STRIPE_WRITE) + printk("raid5: bug, completed STRIPE_WRITE with new == %d\n", new); + } + if (!new) + finish_stripe(sh); + else { + PRINTK(("stripe %lu, new == %d\n", sh->sector, new)); + sh->phase = PHASE_BEGIN; + } +} + +/* + * handle_stripe() is our main logic routine. Note that: + * + * 1. lock_stripe() should be used whenever we can't accept additonal + * buffers, either during short sleeping in handle_stripe() or + * during io operations. + * + * 2. We should be careful to set sh->nr_pending whenever we sleep, + * to prevent re-entry of handle_stripe() for the same sh. + * + * 3. raid_conf->failed_disks and disk->operational can be changed + * from an interrupt. This complicates things a bit, but it allows + * us to stop issuing requests for a failed drive as soon as possible. + */ +static void handle_stripe(struct stripe_head *sh) +{ + struct raid5_data *raid_conf = sh->raid_conf; + struct md_dev *mddev = raid_conf->mddev; + int minor = (int) (mddev - md_dev); + struct buffer_head *bh; + int disks = raid_conf->raid_disks; + int i, nr = 0, nr_read = 0, nr_write = 0; + int nr_cache = 0, nr_cache_other = 0, nr_cache_overwrite = 0, parity = 0; + int nr_failed_other = 0, nr_failed_overwrite = 0, parity_failed = 0; + int reading = 0, nr_writing = 0; + int method1 = INT_MAX, method2 = INT_MAX; + int block; + unsigned long flags; + int operational[MD_SB_DISKS], failed_disks = raid_conf->failed_disks; + + PRINTK(("handle_stripe(), stripe %lu\n", sh->sector)); + if (sh->nr_pending) { + printk("handle_stripe(), stripe %lu, io still pending\n", sh->sector); + return; + } + if (sh->phase == PHASE_COMPLETE) { + printk("handle_stripe(), stripe %lu, already complete\n", sh->sector); + return; + } + + atomic_dec(&raid_conf->nr_handle); + + if (test_and_clear_bit(STRIPE_ERROR, &sh->state)) { + printk("raid5: restarting stripe %lu\n", sh->sector); + sh->phase = PHASE_BEGIN; + } + + if ((sh->cmd == STRIPE_WRITE && sh->phase == PHASE_WRITE) || + (sh->cmd == STRIPE_READ && sh->phase == PHASE_READ)) { + /* + * Completed + */ + complete_stripe(sh); + if (sh->phase == PHASE_COMPLETE) + return; + } + + save_flags(flags); + cli(); + for (i = 0; i < disks; i++) { + operational[i] = raid_conf->disks[i].operational; + if (i == sh->pd_idx && raid_conf->resync_parity) + operational[i] = 0; + } + failed_disks = raid_conf->failed_disks; + restore_flags(flags); + + if (failed_disks > 1) { + for (i = 0; i < disks; i++) { + if (sh->bh_new[i]) { + raid5_end_buffer_io(sh, i, 0); + continue; + } + } + finish_stripe(sh); + return; + } + + for (i = 0; i < disks; i++) { + if (sh->bh_old[i]) + nr_cache++; + if (i == sh->pd_idx) { + if (sh->bh_old[i]) + parity = 1; + else if(!operational[i]) + parity_failed = 1; + continue; + } + if (!sh->bh_new[i]) { + if (sh->bh_old[i]) + nr_cache_other++; + else if (!operational[i]) + nr_failed_other++; + continue; + } + sh->new[i] = 0; + nr++; + if (sh->cmd_new[i] == READ) + nr_read++; + if (sh->cmd_new[i] == WRITE) + nr_write++; + if (sh->bh_old[i]) + nr_cache_overwrite++; + else if (!operational[i]) + nr_failed_overwrite++; + } + + if (nr_write && nr_read) + printk("raid5: bug, nr_write == %d, nr_read == %d, sh->cmd == %d\n", nr_write, nr_read, sh->cmd); + + if (nr_write) { + /* + * Attempt to add entries :-) + */ + if (nr_write != disks - 1) { + for (i = 0; i < disks; i++) { + if (i == sh->pd_idx) + continue; + if (sh->bh_new[i]) + continue; + block = (int) compute_blocknr(sh, i); + bh = efind_buffer(MKDEV(MD_MAJOR, minor), block, sh->size); + if (bh && bh->b_count == 0 && buffer_dirty(bh) && !buffer_locked(bh)) { + PRINTK(("Whee.. sector %lu, index %d (%d) found in the buffer cache!\n", sh->sector, i, block)); + add_stripe_bh(sh, bh, i, WRITE); + sh->new[i] = 0; + nr++; nr_write++; + if (sh->bh_old[i]) { + nr_cache_overwrite++; + nr_cache_other--; + } else if (!operational[i]) { + nr_failed_overwrite++; + nr_failed_other--; + } + } + } + } + PRINTK(("handle_stripe() -- begin writing, stripe %lu\n", sh->sector)); + /* + * Writing, need to update parity buffer. + * + * Compute the number of I/O requests in the "reconstruct + * write" and "read modify write" methods. + */ + if (!nr_failed_other) + method1 = (disks - 1) - (nr_write + nr_cache_other); + if (!nr_failed_overwrite && !parity_failed) + method2 = nr_write - nr_cache_overwrite + (1 - parity); + + if (method1 == INT_MAX && method2 == INT_MAX) + printk("raid5: bug: method1 == method2 == INT_MAX\n"); + PRINTK(("handle_stripe(), sector %lu, nr_write %d, method1 %d, method2 %d\n", sh->sector, nr_write, method1, method2)); + + if (!method1 || !method2) { + lock_stripe(sh); + sh->nr_pending++; + sh->phase = PHASE_WRITE; + compute_parity(sh, method1 <= method2 ? RECONSTRUCT_WRITE : READ_MODIFY_WRITE); + for (i = 0; i < disks; i++) { + if (!operational[i] && !raid_conf->spare && !raid_conf->resync_parity) + continue; + if (i == sh->pd_idx || sh->bh_new[i]) + nr_writing++; + } + + sh->nr_pending = nr_writing; + PRINTK(("handle_stripe() %lu, writing back %d\n", sh->sector, sh->nr_pending)); + + for (i = 0; i < disks; i++) { + if (!operational[i] && !raid_conf->spare && !raid_conf->resync_parity) + continue; + bh = sh->bh_copy[i]; + if (i != sh->pd_idx && ((bh == NULL) ^ (sh->bh_new[i] == NULL))) + printk("raid5: bug: bh == %p, bh_new[%d] == %p\n", bh, i, sh->bh_new[i]); + if (i == sh->pd_idx && !bh) + printk("raid5: bug: bh == NULL, i == pd_idx == %d\n", i); + if (bh) { + bh->b_state |= (1<b_state); + if (!operational[i] && !raid_conf->resync_parity) { + bh->b_rdev = raid_conf->spare->dev; + make_request(MAJOR(raid_conf->spare->dev), WRITE, bh); + } else + make_request(MAJOR(raid_conf->disks[i].dev), WRITE, bh); + } + } + return; + } + + lock_stripe(sh); + sh->nr_pending++; + if (method1 < method2) { + sh->write_method = RECONSTRUCT_WRITE; + for (i = 0; i < disks; i++) { + if (i == sh->pd_idx) + continue; + if (sh->bh_new[i] || sh->bh_old[i]) + continue; + sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size); + raid5_build_block(sh, sh->bh_old[i], i); + reading++; + } + } else { + sh->write_method = READ_MODIFY_WRITE; + for (i = 0; i < disks; i++) { + if (sh->bh_old[i]) + continue; + if (!sh->bh_new[i] && i != sh->pd_idx) + continue; + sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size); + raid5_build_block(sh, sh->bh_old[i], i); + reading++; + } + } + sh->phase = PHASE_READ_OLD; + sh->nr_pending = reading; + PRINTK(("handle_stripe() %lu, reading %d old buffers\n", sh->sector, sh->nr_pending)); + for (i = 0; i < disks; i++) { + if (!sh->bh_old[i]) + continue; + if (buffer_uptodate(sh->bh_old[i])) + continue; + clear_bit(BH_Lock, &sh->bh_old[i]->b_state); + make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]); + } + } else { + /* + * Reading + */ + method1 = nr_read - nr_cache_overwrite; + lock_stripe(sh); + sh->nr_pending++; + + PRINTK(("handle_stripe(), sector %lu, nr_read %d, nr_cache %d, method1 %d\n", sh->sector, nr_read, nr_cache, method1)); + if (!method1 || (method1 == 1 && nr_cache == disks - 1)) { + PRINTK(("read %lu completed from cache\n", sh->sector)); + for (i = 0; i < disks; i++) { + if (!sh->bh_new[i]) + continue; + if (!sh->bh_old[i]) + compute_block(sh, i); + memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size); + } + sh->nr_pending--; + complete_stripe(sh); + return; + } + if (nr_failed_overwrite) { + sh->phase = PHASE_READ_OLD; + sh->nr_pending = (disks - 1) - nr_cache; + PRINTK(("handle_stripe() %lu, phase READ_OLD, pending %d\n", sh->sector, sh->nr_pending)); + for (i = 0; i < disks; i++) { + if (sh->bh_old[i]) + continue; + if (!operational[i]) + continue; + sh->bh_old[i] = raid5_kmalloc_buffer(sh, sh->size); + raid5_build_block(sh, sh->bh_old[i], i); + clear_bit(BH_Lock, &sh->bh_old[i]->b_state); + make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_old[i]); + } + } else { + sh->phase = PHASE_READ; + sh->nr_pending = nr_read - nr_cache_overwrite; + PRINTK(("handle_stripe() %lu, phase READ, pending %d\n", sh->sector, sh->nr_pending)); + for (i = 0; i < disks; i++) { + if (!sh->bh_new[i]) + continue; + if (sh->bh_old[i]) { + memcpy(sh->bh_new[i]->b_data, sh->bh_old[i]->b_data, sh->size); + continue; + } + make_request(MAJOR(raid_conf->disks[i].dev), READ, sh->bh_req[i]); + } + } + } +} + +static int raid5_make_request (struct md_dev *mddev, int rw, struct buffer_head * bh) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + const unsigned int raid_disks = raid_conf->raid_disks; + const unsigned int data_disks = raid_disks - 1; + unsigned int dd_idx, pd_idx; + unsigned long new_sector; + + struct stripe_head *sh; + + if (rw == READA) rw = READ; + if (rw == WRITEA) rw = WRITE; + + new_sector = raid5_compute_sector(bh->b_rsector, raid_disks, data_disks, + &dd_idx, &pd_idx, raid_conf); + + PRINTK(("raid5_make_request, sector %lu\n", new_sector)); +repeat: + sh = get_stripe(raid_conf, new_sector, bh->b_size); + if ((rw == READ && sh->cmd == STRIPE_WRITE) || (rw == WRITE && sh->cmd == STRIPE_READ)) { + PRINTK(("raid5: lock contention, rw == %d, sh->cmd == %d\n", rw, sh->cmd)); + lock_stripe(sh); + if (!sh->nr_pending) + handle_stripe(sh); + goto repeat; + } + sh->pd_idx = pd_idx; + if (sh->phase != PHASE_COMPLETE && sh->phase != PHASE_BEGIN) + PRINTK(("stripe %lu catching the bus!\n", sh->sector)); + add_stripe_bh(sh, bh, dd_idx, rw); + + md_wakeup_thread(raid_conf->thread); + return 0; +} + +static void unplug_devices(struct stripe_head *sh) +{ +#if 0 + struct raid5_data *raid_conf = sh->raid_conf; + int i; + + for (i = 0; i < raid_conf->raid_disks; i++) + unplug_device(blk_dev + MAJOR(raid_conf->disks[i].dev)); +#endif +} + +/* + * This is our raid5 kernel thread. + * + * We scan the hash table for stripes which can be handled now. + * During the scan, completed stripes are saved for us by the interrupt + * handler, so that they will not have to wait for our next wakeup. + */ +static void raid5d (void *data) +{ + struct stripe_head *sh; + struct raid5_data *raid_conf = data; + struct md_dev *mddev = raid_conf->mddev; + int i, handled = 0, unplug = 0; + unsigned long flags; + + PRINTK(("+++ raid5d active\n")); + + if (mddev->sb_dirty) { + mddev->sb_dirty = 0; + md_update_sb((int) (mddev - md_dev)); + } + for (i = 0; i < NR_HASH; i++) { +repeat: + sh = raid_conf->stripe_hashtbl[i]; + for (; sh; sh = sh->hash_next) { + if (sh->raid_conf != raid_conf) + continue; + if (sh->phase == PHASE_COMPLETE) + continue; + if (sh->nr_pending) + continue; + if (sh->sector == raid_conf->next_sector) { + raid_conf->sector_count += (sh->size >> 9); + if (raid_conf->sector_count >= 128) + unplug = 1; + } else + unplug = 1; + if (unplug) { + PRINTK(("unplugging devices, sector == %lu, count == %d\n", sh->sector, raid_conf->sector_count)); + unplug_devices(sh); + unplug = 0; + raid_conf->sector_count = 0; + } + raid_conf->next_sector = sh->sector + (sh->size >> 9); + handled++; + handle_stripe(sh); + goto repeat; + } + } + if (raid_conf) { + PRINTK(("%d stripes handled, nr_handle %d\n", handled, atomic_read(&raid_conf->nr_handle))); + save_flags(flags); + cli(); + if (!atomic_read(&raid_conf->nr_handle)) + clear_bit(THREAD_WAKEUP, &raid_conf->thread->flags); + } + PRINTK(("--- raid5d inactive\n")); +} + +#if SUPPORT_RECONSTRUCTION +/* + * Private kernel thread for parity reconstruction after an unclean + * shutdown. Reconstruction on spare drives in case of a failed drive + * is done by the generic mdsyncd. + */ +static void raid5syncd (void *data) +{ + struct raid5_data *raid_conf = data; + struct md_dev *mddev = raid_conf->mddev; + + if (!raid_conf->resync_parity) + return; + md_do_sync(mddev); + raid_conf->resync_parity = 0; +} +#endif /* SUPPORT_RECONSTRUCTION */ + +static int __check_consistency (struct md_dev *mddev, int row) +{ + struct raid5_data *raid_conf = mddev->private; + kdev_t dev; + struct buffer_head *bh[MD_SB_DISKS], tmp; + int i, rc = 0, nr = 0; + + if (raid_conf->working_disks != raid_conf->raid_disks) + return 0; + tmp.b_size = 4096; + if ((tmp.b_data = (char *) get_free_page(GFP_KERNEL)) == NULL) + return 0; + memset(bh, 0, MD_SB_DISKS * sizeof(struct buffer_head *)); + for (i = 0; i < raid_conf->raid_disks; i++) { + dev = raid_conf->disks[i].dev; + set_blocksize(dev, 4096); + if ((bh[i] = bread(dev, row / 4, 4096)) == NULL) + break; + nr++; + } + if (nr == raid_conf->raid_disks) { + for (i = 1; i < nr; i++) + xor_block(&tmp, bh[i]); + if (memcmp(tmp.b_data, bh[0]->b_data, 4096)) + rc = 1; + } + for (i = 0; i < raid_conf->raid_disks; i++) { + dev = raid_conf->disks[i].dev; + if (bh[i]) { + bforget(bh[i]); + bh[i] = NULL; + } + fsync_dev(dev); + invalidate_buffers(dev); + } + free_page((unsigned long) tmp.b_data); + return rc; +} + +static int check_consistency (struct md_dev *mddev) +{ + int size = mddev->sb->size; + int row; + + for (row = 0; row < size; row += size / 8) + if (__check_consistency(mddev, row)) + return 1; + return 0; +} + +static int raid5_run (int minor, struct md_dev *mddev) +{ + struct raid5_data *raid_conf; + int i, j, raid_disk, memory; + md_superblock_t *sb = mddev->sb; + md_descriptor_t *descriptor; + struct real_dev *realdev; + + MOD_INC_USE_COUNT; + + if (sb->level != 5 && sb->level != 4) { + printk("raid5: %s: raid level not set to 4/5 (%d)\n", kdevname(MKDEV(MD_MAJOR, minor)), sb->level); + MOD_DEC_USE_COUNT; + return -EIO; + } + + mddev->private = kmalloc (sizeof (struct raid5_data), GFP_KERNEL); + if ((raid_conf = mddev->private) == NULL) + goto abort; + memset (raid_conf, 0, sizeof (*raid_conf)); + raid_conf->mddev = mddev; + + if ((raid_conf->stripe_hashtbl = (struct stripe_head **) __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER, 0)) == NULL) + goto abort; + memset(raid_conf->stripe_hashtbl, 0, HASH_PAGES * PAGE_SIZE); + + init_waitqueue(&raid_conf->wait_for_stripe); + PRINTK(("raid5_run(%d) called.\n", minor)); + + for (i = 0; i < mddev->nb_dev; i++) { + realdev = &mddev->devices[i]; + if (!realdev->sb) { + printk(KERN_ERR "raid5: disabled device %s (couldn't access raid superblock)\n", kdevname(realdev->dev)); + continue; + } + + /* + * This is important -- we are using the descriptor on + * the disk only to get a pointer to the descriptor on + * the main superblock, which might be more recent. + */ + descriptor = &sb->disks[realdev->sb->descriptor.number]; + if (descriptor->state & (1 << MD_FAULTY_DEVICE)) { + printk(KERN_ERR "raid5: disabled device %s (errors detected)\n", kdevname(realdev->dev)); + continue; + } + if (descriptor->state & (1 << MD_ACTIVE_DEVICE)) { + if (!(descriptor->state & (1 << MD_SYNC_DEVICE))) { + printk(KERN_ERR "raid5: disabled device %s (not in sync)\n", kdevname(realdev->dev)); + continue; + } + raid_disk = descriptor->raid_disk; + if (descriptor->number > sb->nr_disks || raid_disk > sb->raid_disks) { + printk(KERN_ERR "raid5: disabled device %s (inconsistent descriptor)\n", kdevname(realdev->dev)); + continue; + } + if (raid_conf->disks[raid_disk].operational) { + printk(KERN_ERR "raid5: disabled device %s (device %d already operational)\n", kdevname(realdev->dev), raid_disk); + continue; + } + printk(KERN_INFO "raid5: device %s operational as raid disk %d\n", kdevname(realdev->dev), raid_disk); + + raid_conf->disks[raid_disk].number = descriptor->number; + raid_conf->disks[raid_disk].raid_disk = raid_disk; + raid_conf->disks[raid_disk].dev = mddev->devices[i].dev; + raid_conf->disks[raid_disk].operational = 1; + + raid_conf->working_disks++; + } else { + /* + * Must be a spare disk .. + */ + printk(KERN_INFO "raid5: spare disk %s\n", kdevname(realdev->dev)); + raid_disk = descriptor->raid_disk; + raid_conf->disks[raid_disk].number = descriptor->number; + raid_conf->disks[raid_disk].raid_disk = raid_disk; + raid_conf->disks[raid_disk].dev = mddev->devices [i].dev; + + raid_conf->disks[raid_disk].operational = 0; + raid_conf->disks[raid_disk].write_only = 0; + raid_conf->disks[raid_disk].spare = 1; + } + } + raid_conf->raid_disks = sb->raid_disks; + raid_conf->failed_disks = raid_conf->raid_disks - raid_conf->working_disks; + raid_conf->mddev = mddev; + raid_conf->chunk_size = sb->chunk_size; + raid_conf->level = sb->level; + raid_conf->algorithm = sb->parity_algorithm; + raid_conf->max_nr_stripes = NR_STRIPES; + + if (raid_conf->working_disks != sb->raid_disks && sb->state != (1 << MD_SB_CLEAN)) { + printk(KERN_ALERT "raid5: raid set %s not clean and not all disks are operational -- run ckraid\n", kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + if (!raid_conf->chunk_size || raid_conf->chunk_size % 4) { + printk(KERN_ERR "raid5: invalid chunk size %d for %s\n", raid_conf->chunk_size, kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + if (raid_conf->algorithm > ALGORITHM_RIGHT_SYMMETRIC) { + printk(KERN_ERR "raid5: unsupported parity algorithm %d for %s\n", raid_conf->algorithm, kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + if (raid_conf->failed_disks > 1) { + printk(KERN_ERR "raid5: not enough operational devices for %s (%d/%d failed)\n", kdevname(MKDEV(MD_MAJOR, minor)), raid_conf->failed_disks, raid_conf->raid_disks); + goto abort; + } + + if ((sb->state & (1 << MD_SB_CLEAN)) && check_consistency(mddev)) { + printk(KERN_ERR "raid5: detected raid-5 xor inconsistenty -- run ckraid\n"); + sb->state |= 1 << MD_SB_ERRORS; + goto abort; + } + + if ((raid_conf->thread = md_register_thread(raid5d, raid_conf)) == NULL) { + printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } + +#if SUPPORT_RECONSTRUCTION + if ((raid_conf->resync_thread = md_register_thread(raid5syncd, raid_conf)) == NULL) { + printk(KERN_ERR "raid5: couldn't allocate thread for %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + goto abort; + } +#endif /* SUPPORT_RECONSTRUCTION */ + + memory = raid_conf->max_nr_stripes * (sizeof(struct stripe_head) + + raid_conf->raid_disks * (sizeof(struct buffer_head) + + 2 * (sizeof(struct buffer_head) + PAGE_SIZE))) / 1024; + if (grow_stripes(raid_conf, raid_conf->max_nr_stripes, GFP_KERNEL)) { + printk(KERN_ERR "raid5: couldn't allocate %dkB for buffers\n", memory); + shrink_stripes(raid_conf, raid_conf->max_nr_stripes); + goto abort; + } else + printk(KERN_INFO "raid5: allocated %dkB for %s\n", memory, kdevname(MKDEV(MD_MAJOR, minor))); + + /* + * Regenerate the "device is in sync with the raid set" bit for + * each device. + */ + for (i = 0; i < sb->nr_disks ; i++) { + sb->disks[i].state &= ~(1 << MD_SYNC_DEVICE); + for (j = 0; j < sb->raid_disks; j++) { + if (!raid_conf->disks[j].operational) + continue; + if (sb->disks[i].number == raid_conf->disks[j].number) + sb->disks[i].state |= 1 << MD_SYNC_DEVICE; + } + } + sb->active_disks = raid_conf->working_disks; + + if (sb->active_disks == sb->raid_disks) + printk("raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm); + else + printk(KERN_ALERT "raid5: raid level %d set %s active with %d out of %d devices, algorithm %d\n", raid_conf->level, kdevname(MKDEV(MD_MAJOR, minor)), sb->active_disks, sb->raid_disks, raid_conf->algorithm); + + if ((sb->state & (1 << MD_SB_CLEAN)) == 0) { + printk("raid5: raid set %s not clean; re-constructing parity\n", kdevname(MKDEV(MD_MAJOR, minor))); + raid_conf->resync_parity = 1; +#if SUPPORT_RECONSTRUCTION + md_wakeup_thread(raid_conf->resync_thread); +#endif /* SUPPORT_RECONSTRUCTION */ + } + + /* Ok, everything is just fine now */ + return (0); +abort: + if (raid_conf) { + if (raid_conf->stripe_hashtbl) + free_pages((unsigned long) raid_conf->stripe_hashtbl, HASH_PAGES_ORDER); + kfree(raid_conf); + } + mddev->private = NULL; + printk(KERN_ALERT "raid5: failed to run raid set %s\n", kdevname(MKDEV(MD_MAJOR, minor))); + MOD_DEC_USE_COUNT; + return -EIO; +} + +static int raid5_stop (int minor, struct md_dev *mddev) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + + shrink_stripe_cache(raid_conf, raid_conf->max_nr_stripes); + shrink_stripes(raid_conf, raid_conf->max_nr_stripes); + md_unregister_thread(raid_conf->thread); +#if SUPPORT_RECONSTRUCTION + md_unregister_thread(raid_conf->resync_thread); +#endif /* SUPPORT_RECONSTRUCTION */ + free_pages((unsigned long) raid_conf->stripe_hashtbl, HASH_PAGES_ORDER); + kfree(raid_conf); + mddev->private = NULL; + MOD_DEC_USE_COUNT; + return 0; +} + +static int raid5_status (char *page, int minor, struct md_dev *mddev) +{ + struct raid5_data *raid_conf = (struct raid5_data *) mddev->private; + md_superblock_t *sb = mddev->sb; + int sz = 0, i; + + sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", sb->level, sb->chunk_size >> 10, sb->parity_algorithm); + sz += sprintf (page+sz, " [%d/%d] [", raid_conf->raid_disks, raid_conf->working_disks); + for (i = 0; i < raid_conf->raid_disks; i++) + sz += sprintf (page+sz, "%s", raid_conf->disks[i].operational ? "U" : "_"); + sz += sprintf (page+sz, "]"); + return sz; +} + +static int raid5_mark_spare(struct md_dev *mddev, md_descriptor_t *spare, int state) +{ + int i = 0, failed_disk = -1; + struct raid5_data *raid_conf = mddev->private; + struct disk_info *disk = raid_conf->disks; + unsigned long flags; + md_superblock_t *sb = mddev->sb; + md_descriptor_t *descriptor; + + for (i = 0; i < MD_SB_DISKS; i++, disk++) { + if (disk->spare && disk->number == spare->number) + goto found; + } + return 1; +found: + for (i = 0, disk = raid_conf->disks; i < raid_conf->raid_disks; i++, disk++) + if (!disk->operational) + failed_disk = i; + if (failed_disk == -1) + return 1; + save_flags(flags); + cli(); + switch (state) { + case SPARE_WRITE: + disk->operational = 1; + disk->write_only = 1; + raid_conf->spare = disk; + break; + case SPARE_INACTIVE: + disk->operational = 0; + disk->write_only = 0; + raid_conf->spare = NULL; + break; + case SPARE_ACTIVE: + disk->spare = 0; + disk->write_only = 0; + + descriptor = &sb->disks[raid_conf->disks[failed_disk].number]; + i = spare->raid_disk; + disk->raid_disk = spare->raid_disk = descriptor->raid_disk; + if (disk->raid_disk != failed_disk) + printk("raid5: disk->raid_disk != failed_disk"); + descriptor->raid_disk = i; + + raid_conf->spare = NULL; + raid_conf->working_disks++; + raid_conf->failed_disks--; + raid_conf->disks[failed_disk] = *disk; + break; + default: + printk("raid5_mark_spare: bug: state == %d\n", state); + restore_flags(flags); + return 1; + } + restore_flags(flags); + return 0; +} + +static struct md_personality raid5_personality= +{ + "raid5", + raid5_map, + raid5_make_request, + raid5_end_request, + raid5_run, + raid5_stop, + raid5_status, + NULL, /* no ioctls */ + 0, + raid5_error, + /* raid5_hot_add_disk, */ NULL, + /* raid1_hot_remove_drive */ NULL, + raid5_mark_spare +}; + +int raid5_init (void) +{ + return register_md_personality (RAID5, &raid5_personality); +} + +#ifdef MODULE +int init_module (void) +{ + return raid5_init(); +} + +void cleanup_module (void) +{ + unregister_md_personality (RAID5); +} +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.1.62/linux/drivers/char/Config.in Thu Sep 11 09:02:23 1997 +++ linux/drivers/char/Config.in Tue Nov 4 10:23:25 1997 @@ -109,4 +109,11 @@ fi tristate '/dev/nvram support' CONFIG_NVRAM tristate 'PC joystick support' CONFIG_JOYSTICK +bool 'Radio Device Support' CONFIG_MISC_RADIO +if [ "$CONFIG_MISC_RADIO" != "n" ]; then + bool ' AIMSlab RadioTrack (aka RadioReveal) support' CONFIG_RADIO_RTRACK + if [ "$CONFIG_RADIO_RTRACK" != "n" ]; then + hex ' RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 0x20f + fi +fi endmenu diff -u --recursive --new-file v2.1.62/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.1.62/linux/drivers/char/Makefile Sun Sep 7 13:10:42 1997 +++ linux/drivers/char/Makefile Tue Nov 4 10:23:25 1997 @@ -281,6 +281,13 @@ endif endif +ifeq ($(CONFIG_MISC_RADIO),y) +L_OBJS += radio.o + ifeq ($(CONFIG_RADIO_RTRACK),y) + L_OBJS += rtrack.o + endif +endif + ifeq ($(CONFIG_QIC02_TAPE),y) L_OBJS += tpqic02.o else diff -u --recursive --new-file v2.1.62/linux/drivers/char/README.cycladesZ linux/drivers/char/README.cycladesZ --- v2.1.62/linux/drivers/char/README.cycladesZ Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/README.cycladesZ Tue Nov 11 11:04:15 1997 @@ -0,0 +1,8 @@ + +The Cyclades-Z must have firmware loaded onto the card before it will +operate. This operation should be performed during system startup, + +The firmware, loader program and the latest device driver code are +available from Cyclades at + ftp://ftp.cyclades.com/pub/cyclades/cyclades-z/linux/ + diff -u --recursive --new-file v2.1.62/linux/drivers/char/busmouse.c linux/drivers/char/busmouse.c --- v2.1.62/linux/drivers/char/busmouse.c Tue Sep 23 16:48:47 1997 +++ linux/drivers/char/busmouse.c Mon Nov 3 15:14:20 1997 @@ -159,8 +159,8 @@ * writes are disallowed */ -static long write_mouse(struct inode * inode, struct file * file, - const char * buffer, unsigned long count) +static ssize_t write_mouse(struct file * file, + const char * buffer, size_t count, loff_t *ppos) { return -EINVAL; } @@ -169,8 +169,8 @@ * read mouse data. Currently never blocks. */ -static long read_mouse(struct inode * inode, struct file * file, - char * buffer, unsigned long count) +static ssize_t read_mouse(struct file * file, + char * buffer, size_t count, loff_t *ppos) { int r; int dx; diff -u --recursive --new-file v2.1.62/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c --- v2.1.62/linux/drivers/char/cyclades.c Wed Sep 24 20:05:46 1997 +++ linux/drivers/char/cyclades.c Tue Nov 11 11:04:15 1997 @@ -1,6 +1,6 @@ #define BLOCKMOVE static char rcsid[] = -"$Revision: 1.36.4.33 $$Date: 1997/06/27 19:00:00 $"; +"$Revision: 2.1 $$Date: 1997/11/01 17:42:41 $"; /* * linux/drivers/char/cyclades.c @@ -8,8 +8,9 @@ * This file contains the driver for the Cyclades Cyclom-Y multiport * serial boards. * - * Maintained by Marcio Saito (marcio@cyclades.com) and - * Randolph Bentson (bentson@grieg.seaslug.org) + * Maintained by Ivan Passos (ivan@cyclades.com), + * Marcio Saito (marcio@cyclades.com) and + * Randolph Bentson (bentson@grieg.seaslug.org). * * For Technical support and installation problems, please send e-mail * to support@cyclades.com. @@ -29,6 +30,18 @@ * void cleanup_module(void); * * $Log: cyclades.c,v $ + * Revision 2.1 1997/11/01 17:42:41 ivan + * Changes in the driver to support Alpha systems (except 8Zo V_1); + * BREAK fix for the Cyclades-Z boards; + * driver inactivity control by FW implemented; + * introduction of flag that allows driver to take advantage of + * a special CD1400 feature related to HW flow control; + * added support for the CD1400 rev. J (Cyclom-Y boards); + * introduction of ioctls to: + * - control the rtsdtr_inv flag (Cyclom-Y); + * - control the rflow flag (Cyclom-Y); + * - adjust the polling interval (Cyclades-Z); + * * Revision 1.36.4.33 1997/06/27 19:00:00 ivan * Fixes related to kernel version conditional * compilation. @@ -435,7 +448,6 @@ constant in the definition below. No other change is necessary to support more boards/ports. */ -//#define NR_PORTS 64 #define NR_PORTS 128 #define ZE_V1_NPORTS 64 @@ -463,18 +475,24 @@ #define cy_min(a,b) (((a)<(b))?(a):(b)) +#if 0 +/******** + * For the next two macros, it is assumed that the buffer size is a + * power of 2 + ********/ + #define CHARS_IN_BUF(buf_ctrl) \ - ((buf_ctrl->rx_put - \ - buf_ctrl->rx_get + \ - buf_ctrl->rx_bufsize) % \ - buf_ctrl->rx_bufsize) + ((cy_readl(&buf_ctrl->rx_put) - \ + cy_readl(&buf_ctrl->rx_get) + \ + cy_readl(&buf_ctrl->rx_bufsize)) & \ + (cy_readl(&buf_ctrl->rx_bufsize) - 1)) #define SPACE_IN_BUF(buf_ctrl) \ - ((buf_ctrl->tx_get - \ - buf_ctrl->tx_put + \ - buf_ctrl->tx_bufsize - 1) % \ - buf_ctrl->tx_bufsize) - + ((cy_readl(&buf_ctrl->tx_get) - \ + cy_readl(&buf_ctrl->tx_put) + \ + cy_readl(&buf_ctrl->tx_bufsize) - 1) & \ + (cy_readl(&buf_ctrl->tx_bufsize) - 1)) +#endif #include @@ -540,13 +558,14 @@ #define IS_CYC_Z(card) ((card).num_chips == 1) #define Z_FPGA_CHECK(card) \ - ((((struct RUNTIME_9060 *)((card).ctl_addr))->init_ctrl&(1<<17))!=0) + ((cy_readl(&((struct RUNTIME_9060 *) \ + ((card).ctl_addr))->init_ctrl) & (1<<17)) != 0) -#define ISZLOADED(card) (((ZO_V1==((struct RUNTIME_9060 *) \ - ((card).ctl_addr))->mail_box_0) || \ +#define ISZLOADED(card) (((ZO_V1==cy_readl(&((struct RUNTIME_9060 *) \ + ((card).ctl_addr))->mail_box_0)) || \ Z_FPGA_CHECK(card)) && \ - (ZFIRM_ID==((struct FIRM_ID *) \ - ((card).base_addr+ID_ADDRESS))->signature)) + (ZFIRM_ID==cy_readl(&((struct FIRM_ID *) \ + ((card).base_addr+ID_ADDRESS))->signature))) #define WAKEUP_CHARS (SERIAL_XMIT_SIZE-256) @@ -563,7 +582,7 @@ static volatile int cy_irq_triggered; static volatile int cy_triggered; static int cy_wild_int_mask; -static unsigned char *intr_base_addr; +static volatile ucchar *intr_base_addr; /* This is the address lookup table. The driver will probe for @@ -632,26 +651,55 @@ * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, * HI VHI + * 20 */ static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800,115200,150000, - 0}; + 230400, 0}; -static char baud_co[] = { /* 25 MHz clock option table */ +static char baud_co_25[] = { /* 25 MHz clock option table */ /* value => 00 01 02 03 04 */ /* divide by 8 32 128 512 2048 */ 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -static char baud_bpr[] = { /* 25 MHz baud rate period table */ +static char baud_bpr_25[] = { /* 25 MHz baud rate period table */ 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15}; +static char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ + /* value => 00 01 02 03 04 */ + /* divide by 8 32 128 512 2048 */ + 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, + 0x03, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00}; + +static char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ + 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, + 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, + 0x21}; + static char baud_cor3[] = { /* receive threshold */ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07}; + 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, + 0x07}; +/* + * The Cyclades driver implements HW flow control as any serial driver. + * The cyclades_port structure member rflow and the vector rflow_thr + * allows us to take advantage of a special feature in the CD1400 to avoid + * data loss even when the system interrupt latency is too high. These flags + * are to be used only with very special applications. Setting these flags + * requires the use of a special cable (DTR and RTS reversed). In the new + * CD1400-based boards (rev. 6.00 or later), there is no need for special + * cables. + */ + +static char rflow_thr[] = { /* rflow threshold */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a}; /* The Cyclom-Ye has placed the sequential chips in non-sequential * address order. This look-up table overcomes that problem. @@ -689,6 +737,8 @@ static void show_status(int); #endif +/* The Cyclades-Z polling cycle is defined by this variable */ +static long cyz_polling_cycle = CZ_DEF_POLL; static int cyz_timeron = 0; static struct timer_list @@ -696,13 +746,12 @@ NULL, NULL, 0, 0, cyz_poll }; - /************************************************** error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long)); -memcpy_tofs (to, from, count); +copy_to_user (to, from, count); *************************************************************** error = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long *)); -memcpy_fromfs(to, from, count); +copy_from_user(to, from, count); **************************************************/ @@ -873,7 +922,7 @@ didn't finish within the time limit. */ static int -cyy_issue_cmd(u_char *base_addr, u_char cmd, int index) +cyy_issue_cmd(volatile ucchar *base_addr, u_char cmd, int index) { unsigned long flags; volatile int i; @@ -881,7 +930,7 @@ save_flags(flags); cli(); /* Check to see that the previous command has completed */ for(i = 0 ; i < 100 ; i++){ - if (base_addr[CyCCR<= jiffies) ; cy_triggered = 0; /* Reset after letting things settle */ - timeout = jiffies+10; + timeout = jiffies+(HZ/10); while (timeout >= jiffies) ; @@ -986,11 +1036,11 @@ * fool-proof, but it works a large part of the time. */ static int -get_auto_irq(unsigned char *address) +get_auto_irq(volatile ucchar *address) { - unsigned long timeout; - unsigned char *base_addr; - int index; + unsigned long timeout; + volatile ucchar *base_addr; + int index; index = 0; /* IRQ probing is only for ISA */ base_addr = address; @@ -1001,13 +1051,14 @@ */ cy_irq_triggered = 0; cli(); - base_addr[CyCAR<= jiffies) { if (cy_irq_triggered) break; @@ -1021,7 +1072,7 @@ * faked out by random interrupts */ static int -do_auto_irq(unsigned char *address) +do_auto_irq(volatile ucchar *address) { int irq_lines = 0; int irq_try_1 = 0, irq_try_2 = 0; @@ -1065,27 +1116,28 @@ int index = 0; /* probing interrupts is only for ISA */ if (!probe_ready) { - *(intr_base_addr + (Cy_ClrIntr<base_addr; index = cinfo->bus_index; + /* This loop checks all chips in the card. Make a note whenever _any_ chip had some work to do, as this is considered an indication that there will be more to do. Only when no chip @@ -1131,7 +1187,7 @@ base_addr = (unsigned char *) (cinfo->base_addr + (cy_chip_offset[chip]<first_line; info = &cy_port[i]; info->last_active = jiffies; - save_car = base_addr[CyCAR<tty == 0){ - j = (base_addr[CyRIVR<tty; - j = (base_addr[CyRIVR<ignore_status_mask){ continue; } @@ -1177,7 +1236,7 @@ *tty->flip.flag_buf_ptr++ = TTY_BREAK; *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flags & ASYNC_SAK){ do_SAK(tty); } @@ -1185,12 +1244,12 @@ *tty->flip.flag_buf_ptr++ = TTY_FRAME; *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = TTY_PARITY; *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.flag_buf_ptr++ = TTY_OVERRUN; @@ -1204,8 +1263,8 @@ tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_NORMAL; - *tty->flip.char_buf_ptr++ = - base_addr[CyRDSR<flip.char_buf_ptr++ = + cy_readb(base_addr+(CyRDSR<mon.int_count; @@ -1240,7 +1299,7 @@ break; } tty->flip.count++; - data = base_addr[CyRDSR<flip.flag_buf_ptr++ = TTY_NORMAL; *tty->flip.char_buf_ptr++ = data; #ifdef CYCLOM_16Y_HACK @@ -1251,8 +1310,8 @@ queue_task(&tty->flip.tqueue, &tq_timer); } /* end of service */ - base_addr[CyRIR<first_line; - save_car = base_addr[CyCAR<last_active = jiffies; if(info->tty == 0){ - base_addr[CySRER<x_char) { /* send special char */ outch = info->x_char; - base_addr[CyTDR<x_char = 0; } @@ -1300,29 +1364,44 @@ PPR runs at 200 Hz, so the delay is duration * 200/HZ, and thus a break can run from 1/100 sec to about 5/4 sec. + For CD1400 J or later, replace the 200 Hz + by 500 Hz. */ - base_addr[CyTDR<x_break*200/HZ; - base_addr[CyTDR<= 0x48 ) { + /* It is a CD1400 rev. J or later */ + cy_writeb((u_long)base_addr + (CyTDR<x_break*500/HZ); + } else { + cy_writeb((u_long)base_addr + (CyTDR<x_break*200/HZ); + } + /* finish break */ + cy_writeb((u_long)base_addr + (CyTDR<x_break = 0; } while (char_count-- > 0){ if (!info->xmit_cnt){ - base_addr[CySRER<xmit_buf == 0){ - base_addr[CySRER<tty->stopped || info->tty->hw_stopped){ - base_addr[CySRER<xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR< 1){ info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) - & (PAGE_SIZE - 1); - base_addr[CyTDR<first_line]; info->last_active = jiffies; - save_car = base_addr[CyCAR<tty == 0){/* no place for data, ignore it*/ ; @@ -1403,7 +1484,9 @@ /* cy_start isn't used because... !!! */ info->tty->hw_stopped = 0; - base_addr[CySRER<tty->hw_stopped = 1; - base_addr[CySRER<base_addr + firm_id->zfwctrl_addr); + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); board_ctrl = &zfw_ctrl->board_ctrl; - loc_doorbell = ((struct RUNTIME_9060 *) - (cinfo->ctl_addr))->loc_doorbell; + loc_doorbell = cy_readl(&((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->loc_doorbell); if (loc_doorbell){ *cmd = (char)(0xff & loc_doorbell); - *channel = board_ctrl->fwcmd_channel; - *param = board_ctrl->fwcmd_param; - ((struct RUNTIME_9060 *) - (cinfo->ctl_addr))->loc_doorbell = 0xffffffff; + *channel = cy_readl(&board_ctrl->fwcmd_channel); + *param = (uclong)cy_readl(&board_ctrl->fwcmd_param); + cy_writel(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->loc_doorbell, + 0xffffffff); return 1; } return 0; @@ -1475,12 +1560,12 @@ static int cyz_issue_cmd( struct cyclades_card *cinfo, - u_long channel, u_char cmd, u_long *param) + uclong channel, ucchar cmd, uclong param) { struct FIRM_ID *firm_id; struct ZFW_CTRL *zfw_ctrl; struct BOARD_CTRL *board_ctrl; - volatile unsigned long *pci_doorbell; + volatile uclong *pci_doorbell; int index; firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); @@ -1488,21 +1573,21 @@ return (-1); } zfw_ctrl = (struct ZFW_CTRL *) - (cinfo->base_addr + firm_id->zfwctrl_addr); + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); board_ctrl = &zfw_ctrl->board_ctrl; index = 0; - pci_doorbell = &((struct RUNTIME_9060 *) - (cinfo->ctl_addr))->pci_doorbell; - while( (*pci_doorbell & 0xff) != 0){ + pci_doorbell = (uclong *)(&((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->pci_doorbell); + while( (cy_readl(pci_doorbell) & 0xff) != 0){ if (index++ == 1000){ return(-1); } udelay(50L); } - board_ctrl->hcmd_channel = channel; - board_ctrl->hcmd_param = param; - *pci_doorbell = (long)cmd; + cy_writel((u_long)&board_ctrl->hcmd_channel, channel); + cy_writel((u_long)&board_ctrl->hcmd_param , param); + cy_writel((u_long)pci_doorbell, (long)cmd); return(0); } /* cyz_issue_cmd */ @@ -1521,11 +1606,11 @@ if (!ISZLOADED(*cinfo)){ return (-1); } - zfw_ctrl = - (struct ZFW_CTRL *)(cinfo->base_addr + firm_id->zfwctrl_addr); + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); ch_ctrl = zfw_ctrl->ch_ctrl; - ch_ctrl[channel].op_mode = (long)mode; + cy_writel(&ch_ctrl[channel].op_mode, (uclong)mode); return cyz_issue_cmd(cinfo, channel, cmd, 0L); @@ -1542,42 +1627,53 @@ static void cyz_poll(unsigned long arg) { - struct FIRM_ID *firm_id; - struct ZFW_CTRL *zfw_ctrl; - struct BOARD_CTRL *board_ctrl; - struct CH_CTRL *ch_ctrl; - struct BUF_CTRL *buf_ctrl; + static volatile struct FIRM_ID *firm_id; + static volatile struct ZFW_CTRL *zfw_ctrl; + static volatile struct BOARD_CTRL *board_ctrl; + static volatile struct CH_CTRL *ch_ctrl; + static volatile struct BUF_CTRL *buf_ctrl; struct cyclades_card *cinfo; struct cyclades_port *info; struct tty_struct *tty; int card, port; - int char_count, small_count; + int char_count; +#ifdef BLOCKMOVE + int small_count; +#endif char data; - u_long channel; - u_char cmd; - u_long *param; - u_long hw_ver, fw_ver; - - cyz_timerlist.expires = jiffies + 100; + uclong channel; + ucchar cmd; + uclong param; + uclong hw_ver, fw_ver; + volatile uclong tx_put, tx_get, tx_bufsize; + volatile uclong rx_put, rx_get, rx_bufsize; + cyz_timerlist.expires = jiffies + (HZ); for (card = 0 ; card < NR_CARDS ; card++){ cinfo = &cy_card[card]; if (!IS_CYC_Z(*cinfo)) continue; - firm_id = (struct FIRM_ID *) - (cinfo->base_addr + ID_ADDRESS); - if (!ISZLOADED(*cinfo)){ + + firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS); + if (!ISZLOADED(*cinfo)) { + cinfo->inact_ctrl = 0; continue; } - zfw_ctrl = - (struct ZFW_CTRL *) - (cinfo->base_addr + firm_id->zfwctrl_addr); - board_ctrl = &zfw_ctrl->board_ctrl; - fw_ver = board_ctrl->fw_version; - hw_ver = ((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0; + zfw_ctrl = (struct ZFW_CTRL *) + (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &(zfw_ctrl->board_ctrl); + fw_ver = cy_readl(&board_ctrl->fw_version); + hw_ver = cy_readl(&((struct RUNTIME_9060 *) + (cinfo->ctl_addr))->mail_box_0); + + /* Enables the firmware inactivity control */ + if ((fw_ver > 0x00000310L) && (!cinfo->inact_ctrl)) { + param = cyz_issue_cmd( &cy_card[card], 0L, C_CM_TINACT, 0L); + cinfo->inact_ctrl = 1; + } - while( cyz_fetch_msg( cinfo, &channel, &cmd, ¶m) == 1){ + while(cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1){ char_count = 0; info = &cy_port[ channel + cinfo->first_line ]; if((tty = info->tty) == 0) continue; @@ -1606,9 +1702,9 @@ break; case C_CM_MDCD: if (info->flags & ASYNC_CHECK_CD){ - if (((hw_ver != 0 || fw_ver > 241) - ? ((u_long)param) - : ch_ctrl[channel].rs_status) & C_RS_DCD) { + if ((fw_ver > 241 ? + ((u_long)param) : + cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) { /* SP("Open Wakeup\n"); */ cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); @@ -1625,7 +1721,7 @@ case C_CM_MCTS: if (info->flags & ASYNC_CTS_FLOW) { if(info->tty->hw_stopped){ - if( ch_ctrl[channel].rs_status & C_RS_DCD){ + if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){ /* cy_start isn't used because... HW flow is handled by the board */ /* SP("Write Wakeup\n"); */ @@ -1633,7 +1729,7 @@ Cy_EVENT_WRITE_WAKEUP); } }else{ - if(!(ch_ctrl[channel].rs_status & C_RS_CTS)){ + if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){ /* cy_stop isn't used because HW flow is handled by the board */ /* SP("Write stop\n"); */ @@ -1653,14 +1749,25 @@ queue_task(&tty->flip.tqueue, &tq_timer); } } - - for (port = 0; port < board_ctrl->n_channel; port++){ + for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){ info = &cy_port[ port + cinfo->first_line ]; tty = info->tty; ch_ctrl = &(zfw_ctrl->ch_ctrl[port]); buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); - if ((char_count = CHARS_IN_BUF(buf_ctrl))){ +/* Removed due to compilation problems in Alpha systems */ +// if ((char_count = CHARS_IN_BUF(buf_ctrl))){ + + rx_get = cy_readl(&buf_ctrl->rx_get); + rx_put = cy_readl(&buf_ctrl->rx_put); + rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize); + if (rx_put >= rx_get) + char_count = rx_put - rx_get; + else + char_count = rx_put - rx_get + rx_bufsize; + + if ( char_count ){ + info->last_active = jiffies; info->jiffies[1] = jiffies; @@ -1673,9 +1780,7 @@ #endif if( tty == 0){ /* flush received characters */ - buf_ctrl->rx_get = - (buf_ctrl->rx_get + char_count) - % buf_ctrl->rx_bufsize; + rx_get = (rx_get + char_count) & (rx_bufsize - 1); /* SP("-"); */ info->rflush_count++; }else{ @@ -1684,22 +1789,22 @@ for performance, but because of buffer boundaries, there may be several steps to the operation */ while(0 < (small_count - = cy_min( (buf_ctrl->rx_bufsize - buf_ctrl->rx_get), - cy_min( (TTY_FLIPBUF_SIZE - tty->flip.count), - char_count)))){ - memcpy(tty->flip.char_buf_ptr, - (char *)(cinfo->base_addr - + buf_ctrl->rx_bufaddr - + buf_ctrl->rx_get), - small_count); + = cy_min((rx_bufsize - rx_get), + cy_min((TTY_FLIPBUF_SIZE - tty->flip.count), + char_count)))){ + + memcpy_fromio(tty->flip.char_buf_ptr, + (char *)(cinfo->base_addr + + cy_readl(&buf_ctrl->rx_bufaddr) + + rx_get), + small_count); + tty->flip.char_buf_ptr += small_count; memset(tty->flip.flag_buf_ptr, TTY_NORMAL, small_count); tty->flip.flag_buf_ptr += small_count; - buf_ctrl->rx_get = - (buf_ctrl->rx_get + small_count) - % buf_ctrl->rx_bufsize; + rx_get = (rx_get + small_count) & (rx_bufsize - 1); char_count -= small_count; tty->flip.count += small_count; } @@ -1708,13 +1813,9 @@ if (tty->flip.count >= TTY_FLIPBUF_SIZE){ break; } - data = *(char *) (cinfo->base_addr + - buf_ctrl->rx_bufaddr + - buf_ctrl->rx_get); - buf_ctrl->rx_get = - (buf_ctrl->rx_get + 1) - % buf_ctrl->rx_bufsize; - + data = cy_readb(cinfo->base_addr + + cy_readl(&buf_ctrl->rx_bufaddr) + rx_get); + rx_get = (rx_get + 1) & (rx_bufsize - 1); tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_NORMAL; *tty->flip.char_buf_ptr++ = data; @@ -1722,22 +1823,33 @@ #endif queue_task(&tty->flip.tqueue, &tq_timer); } + /* Update rx_get */ + cy_writel(&buf_ctrl->rx_get, rx_get); } - if ((char_count = SPACE_IN_BUF(buf_ctrl))){ - if( tty == 0){ +/* Removed due to compilation problems in Alpha systems */ +// if ((char_count = SPACE_IN_BUF(buf_ctrl))){ + + tx_get = cy_readl(&buf_ctrl->tx_get); + tx_put = cy_readl(&buf_ctrl->tx_put); + tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize); + if (tx_put >= tx_get) + char_count = tx_get - tx_put - 1 + tx_bufsize; + else + char_count = tx_get - tx_put - 1; + + if ( char_count ){ + + if( tty == 0 ){ goto ztxdone; } if(info->x_char) { /* send special char */ data = info->x_char; - *(char *) (cinfo->base_addr + - buf_ctrl->tx_bufaddr + - buf_ctrl->tx_put) = data; - buf_ctrl->tx_put = - (buf_ctrl->tx_put + 1) - % buf_ctrl->tx_bufsize; + cy_writeb((cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); info->x_char = 0; char_count--; info->last_active = jiffies; @@ -1751,21 +1863,20 @@ } #ifdef BLOCKMOVE while(0 < (small_count - = cy_min( (buf_ctrl->tx_bufsize - buf_ctrl->tx_put), - cy_min ( (PAGE_SIZE - info->xmit_tail), - cy_min( info->xmit_cnt, char_count))))){ - memcpy((char *)(cinfo->base_addr - + buf_ctrl->tx_bufaddr - + buf_ctrl->tx_put), - &info->xmit_buf[info->xmit_tail], - small_count); - buf_ctrl->tx_put = - (buf_ctrl->tx_put + small_count) - % buf_ctrl->tx_bufsize; + = cy_min((tx_bufsize - tx_put), + cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail), + cy_min(info->xmit_cnt, char_count))))){ + + memcpy_toio((char *)(cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), + &info->xmit_buf[info->xmit_tail], + small_count); + + tx_put = (tx_put + small_count) & (tx_bufsize - 1); char_count -= small_count; info->xmit_cnt -= small_count; info->xmit_tail = - (info->xmit_tail + small_count) & (PAGE_SIZE - 1); + (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1); info->last_active = jiffies; info->jiffies[2] = jiffies; } @@ -1774,28 +1885,33 @@ data = info->xmit_buf[info->xmit_tail]; info->xmit_cnt--; info->xmit_tail = - (info->xmit_tail + 1) & (PAGE_SIZE - 1); + (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); - *(char *) (cinfo->base_addr + - buf_ctrl->tx_bufaddr + - buf_ctrl->tx_put) = data; - buf_ctrl->tx_put = - (buf_ctrl->tx_put + 1) - % buf_ctrl->tx_bufsize; + cy_writeb(cinfo->base_addr + + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, + data); + tx_put = (tx_put + 1) & (tx_bufsize - 1); char_count--; info->last_active = jiffies; info->jiffies[2] = jiffies; } + #endif ztxdone: if (info->xmit_cnt < WAKEUP_CHARS) { cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } + /* Update tx_put */ + cy_writel(&buf_ctrl->tx_put, tx_put); } } - /* poll every 40 ms */ - cyz_timerlist.expires = jiffies + 4; + cyz_timerlist.expires = jiffies + cyz_polling_cycle; + + /* refresh inactivity counter */ + if (cinfo->inact_ctrl) { + cy_writel(&board_ctrl->inactivity, (uclong) ZF_TINACT); + } } add_timer(&cyz_timerlist); @@ -1851,26 +1967,27 @@ #endif save_flags(flags); cli(); - base_addr[CyCAR<default_timeout + cy_writeb((ulong)base_addr+(CyRTPR<default_timeout ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + : 0x02)); /* 10ms rx timeout */ cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index); - base_addr[CyCAR<flags |= ASYNC_INITIALIZED; if (info->tty){ @@ -1894,7 +2011,7 @@ zfw_ctrl = (struct ZFW_CTRL *) - (cy_card[card].base_addr + firm_id->zfwctrl_addr); + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); board_ctrl = &zfw_ctrl->board_ctrl; ch_ctrl = zfw_ctrl->ch_ctrl; @@ -1903,8 +2020,8 @@ card, channel, (long)base_addr);/**/ #endif - ch_ctrl[channel].op_mode = C_CH_ENABLE; - ch_ctrl[channel].intr_enable = C_IN_MDCD|C_IN_MCTS; + cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE); + cy_writel(&ch_ctrl[channel].intr_enable, C_IN_MDCD|C_IN_MCTS); retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTL, 0L); /* was C_CM_RESET */ if (retval != 0){ @@ -1913,8 +2030,8 @@ /* set timeout !!! */ /* set RTS and DTR !!! */ - ch_ctrl[channel].rs_control |= - C_RS_RTS | C_RS_DTR ; + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR) ; retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLM, 0L); if (retval != 0){ @@ -1959,8 +2076,9 @@ + (cy_chip_offset[chip]<tty || (info->tty->termios->c_cflag & HUPCL)) { - base_addr[CyMSVR1<zfwctrl_addr); - board_ctrl = &zfw_ctrl->board_ctrl; + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); + board_ctrl = &(zfw_ctrl->board_ctrl); ch_ctrl = zfw_ctrl->ch_ctrl; + save_flags(flags); cli(); if (info->xmit_buf){ unsigned char * temp; @@ -2063,8 +2183,9 @@ free_page((unsigned long) temp); } if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { - ch_ctrl[channel].rs_control &= - ~(C_RS_RTS | C_RS_DTR ); + cy_writel((u_long)&ch_ctrl[channel].rs_control, + (uclong)(cy_readl(&ch_ctrl[channel].rs_control) & + ~(C_RS_RTS | C_RS_DTR))); retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLM, 0L); if (retval != 0){ @@ -2187,13 +2308,14 @@ while (1) { save_flags(flags); cli(); if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){ - base_addr[CyCAR<flags & ASYNC_CALLOUT_ACTIVE) && !(info->flags & ASYNC_CLOSING) && (C_CLOCAL(tty) - || (base_addr[CyMSVR1<signal & ~current->blocked) { retval = -ERESTARTSYS; break; } @@ -2243,13 +2365,13 @@ zfw_ctrl = (struct ZFW_CTRL *) - (base_addr + firm_id->zfwctrl_addr); + (base_addr + cy_readl(&firm_id->zfwctrl_addr)); board_ctrl = &zfw_ctrl->board_ctrl; ch_ctrl = zfw_ctrl->ch_ctrl; while (1) { - ch_ctrl[channel].rs_control |= - C_RS_RTS | C_RS_DTR ; + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | C_RS_DTR); retval = cyz_issue_cmd(&cy_card[info->card], channel, C_CM_IOCTLM, 0L); if (retval != 0){ @@ -2272,10 +2394,10 @@ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && !(info->flags & ASYNC_CLOSING) && (C_CLOCAL(tty) - || (ch_ctrl[channel].rs_status & C_RS_DCD))) { + || (cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) { break; } - if (signal_pending(current)) { + if (current->signal & ~current->blocked) { retval = -ERESTARTSYS; break; } @@ -2332,11 +2454,11 @@ */ if (IS_CYC_Z(cy_card[info->card])) { if (!ISZLOADED(cy_card[info->card])) { - if (((ZE_V1 ==((struct RUNTIME_9060 *) - ((cy_card[info->card]).ctl_addr))->mail_box_0) && + if (((ZE_V1 ==cy_readl(&((struct RUNTIME_9060 *) + ((cy_card[info->card]).ctl_addr))->mail_box_0)) && Z_FPGA_CHECK(cy_card[info->card])) && - (ZFIRM_HLT==((struct FIRM_ID *) - ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature)) + (ZFIRM_HLT==cy_readl(&((struct FIRM_ID *) + ((cy_card[info->card]).base_addr+ID_ADDRESS))->signature))) { printk ("Cyclades-Z Error: you need an external power supply for this number of ports.\n\rFirmware halted.\r\n"); } else { @@ -2403,6 +2525,7 @@ #ifdef SERIAL_DEBUG_OPEN printk(" cyc:cy_open done\n");/**/ #endif + return 0; } /* cy_open */ @@ -2603,13 +2726,13 @@ return; save_flags(flags); cli(); - if (info->xmit_cnt >= PAGE_SIZE - 1) { + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { restore_flags(flags); return; } info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= PAGE_SIZE - 1; + info->xmit_head &= SERIAL_XMIT_SIZE - 1; info->xmit_cnt++; restore_flags(flags); #if 0 @@ -2652,8 +2775,9 @@ + (cy_chip_offset[chip]<device, "cy_write_room")) return 0; - ret = PAGE_SIZE - info->xmit_cnt - 1; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) ret = 0; return ret; @@ -2724,6 +2848,7 @@ unsigned cflag; int i; + if (!info->tty || !info->tty->termios){ return; } @@ -2736,6 +2861,9 @@ channel = (info->line) - (cy_card[card].first_line); if (!IS_CYC_Z(cy_card[card])) { + + index = cy_card[card].bus_index; + /* baud rate */ i = cflag & CBAUD; @@ -2744,6 +2872,11 @@ i = 16; else if(i == B115200) i = 18; + else if(i == B230400 && + cy_readb(cy_card[card].base_addr+(CyGFRCR<= 0x48) { + /* It is a CD1400 rev. J or later */ + i = 20; + } #ifdef B76800 else if(i == B76800) i = 17; @@ -2753,15 +2886,35 @@ } if (i == 15) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - i += 1; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - i += 3; - } - info->tbpr = baud_bpr[i]; /* Tx BPR */ - info->tco = baud_co[i]; /* Tx CO */ - info->rbpr = baud_bpr[i]; /* Rx BPR */ - info->rco = baud_co[i]; /* Rx CO */ + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + i += 1; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + i += 3; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ + switch(info->baud) { + case 57600: + i += 1; break; + case 115200: + i += 3; break; + case 230400: + i += 5; break; + default: + break; + } + } + } + if(cy_readb(cy_card[card].base_addr+(CyGFRCR<= 0x48) { + /* It is a CD1400 rev. J or later */ + info->tbpr = baud_bpr_60[i]; /* Tx BPR */ + info->tco = baud_co_60[i]; /* Tx CO */ + info->rbpr = baud_bpr_60[i]; /* Rx BPR */ + info->rco = baud_co_60[i]; /* Rx CO */ + } else { + info->tbpr = baud_bpr_25[i]; /* Tx BPR */ + info->tco = baud_co_25[i]; /* Tx CO */ + info->rbpr = baud_bpr_25[i]; /* Rx BPR */ + info->rco = baud_co_25[i]; /* Rx CO */ + } if (baud_table[i] == 134) { info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; /* get it right for 134.5 baud */ @@ -2834,70 +2987,94 @@ chip = channel>>2; channel &= 0x03; - index = cy_card[card].bus_index; base_addr = (unsigned char*) (cy_card[card].base_addr + (cy_chip_offset[chip]<tco; - base_addr[CyTBPR<tbpr; - base_addr[CyRCOR<rco; - base_addr[CyRBPR<rbpr; + cy_writeb((u_long)base_addr+(CyTCOR<tco); + cy_writeb((u_long)base_addr+(CyTBPR<tbpr); + cy_writeb((u_long)base_addr+(CyRCOR<rco); + cy_writeb((u_long)base_addr+(CyRBPR<rbpr); /* set line characteristics according configuration */ - base_addr[CySCHR1<tty); - base_addr[CySCHR2<tty); - base_addr[CyCOR1<cor1; - base_addr[CyCOR2<cor2; - base_addr[CyCOR3<cor3; - base_addr[CyCOR4<cor4; - base_addr[CyCOR5<cor5; + cy_writeb((u_long)base_addr+(CySCHR1<tty)); + cy_writeb((u_long)base_addr+(CySCHR2<tty)); + cy_writeb((u_long)base_addr+(CyCOR1<cor1); + cy_writeb((u_long)base_addr+(CyCOR2<cor2); + cy_writeb((u_long)base_addr+(CyCOR3<cor3); + cy_writeb((u_long)base_addr+(CyCOR4<cor4); + cy_writeb((u_long)base_addr+(CyCOR5<cor5); cyy_issue_cmd(base_addr, CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index); - base_addr[CyCAR<default_timeout - ? info->default_timeout - : 0x02); /* 10ms rx timeout */ + cy_writeb((u_long)base_addr+(CyCAR<default_timeout + ? info->default_timeout + : 0x02)); /* 10ms rx timeout */ if (C_CLOCAL(info->tty)) { - base_addr[CySRER<0 modem transitions */ - base_addr[CyMCOR1<rflow) { + cy_writeb((u_long)base_addr+(CyMCOR1<1 modem transitions */ - base_addr[CyMCOR2<0 modem transitions */ - base_addr[CyMCOR1<rflow) { + cy_writeb((u_long)base_addr+(CyMCOR1<1 modem transitions */ - base_addr[CyMCOR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<zfwctrl_addr); + zfw_ctrl = (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = &zfw_ctrl->ch_ctrl[channel]; + ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; /* baud rate */ switch(i = cflag & CBAUD){ /* - case B0: ch_ctrl->comm_baud = 0; break; + case B0: cy_writel(&ch_ctrl->comm_baud , 0); break; */ - case B50: ch_ctrl->comm_baud = 50; break; - case B75: ch_ctrl->comm_baud = 75; break; - case B110: ch_ctrl->comm_baud = 110; break; - case B134: ch_ctrl->comm_baud = 134; break; - case B150: ch_ctrl->comm_baud = 150; break; - case B200: ch_ctrl->comm_baud = 200; break; - case B300: ch_ctrl->comm_baud = 300; break; - case B600: ch_ctrl->comm_baud = 600; break; - case B1200: ch_ctrl->comm_baud = 1200; break; - case B1800: ch_ctrl->comm_baud = 1800; break; - case B2400: ch_ctrl->comm_baud = 2400; break; - case B4800: ch_ctrl->comm_baud = 4800; break; - case B9600: ch_ctrl->comm_baud = 9600; break; - case B19200: ch_ctrl->comm_baud = 19200; break; + case B50: cy_writel(&ch_ctrl->comm_baud , 50); break; + case B75: cy_writel(&ch_ctrl->comm_baud , 75); break; + case B110: cy_writel(&ch_ctrl->comm_baud , 110); break; + case B134: cy_writel(&ch_ctrl->comm_baud , 134); break; + case B150: cy_writel(&ch_ctrl->comm_baud , 150); break; + case B200: cy_writel(&ch_ctrl->comm_baud , 200); break; + case B300: cy_writel(&ch_ctrl->comm_baud , 300); break; + case B600: cy_writel(&ch_ctrl->comm_baud , 600); break; + case B1200: cy_writel(&ch_ctrl->comm_baud , 1200); break; + case B1800: cy_writel(&ch_ctrl->comm_baud , 1800); break; + case B2400: cy_writel(&ch_ctrl->comm_baud , 2400); break; + case B4800: cy_writel(&ch_ctrl->comm_baud , 4800); break; + case B9600: cy_writel(&ch_ctrl->comm_baud , 9600); break; + case B19200: cy_writel(&ch_ctrl->comm_baud , 19200); break; case B38400: if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI){ - ch_ctrl->comm_baud = 57600; + cy_writel(&ch_ctrl->comm_baud , 57600); }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI){ - ch_ctrl->comm_baud = 115200; + cy_writel(&ch_ctrl->comm_baud , 115200); + }else if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ + cy_writel(&ch_ctrl->comm_baud , info->baud); }else{ - ch_ctrl->comm_baud = 38400; + cy_writel(&ch_ctrl->comm_baud , 38400); } break; - case B57600: ch_ctrl->comm_baud = 57600; break; + case B57600: cy_writel(&ch_ctrl->comm_baud , 57600); break; #ifdef B76800 - case B76800: ch_ctrl->comm_baud = 76800; break; + case B76800: cy_writel(&ch_ctrl->comm_baud , 76800); break; #endif - case B115200: ch_ctrl->comm_baud = 115200; break; - case B230400: ch_ctrl->comm_baud = 230400; break; - case B460800: ch_ctrl->comm_baud = 460800; break; + case B115200: cy_writel(&ch_ctrl->comm_baud , 115200); break; + case B230400: cy_writel(&ch_ctrl->comm_baud , 230400); break; + case B460800: cy_writel(&ch_ctrl->comm_baud , 460800); break; } - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST){ - ch_ctrl->comm_baud = info->baud; - } /* byte size and parity */ switch(cflag & CSIZE){ - case CS5: ch_ctrl->comm_data_l = C_DL_CS5; break; - case CS6: ch_ctrl->comm_data_l = C_DL_CS6; break; - case CS7: ch_ctrl->comm_data_l = C_DL_CS7; break; - case CS8: ch_ctrl->comm_data_l = C_DL_CS8; break; + case CS5: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS5); break; + case CS6: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS6); break; + case CS7: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS7); break; + case CS8: cy_writel(&ch_ctrl->comm_data_l , C_DL_CS8); break; } if(cflag & CSTOPB){ - ch_ctrl->comm_data_l |= C_DL_2STOP; + cy_writel(&ch_ctrl->comm_data_l, + cy_readl(&ch_ctrl->comm_data_l) | C_DL_2STOP); }else{ - ch_ctrl->comm_data_l |= C_DL_1STOP; + cy_writel(&ch_ctrl->comm_data_l, + cy_readl(&ch_ctrl->comm_data_l) | C_DL_1STOP); } if (cflag & PARENB){ if (cflag & PARODD){ - ch_ctrl->comm_parity = C_PR_ODD; + cy_writel(&ch_ctrl->comm_parity , C_PR_ODD); }else{ - ch_ctrl->comm_parity = C_PR_EVEN; + cy_writel(&ch_ctrl->comm_parity , C_PR_EVEN); } }else{ - ch_ctrl->comm_parity = C_PR_NONE; + cy_writel(&ch_ctrl->comm_parity , C_PR_NONE); } /* CTS flow control flag */ if (cflag & CRTSCTS){ info->flags |= ASYNC_CTS_FLOW; - ch_ctrl->hw_flow |= C_RS_CTS | C_RS_RTS; + cy_writel(&ch_ctrl->hw_flow, + cy_readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS); }else{ info->flags &= ~ASYNC_CTS_FLOW; - ch_ctrl->hw_flow &= ~(C_RS_CTS | C_RS_RTS); + cy_writel(&ch_ctrl->hw_flow, + cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS)); } retval = cyz_issue_cmd( &cy_card[card], channel, C_CM_IOCTL, 0L); @@ -3012,12 +3191,14 @@ } if(i == 0){ /* baud rate is zero, turn off line */ - ch_ctrl->rs_control &= ~C_RS_DTR; + cy_writel(&ch_ctrl->rs_control, + cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR); #ifdef SERIAL_DEBUG_DTR printk("cyc:set_line_char dropping Z DTR\n"); #endif }else{ - ch_ctrl->rs_control |= C_RS_DTR; + cy_writel(&ch_ctrl->rs_control, + cy_readl(&ch_ctrl->rs_control) | C_RS_DTR); #ifdef SERIAL_DEBUG_DTR printk("cyc:set_line_char raising Z DTR\n"); #endif @@ -3028,6 +3209,7 @@ printk("cyc:set_line_char retval at %d was %x\n", __LINE__, retval); } + cy_readl(&ch_ctrl->comm_baud); if (info->tty){ clear_bit(TTY_IO_ERROR, &info->tty->flags); @@ -3131,9 +3313,9 @@ + (cy_chip_offset[chip]<zfwctrl_addr); + zfw_ctrl = (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); board_ctrl = &zfw_ctrl->board_ctrl; ch_ctrl = zfw_ctrl->ch_ctrl; - lstatus = ch_ctrl[channel].rs_status; + lstatus = cy_readl(&ch_ctrl[channel].rs_status); result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) @@ -3203,18 +3384,27 @@ case TIOCMBIS: if (arg & TIOCM_RTS){ save_flags(flags); cli(); - base_addr[CyCAR<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR1<zfwctrl_addr); + zfw_ctrl = (struct ZFW_CTRL *) + (cy_card[card].base_addr + cy_readl(&firm_id->zfwctrl_addr)); board_ctrl = &zfw_ctrl->board_ctrl; ch_ctrl = zfw_ctrl->ch_ctrl; switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS){ - ch_ctrl[channel].rs_control |= C_RS_RTS; + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS); } if (arg & TIOCM_DTR){ - ch_ctrl[channel].rs_control |= C_RS_DTR; + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); #ifdef SERIAL_DEBUG_DTR printk("cyc:set_modem_info raising Z DTR\n"); #endif @@ -3301,10 +3521,12 @@ break; case TIOCMBIC: if (arg & TIOCM_RTS){ - ch_ctrl[channel].rs_control &= ~C_RS_RTS; + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS); } if (arg & TIOCM_DTR){ - ch_ctrl[channel].rs_control &= ~C_RS_DTR; + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); #ifdef SERIAL_DEBUG_DTR printk("cyc:set_modem_info clearing Z DTR\n"); #endif @@ -3312,17 +3534,21 @@ break; case TIOCMSET: if (arg & TIOCM_RTS){ - ch_ctrl[channel].rs_control |= C_RS_RTS; + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS); }else{ - ch_ctrl[channel].rs_control &= ~C_RS_RTS; + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS); } if (arg & TIOCM_DTR){ - ch_ctrl[channel].rs_control |= C_RS_DTR; + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR); #ifdef SERIAL_DEBUG_DTR printk("cyc:set_modem_info raising Z DTR\n"); #endif }else{ - ch_ctrl[channel].rs_control &= ~C_RS_DTR; + cy_writel(&ch_ctrl[channel].rs_control, + cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR); #ifdef SERIAL_DEBUG_DTR printk("cyc:set_modem_info clearing Z DTR\n"); #endif @@ -3362,8 +3588,8 @@ A better implementation will use C_CM_SET_BREAK and C_CM_CLR_BREAK with the appropriate delay. */ -#if 0 -this appears to wedge the output data stream +#if 1 +// this appears to wedge the output data stream int retval; retval = cyz_issue_cmd(&cy_card[info->card], (info->line) - (cy_card[info->card].first_line), @@ -3408,7 +3634,7 @@ info->cor3 &= ~CyREC_FIFO; info->cor3 |= value & CyREC_FIFO; - base_addr[CyCOR3<cor3; + cy_writeb((u_long)base_addr+(CyCOR3<cor3); cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index); } else { // Nothing to do! @@ -3434,7 +3660,7 @@ (cy_card[card].base_addr + (cy_chip_offset[chip]<rflow = 1; + ret_val = 0; + break; + case CYRESETRFLOW: + info->rflow = 0; + ret_val = 0; + break; + case CYSETRTSDTR_INV: + info->rtsdtr_inv = 1; + ret_val = 0; + break; + case CYRESETRTSDTR_INV: + info->rtsdtr_inv = 0; + ret_val = 0; + break; + case CYZPOLLCYCLE: + cyz_polling_cycle = (HZ * arg) / 1000; + ret_val = 0; + break; case TCSBRK: /* SVID version: non-zero arg --> no break */ ret_val = tty_check_change(tty); if (ret_val) @@ -3745,7 +3990,7 @@ #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("cyc:throttle %s: %d....ttyC%d\n", _tty_name(tty, buf), + printk("cyc:throttle %s: %d....ttyC%d\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty), info->line); #endif @@ -3769,8 +4014,12 @@ + (cy_chip_offset[chip]<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<ldisc.chars_in_buffer(tty), info->line); #endif @@ -3820,8 +4069,12 @@ + (cy_chip_offset[chip]<rtsdtr_inv) { + cy_writeb((u_long)base_addr+(CyMSVR2<= 0x48){ + /* It is a CD1400 rev. J or later */ + /* Impossible to reach 5ms with this chip. + Changed to 2ms instead (f = 500 Hz). */ + cy_writeb((u_long)base_addr+(CyPPR<= 0x020100) - cy_isa_address = ioremap((unsigned int)cy_isa_address,0x2000); + +#if !defined(__alpha__) && (LINUX_VERSION_CODE >= 0x020100) + cy_isa_address = ioremap((unsigned int)cy_isa_address, + CyISA_Ywin); #endif - cy_isa_nchan = 4 * cyy_init_card(cy_isa_address,0); + cy_isa_nchan = CyPORTS_PER_CHIP * + cyy_init_card(cy_isa_address,0); if (cy_isa_nchan == 0) { continue; } @@ -4078,15 +4342,15 @@ /* find out the board's irq by probing */ cy_isa_irq = do_auto_irq(cy_isa_address); if (cy_isa_irq == 0) { - printk("Cyclom-Y/ISA found at 0x%x ", - (unsigned int) cy_isa_address); + printk("Cyclom-Y/ISA found at 0x%lx ", + (unsigned long) cy_isa_address); printk("but the IRQ could not be detected.\n"); continue; } if((cy_next_channel+cy_isa_nchan) > NR_PORTS) { - printk("Cyclom-Y/ISA found at 0x%x ", - (unsigned int) cy_isa_address); + printk("Cyclom-Y/ISA found at 0x%lx ", + (unsigned long) cy_isa_address); printk("but no more channels are available.\n"); printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); return(nboard); @@ -4096,8 +4360,8 @@ if (cy_card[j].base_addr == 0) break; } if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Y/ISA found at 0x%x ", - (unsigned int) cy_isa_address); + printk("Cyclom-Y/ISA found at 0x%lx ", + (unsigned long) cy_isa_address); printk("but no more cards can be used .\n"); printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(nboard); @@ -4107,15 +4371,15 @@ if(request_irq(cy_isa_irq, cyy_interrupt, SA_INTERRUPT, "cyclomY", NULL)) { - printk("Cyclom-Y/ISA found at 0x%x ", - (unsigned int) cy_isa_address); + printk("Cyclom-Y/ISA found at 0x%lx ", + (unsigned long) cy_isa_address); printk("but could not allocate IRQ#%d.\n", cy_isa_irq); return(nboard); } /* set cy_card */ - cy_card[j].base_addr = (int) cy_isa_address; + cy_card[j].base_addr = (u_long) cy_isa_address; cy_card[j].ctl_addr = 0; cy_card[j].irq = (int) cy_isa_irq; cy_card[j].bus_index = 0; @@ -4125,10 +4389,10 @@ nboard++; /* print message */ - printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, ", - j+1, (unsigned int) cy_isa_address, - (unsigned int)(cy_isa_address + 0x1fff), - cy_isa_irq); + printk("Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1, (unsigned long) cy_isa_address, + (unsigned long)(cy_isa_address + (CyISA_Ywin - 1)), + cy_isa_irq); printk("%d channels starting from port %d.\n", cy_isa_nchan, cy_next_channel); cy_next_channel += cy_isa_nchan; @@ -4150,11 +4414,11 @@ unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id; unsigned long pci_intr_ctrl; unsigned char cy_pci_irq; - unsigned int cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; + uclong cy_pci_addr0, cy_pci_addr1, cy_pci_addr2; unsigned short i,j,cy_pci_nchan; unsigned short device_id,dev_index = 0,board_index = 0; - unsigned long mailbox; - unsigned int Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0; + uclong mailbox; + uclong Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0; if(pcibios_present() == 0) { /* PCI bus not present */ return(0); @@ -4181,13 +4445,17 @@ pcibios_read_config_byte(cyy_bus, cyy_dev_fn, PCI_INTERRUPT_LINE, &cy_pci_irq); pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_0, &cy_pci_addr0); + PCI_BASE_ADDRESS_0, + (unsigned int *) &cy_pci_addr0); pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_1, &cy_pci_addr1); + PCI_BASE_ADDRESS_1, + (unsigned int *) &cy_pci_addr1); pcibios_read_config_dword(cyy_bus, cyy_dev_fn, - PCI_BASE_ADDRESS_2, &cy_pci_addr2); + PCI_BASE_ADDRESS_2, + (unsigned int *) &cy_pci_addr2); pcibios_read_config_byte(cyy_bus, cyy_dev_fn, PCI_REVISION_ID, &cyy_rev_id); + if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){ #ifdef CY_PCI_DEBUG @@ -4195,34 +4463,48 @@ cyy_bus, cyy_dev_fn); printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); - printk("Cyclom-Y/PCI:found winaddr=0x%x ioaddr=0x%x\n", - cy_pci_addr2, cy_pci_addr1); + printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr1); #endif cy_pci_addr1 &= 0xfffffffc; cy_pci_addr2 &= 0xfffffff0; -#if (LINUX_VERSION_CODE < 0x020100) +#if defined(__alpha__) + if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */ + printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ", + cyy_bus, cyy_dev_fn); + printk("rev_id=%d) IRQ%d\n", + cyy_rev_id, (int)cy_pci_irq); + printk("Cyclom-Y/PCI:found winaddr=0x%lx ioaddr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr1); + printk("Cyclom-Y/PCI not supported for low addresses in " + "Alpha systems.\n"); + i--; + continue; + } +#else +#if (LINUX_VERSION_CODE < 0x020100) if ((ulong)cy_pci_addr2 >= 0x100000) /* above 1M? */ #endif - cy_pci_addr2 = - (unsigned int) ioremap(cy_pci_addr2,CyPCI_Ywin); + cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin); +#endif #ifdef CY_PCI_DEBUG - printk("Cyclom-Y/PCI: relocate winaddr=0x%x ioaddr=0x%x\n", - cy_pci_addr2, cy_pci_addr1); + printk("Cyclom-Y/PCI: relocate winaddr=0x%lx ioaddr=0x%lx\n", + (u_long)cy_pci_addr2, (u_long)cy_pci_addr1); #endif - cy_pci_nchan = 4 * cyy_init_card((unsigned char *) - cy_pci_addr2,1); + cy_pci_nchan = (unsigned short)(CyPORTS_PER_CHIP * + cyy_init_card((volatile ucchar *)cy_pci_addr2, 1)); if(cy_pci_nchan == 0) { printk("Cyclom-Y PCI host card with "); - printk("no Serial-Modules at 0x%x.\n", - (unsigned int) cy_pci_addr2); + printk("no Serial-Modules at 0x%lx.\n", + (ulong) cy_pci_addr2); i--; continue; } if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { - printk("Cyclom-Y/PCI found at 0x%x ", - (unsigned int) cy_pci_addr2); + printk("Cyclom-Y/PCI found at 0x%lx ", + (ulong) cy_pci_addr2); printk("but no channels are available.\n"); printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); return(i); @@ -4232,8 +4514,8 @@ if (cy_card[j].base_addr == 0) break; } if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclom-Y/PCI found at 0x%x ", - (unsigned int) cy_pci_addr2); + printk("Cyclom-Y/PCI found at 0x%lx ", + (ulong) cy_pci_addr2); printk("but no more cards can be used.\n"); printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(i); @@ -4243,15 +4525,15 @@ if(request_irq(cy_pci_irq, cyy_interrupt, SA_INTERRUPT, "cyclomY", NULL)) { - printk("Cyclom-Y/PCI found at 0x%x ", - (unsigned int) cy_pci_addr2); + printk("Cyclom-Y/PCI found at 0x%lx ", + (ulong) cy_pci_addr2); printk("but could not allocate IRQ%d.\n", cy_pci_irq); return(i); } /* set cy_card */ - cy_card[j].base_addr = (int) cy_pci_addr2; + cy_card[j].base_addr = (ulong)cy_pci_addr2; cy_card[j].ctl_addr = 0; cy_card[j].irq = (int) cy_pci_irq; cy_card[j].bus_index = 1; @@ -4266,9 +4548,11 @@ | inw(cy_pci_addr1+0x6a)<<16); /* print message */ - printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, ", - j+1, cy_pci_addr2, (cy_pci_addr2 + CyPCI_Ywin - 1), - (int)cy_pci_irq); + printk("Cyclom-Y/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1, + (ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Ywin - 1), + (int)cy_pci_irq); printk("%d channels starting from port %d.\n", cy_pci_nchan, cy_next_channel); @@ -4279,8 +4563,8 @@ cyy_bus, cyy_dev_fn); printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); - printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", - cy_pci_addr2, cy_pci_addr0); + printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); printk("Cyclades-Z/PCI not supported for low addresses\n"); break; }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){ @@ -4289,25 +4573,29 @@ cyy_bus, cyy_dev_fn); printk("rev_id=%d) IRQ%d\n", cyy_rev_id, (int)cy_pci_irq); - printk("Cyclades-Z/PCI: found winaddr=0x%x ctladdr=0x%x\n", - cy_pci_addr2, cy_pci_addr0); + printk("Cyclades-Z/PCI: found winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); #endif cy_pci_addr0 &= 0xfffffff0; +#if !defined(__alpha__) cy_pci_addr0 = (unsigned int) ioremap( cy_pci_addr0 & PAGE_MASK, PAGE_ALIGN(CyPCI_Zctl)) + (cy_pci_addr0 & (PAGE_SIZE-1)); - - mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0; +#endif + mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) + cy_pci_addr0)->mail_box_0); cy_pci_addr2 &= 0xfffffff0; if (mailbox == ZE_V1) { +#if !defined(__alpha__) cy_pci_addr2 = (unsigned int) ioremap( cy_pci_addr2 & PAGE_MASK, PAGE_ALIGN(CyPCI_Ze_win)) + (cy_pci_addr2 & (PAGE_SIZE-1)); +#endif if (ZeIndex == NR_CARDS) { - printk("Cyclades-Z/PCI found at 0x%x ", - (unsigned int) cy_pci_addr2); + printk("Cyclades-Ze/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); printk("but no more cards can be used.\n"); printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); } else { @@ -4318,24 +4606,28 @@ i--; continue; } else { +#if !defined(__alpha__) cy_pci_addr2 = (unsigned int) ioremap( cy_pci_addr2 & PAGE_MASK, PAGE_ALIGN(CyPCI_Zwin)) + (cy_pci_addr2 & (PAGE_SIZE-1)); +#endif } #ifdef CY_PCI_DEBUG - printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", - cy_pci_addr2, cy_pci_addr0); + printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); if (mailbox == ZO_V1) { - ((struct RUNTIME_9060 *)(cy_pci_addr0)) - ->loc_addr_base = WIN_CREG; + cy_writel(&((struct RUNTIME_9060 *) + (cy_pci_addr0))->loc_addr_base, WIN_CREG); PAUSE - printk("Cyclades-Z/PCI: FPGA id %lx, ver %lx\n", - 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_id, - 0xff & ((struct CUSTOM_REG *)(cy_pci_addr2))->fpga_version); - ((struct RUNTIME_9060 *)(cy_pci_addr0)) - ->loc_addr_base = WIN_RAM; + printk("Cyclades-8Zo/PCI: FPGA id %lx, ver %lx\n", + (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *) + (cy_pci_addr2))->fpga_id)), + (ulong)(0xff & cy_readl(&((struct CUSTOM_REG *) + (cy_pci_addr2))->fpga_version))); + cy_writel(&((struct RUNTIME_9060 *) + (cy_pci_addr0))->loc_addr_base, WIN_RAM); } else { printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); } @@ -4345,8 +4637,8 @@ until it has been properly initialized. */ PAUSE - if (mailbox == ZO_V1) - *(unsigned long *)(cy_pci_addr2+ID_ADDRESS) = 0L; + if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) + cy_writel((ulong)(cy_pci_addr2+ID_ADDRESS), 0L); /* This must be a Cyclades-8Zo/PCI. The extendable version will have a different device_id and will @@ -4354,8 +4646,8 @@ cy_pci_nchan = 8; if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { - printk("Cyclades-Z/PCI found at 0x%x ", - (unsigned int) cy_pci_addr2); + printk("Cyclades-8Zo/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); printk("but no channels are available.\n"); printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); return(i); @@ -4366,8 +4658,8 @@ if (cy_card[j].base_addr == 0) break; } if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclades-Z/PCI found at 0x%x ", - (unsigned int) cy_pci_addr2); + printk("Cyclades-8Zo/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); printk("but no more cards can be used.\n"); printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(i); @@ -4380,12 +4672,13 @@ { printk("Could not allocate IRQ%d ", cy_pci_irq); - printk("for Cyclades-Z/PCI at 0x%x.\n", - (unsigned int) cy_pci_addr2); + printk("for Cyclades-8Zo/PCI at 0x%lx.\n", + (ulong)cy_pci_addr2); return(i); } } + /* set cy_card */ cy_card[j].base_addr = cy_pci_addr2; cy_card[j].ctl_addr = cy_pci_addr0; @@ -4398,14 +4691,14 @@ /* print message */ /* don't report IRQ if board is no IRQ */ if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { - printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", - j+1,cy_pci_addr2, - (cy_pci_addr2 + CyPCI_Zwin - 1), + printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1), (int)cy_pci_irq); }else{ - printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ", - j+1,cy_pci_addr2, - (cy_pci_addr2 + CyPCI_Zwin - 1)); + printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Zwin - 1)); } printk("%d channels starting from port %d.\n", cy_pci_nchan,cy_next_channel); @@ -4421,10 +4714,11 @@ Ze_addr2[j] = Ze_addr2[j+1]; } ZeIndex--; - mailbox = ((struct RUNTIME_9060 *) cy_pci_addr0)->mail_box_0; + mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) + cy_pci_addr0)->mail_box_0); #ifdef CY_PCI_DEBUG - printk("Cyclades-Z/PCI: relocate winaddr=0x%x ctladdr=0x%x\n", - cy_pci_addr2, cy_pci_addr0); + printk("Cyclades-Z/PCI: relocate winaddr=0x%lx ctladdr=0x%lx\n", + (ulong)cy_pci_addr2, (ulong)cy_pci_addr0); printk("Cyclades-Z/PCI: New Cyclades-Z board. FPGA not loaded\n"); #endif /* The following clears the firmware id word. This ensures @@ -4436,8 +4730,8 @@ cy_pci_nchan = ZE_V1_NPORTS; if((cy_next_channel+cy_pci_nchan) > NR_PORTS) { - printk("Cyclades-Z/PCI found at 0x%x ", - (unsigned int) cy_pci_addr2); + printk("Cyclades-Ze/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); printk("but no channels are available.\n"); printk("Change NR_PORTS in cyclades.c and recompile kernel.\n"); return(i); @@ -4448,8 +4742,8 @@ if (cy_card[j].base_addr == 0) break; } if (j == NR_CARDS) { /* no more cy_cards available */ - printk("Cyclades-Z/PCI found at 0x%x ", - (unsigned int) cy_pci_addr2); + printk("Cyclades-Ze/PCI found at 0x%lx ", + (ulong)cy_pci_addr2); printk("but no more cards can be used.\n"); printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); return(i); @@ -4462,8 +4756,8 @@ { printk("Could not allocate IRQ%d ", cy_pci_irq); - printk("for Cyclades-Z/PCI at 0x%x.\n", - (unsigned int) cy_pci_addr2); + printk("for Cyclades-Ze/PCI at 0x%lx.\n", + (ulong) cy_pci_addr2); return(i); } } @@ -4480,21 +4774,21 @@ /* print message */ /* don't report IRQ if board is no IRQ */ if( (cy_pci_irq < 15) && (cy_pci_irq > 1) ) { - printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, IRQ%d, ", - j+1,cy_pci_addr2, - (cy_pci_addr2 + CyPCI_Ze_win - 1), + printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, IRQ%d, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1), (int)cy_pci_irq); }else{ - printk("Cyclades-Z/PCI #%d: 0x%x-0x%x, ", - j+1,cy_pci_addr2, - (cy_pci_addr2 + CyPCI_Ze_win - 1)); + printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, ", + j+1,(ulong)cy_pci_addr2, + (ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1)); } printk("%d channels starting from port %d.\n", cy_pci_nchan,cy_next_channel); cy_next_channel += cy_pci_nchan; } if (ZeIndex != 0) { - printk("Cyclades-Z/PCI found at 0x%x ", + printk("Cyclades-Ze/PCI found at 0x%x ", (unsigned int) Ze_addr2[0]); printk("but no more cards can be used.\n"); printk("Change NR_CARDS in cyclades.c and recompile kernel.\n"); @@ -4520,7 +4814,7 @@ tmp = strrchr(rcsdate, ' '); *tmp = '\0'; printk("Cyclom driver %s %s\n", rcsvers, rcsdate); - printk("\tbuilt %s %s\n", + printk(" built %s %s\n", __DATE__, __TIME__); } /* show_version */ @@ -4549,7 +4843,7 @@ struct cyclades_port *info; struct cyclades_card *cinfo; int number_z_boards = 0; - int board,port,i; + int board,port,i,index; unsigned long mailbox; int nports; @@ -4623,7 +4917,7 @@ /* look for isa boards */ cy_isa_nboard = cy_detect_isa(); - + /* look for pci boards */ cy_pci_nboard = cy_detect_pci(); @@ -4649,10 +4943,10 @@ /* initialize per-port data structures for each valid board found */ for (board = 0 ; board < cy_nboard ; board++) { cinfo = &cy_card[board]; - if (cinfo->num_chips == 1){ /* Cyclades-8Zo/PCI */ + if (cinfo->num_chips == 1){ /* Cyclades-Z */ number_z_boards++; - mailbox = ((struct RUNTIME_9060 *) - cy_card[board].ctl_addr)->mail_box_0; + mailbox = cy_readl(&((struct RUNTIME_9060 *) + cy_card[board].ctl_addr)->mail_box_0); nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8; for (port = cinfo->first_line ; port < cinfo->first_line + nports; @@ -4680,7 +4974,7 @@ info->event = 0; info->count = 0; #ifdef SERIAL_DEBUG_COUNT - printk("cyc:cy_init(1) setting Z count to 0\n"); +// printk("cyc:cy_init(1) setting Z count to 0\n"); #endif info->blocked_open = 0; info->default_threshold = 0; @@ -4705,6 +4999,7 @@ } continue; }else{ /* Cyclom-Y of some kind*/ + index = cinfo->bus_index; for (port = cinfo->first_line ; port < cinfo->first_line + 4*cinfo->num_chips ; port++) @@ -4722,16 +5017,28 @@ info->cor3 = 0x08; /* _very_ small rcv threshold */ info->cor4 = 0; info->cor5 = 0; - info->tbpr = baud_bpr[13]; /* Tx BPR */ - info->tco = baud_co[13]; /* Tx CO */ - info->rbpr = baud_bpr[13]; /* Rx BPR */ - info->rco = baud_co[13]; /* Rx CO */ info->close_delay = 0; + if (cy_readb(cinfo->base_addr+(CyGFRCR<= 0x48) { + /* It is a CD1400 rev. J or later */ + info->tbpr = baud_bpr_60[13]; /* Tx BPR */ + info->tco = baud_co_60[13]; /* Tx CO */ + info->rbpr = baud_bpr_60[13]; /* Rx BPR */ + info->rco = baud_co_60[13]; /* Rx CO */ + info->rflow = 0; + info->rtsdtr_inv = 1; + } else { + info->tbpr = baud_bpr_25[13]; /* Tx BPR */ + info->tco = baud_co_25[13]; /* Tx CO */ + info->rbpr = baud_bpr_25[13]; /* Rx BPR */ + info->rco = baud_co_25[13]; /* Rx CO */ + info->rflow = 0; + info->rtsdtr_inv = 0; + } info->x_char = 0; info->event = 0; info->count = 0; #ifdef SERIAL_DEBUG_COUNT - printk("cyc:cy_init(2) setting Y count to 0\n"); +// printk("cyc:cy_init(2) setting Y count to 0\n"); #endif info->blocked_open = 0; info->default_threshold = 0; @@ -4874,54 +5181,54 @@ /* Global Registers */ - printk(" CyGFRCR %x\n", base_addr[CyGFRCR< @@ -227,20 +230,21 @@ * one shots to clear. */ -static long js_read (struct inode *inode, struct file *file, char *buf, unsigned long count) +static ssize_t js_read (struct file *file, char *buf, + size_t count, loff_t *ppos) { int j, chk, jsmask; int t0, t_x0, t_y0, t_x1, t_y1; - unsigned int minor, minor2; + unsigned int minor; int buttons; - + struct inode *inode=file->f_dentry->d_inode; + if (count != JS_RETURN) return -EINVAL; - minor = MINOR (inode->i_rdev); + minor = MINOR (inode->i_rdev); inode->i_atime = CURRENT_TIME; if (jiffies >= js_data[minor].js_expiretime) { - minor2 = minor << 1; j = js_data[minor].js_timeout; for (; (js_exist & inb (JS_PORT)) && j; j--); if (j == 0) @@ -262,8 +266,8 @@ /*get init timestamp*/ t_x0 = t_y0 = t_x1 = t_y1 = t0 = get_timer0 (); /*wait for an axis' bit to clear or timeout*/ - while (j-- && (chk = (inb (JS_PORT) & js_exist ) | jsmask)) - { + do { + chk = (inb (JS_PORT) & js_exist) | jsmask; if (!(chk & JS_X_0)) { t_x0 = get_timer0(); jsmask |= JS_X_0; @@ -280,7 +284,7 @@ t_y1 = get_timer0(); jsmask |= JS_Y_1; } - } + } while (--j && jsmask != js_exist); sti(); /* allow interrupts */ js_read_semaphore = 0; /* allow other reads to progress */ diff -u --recursive --new-file v2.1.62/linux/drivers/char/misc.c linux/drivers/char/misc.c --- v2.1.62/linux/drivers/char/misc.c Sun Sep 7 13:10:42 1997 +++ linux/drivers/char/misc.c Tue Nov 4 10:23:25 1997 @@ -76,6 +76,7 @@ extern int rtc_init(void); extern int dsp56k_init(void); extern int nvram_init(void); +extern int radio_init(void); extern void hfmodem_init(void); #ifdef CONFIG_PROC_FS @@ -256,6 +257,9 @@ #endif #ifdef CONFIG_NVRAM nvram_init(); +#endif +#ifdef CONFIG_MISC_RADIO + radio_init(); #endif #ifdef CONFIG_HFMODEM hfmodem_init(); diff -u --recursive --new-file v2.1.62/linux/drivers/char/nvram.c linux/drivers/char/nvram.c --- v2.1.62/linux/drivers/char/nvram.c Tue Sep 23 16:48:47 1997 +++ linux/drivers/char/nvram.c Tue Nov 4 08:34:39 1997 @@ -227,11 +227,11 @@ return( (offset >= 0) ? (file->f_pos = offset) : -EINVAL ); } -static long nvram_read( struct inode * inode, struct file * file, - char * buf, unsigned long count ) +static ssize_t nvram_read( struct file * file, + char * buf, size_t count, loff_t *ppos ) { unsigned long flags; - unsigned i = file->f_pos; + unsigned i = *ppos; char *tmp = buf; save_flags(flags); @@ -244,17 +244,16 @@ for( ; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp ) put_user( nvram_read_int(i), tmp ); - file->f_pos = i; + *ppos = i; restore_flags(flags); return( tmp - buf ); } -static long nvram_write( struct inode * inode, struct file * file, - const char * buf, unsigned long count ) +static ssize_t nvram_write( struct file * file, const char * buf, size_t count, loff_t *ppos ) { unsigned long flags; - unsigned i = file->f_pos; + unsigned i = *ppos; const char *tmp = buf; char c; @@ -271,7 +270,7 @@ nvram_write_int( c, i ); } nvram_set_checksum_int(); - file->f_pos = i; + *ppos = i; restore_flags(flags); return( tmp - buf ); diff -u --recursive --new-file v2.1.62/linux/drivers/char/pcwd.c linux/drivers/char/pcwd.c --- v2.1.62/linux/drivers/char/pcwd.c Tue Sep 23 16:48:47 1997 +++ linux/drivers/char/pcwd.c Thu Nov 6 15:29:47 1997 @@ -29,6 +29,7 @@ * 961118 Changed some verbiage on some of the output, tidied up * code bits, and added compatibility to 2.1.x. * 970912 Enabled board on open and disable on close. + * 971107 Took account of recent VFS changes (broke read). */ #include @@ -222,7 +223,7 @@ } static int pcwd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { int i, cdat, rv; static struct watchdog_info ident= @@ -359,8 +360,12 @@ return 0; } -static long pcwd_write(struct inode *inode, struct file *file, const char *buf, unsigned long len) +static ssize_t pcwd_write(struct file *file, const char *buf, size_t len, + loff_t *ppos) { + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; if (len) { pcwd_send_heartbeat(); @@ -381,13 +386,16 @@ return(0); } -static long pcwd_read(struct inode *inode, struct file *file, char *buf, - unsigned long count) +static ssize_t pcwd_read(struct file *file, char *buf, size_t count, + loff_t *ppos) { unsigned short c = inb(current_readport); unsigned char cp; - switch(MINOR(inode->i_rdev)) + /* Can't seek (pread) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + switch(MINOR(file->f_dentry->d_inode->i_rdev)) { case TEMP_MINOR: cp = c; @@ -489,11 +497,16 @@ pcwd_read, /* Read */ pcwd_write, /* Write */ NULL, /* Readdir */ - NULL, /* Select */ + NULL, /* Poll */ pcwd_ioctl, /* IOctl */ NULL, /* MMAP */ pcwd_open, /* Open */ - pcwd_close /* Close */ + pcwd_close, /* Release */ + NULL, /* Fsync */ + NULL, /* Fasync */ + NULL, /* CheckMediaChange */ + NULL, /* Revalidate */ + NULL, /* Lock */ }; static struct miscdevice pcwd_miscdev = { diff -u --recursive --new-file v2.1.62/linux/drivers/char/radio.c linux/drivers/char/radio.c --- v2.1.62/linux/drivers/char/radio.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/radio.c Tue Nov 4 10:23:25 1997 @@ -0,0 +1,234 @@ +/* + * Radio Card Device Driver for Linux + * + * (c) 1997 Matthew Kirkwood + * + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#ifdef CONFIG_RADIO_RTRACK +#include "rtrack.h" +#endif +#ifdef CONFIG_RADIO_WINRADIO +#include "winradio.h" +#endif + +int radio_open(struct inode *inode, struct file *file); +int radio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); + +/* /dev/radio interface */ +static struct file_operations radio_fops = { + NULL, /* seek */ + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* select */ + &radio_ioctl, /* ioctl */ + NULL, /* mmap */ + &radio_open, /* we're not allowed NULL, it seems... */ + NULL /* release */ +}; + +static struct miscdevice radio_miscdevice = { + RADIO_MINOR, /* minor device number */ + "radio", /* title */ + &radio_fops, /* file operations */ + NULL, NULL /* previous and next (not our business) */ +}; + + +static struct radio_device *firstdevice, *lastdevice; +static int numdevices; + +__initfunc(void radio_init(void)) +{ + /* register the handler for the device number... */ + if(misc_register(&radio_miscdevice)) { + printk("radio: couldn't register misc device\n"); + return; + } + + /* do some general initialisation stuff */ + lastdevice = firstdevice = NULL; + numdevices = 0; + +#ifdef CONFIG_RADIO_RTRACK + radiotrack_init(); +#endif +#ifdef CONFIG_RADIO_WINRADIO + printk("oooops. no winradio support yet... :-(\n"); +#endif +/* etc.... */ + + printk("radio: registered %d devices\n", numdevices); +} + + +/* according to drivers/char/misc.c, the "open" call must not be NULL. + * I'm not sure if I approve, but I didn't write it, so... + */ +int radio_open(struct inode *inode, struct file *file) +{ + return 0; +} + + +/* append a device to the linked list... */ +int radio_add_device(struct radio_device *newdev) +{ + if(firstdevice == NULL) { + firstdevice = newdev; + } else { + lastdevice->next = newdev; + } + lastdevice = newdev; numdevices++; + newdev->cap->dev_num=numdevices; + newdev->next = NULL; /* don't need, but... */ + return(numdevices); +} + +struct radio_device *getdev(int index) +{ +struct radio_device *retval; + + if(index > numdevices) + return NULL; /* let's have a bit less of that */ + + retval = firstdevice; + for(;index;index--) + retval = retval->next; + + return retval; +} + + +/* interface routine */ +int radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ +struct radio_device *dev; +struct radio_ctl *ctl_arg = (struct radio_ctl*)arg; +int nowrite; +int val, ret; + + if((void*)arg == NULL) + return -EFAULT; /* XXX - check errnos are OK */ + + + switch(cmd) { + case RADIO_NUMDEVS: + return (put_user(numdevices,(int*)arg) ? -EFAULT : 0); + + + case RADIO_GETCAPS: + /* p'raps I should verify for read then write ?? */ + if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_cap))) + return -EFAULT; + if((dev = getdev(((struct radio_cap*)arg)->dev_num)) == NULL) + return -EINVAL; + copy_to_user((void*)arg, dev->cap, sizeof(struct radio_cap)); + return 0; + + + case RADIO_GETBNDCAP: + if(verify_area(VERIFY_WRITE, (void*)arg, sizeof(struct radio_band))) + return -EFAULT; + + if((dev = getdev(((struct radio_band*)arg)->dev_num)) == NULL) + return -EINVAL; + + val = ((struct radio_band*)arg)->index; + if(val >= dev->cap->num_bwidths) + return -EINVAL; /* XXX errno */ + + copy_to_user((void*)arg, (dev->bands)+(val*sizeof(struct radio_band)), + sizeof(struct radio_band)); + return 0; + } + + +/* now, we know that arg points to a struct radio_ctl */ + /* get the requested device */ + if(verify_area(VERIFY_READ, ctl_arg, sizeof(struct radio_ctl))) + return -EFAULT; + + if((dev = getdev(ctl_arg->dev_num)) == NULL) + return -EINVAL; + + nowrite = verify_area(VERIFY_WRITE, ctl_arg, sizeof(struct radio_ctl)); + + val = ctl_arg->value; + + switch(cmd) { + case RADIO_SETVOL: + if((val < dev->cap->volmin) || (val > dev->cap->volmax)) + return -EINVAL; + if((ret = (*(dev->setvol))(dev, val))) + return ret; + dev->curvol = val; + return 0; + + case RADIO_GETVOL: + if(nowrite) + return -EFAULT; + ctl_arg->value = dev->curvol; + return 0; + + + case RADIO_SETBAND: + if(val >= dev->cap->num_bwidths) + return -EINVAL; + if((ret = (*(dev->setband))(dev, val))) + return ret; + dev->curband = val; + return 0; + + case RADIO_GETBAND: + if(nowrite) + return -EFAULT; + ctl_arg->value = dev->curband; + return 0; + + + case RADIO_SETFREQ: { + struct radio_band *bp; + + bp = (dev->bands) + ((dev->curband) * sizeof(struct radio_band)); + if((val < bp->freqmin) || (val > bp->freqmax)) + return -EINVAL; + if((ret = (*(dev->setfreq))(dev, val))) + return ret; + dev->curfreq = val; + return 0; + } + + case RADIO_GETFREQ: + if(nowrite) + return -EFAULT; + ctl_arg->value = dev->curfreq; + return 0; + + + case RADIO_GETSIGSTR: + if(nowrite) + return -EFAULT; + ctl_arg->value = (*(dev->getsigstr))(dev); + return 0; + + + default: + return -ENOSYS; + } +} diff -u --recursive --new-file v2.1.62/linux/drivers/char/rtrack.c linux/drivers/char/rtrack.c --- v2.1.62/linux/drivers/char/rtrack.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/rtrack.c Tue Nov 4 10:23:25 1997 @@ -0,0 +1,213 @@ +/* radiotrack (radioreveal) driver for Linux radio support + * (c) 1997 M. Kirkwood + */ +/* TODO: Allow for more than one of these foolish entities :-) */ + +/* Notes on the hardware (reverse engineered from other peoples' + * reverse engineering of AIMS' code :-) + * + * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); + * + * The signal strength query is unsurprisingly inaccurate. And it seems + * to indicate that (on my card, at least) the frequency setting isn't + * too great. (I have to tune up .025MHz from what the freq should be + * to get a report that the thing is tuned.) + * + * Volume control is (ugh) analogue: + * out(port, start_increasing_volume); + * wait(a_wee_while); + * out(port, stop_changing_the_volume); + * + */ + +#include +#include +#include + +#include +#include +#include + +#include "rtrack.h" + +/* Let's just be a bit careful here, shall we? */ +#if (CONFIG_RADIO_RTRACK_PORT != 0x20f) && (CONFIG_RADIO_RTRACK_PORT != 0x30f) +#error Invalid port specified for RadioTrack +#endif + +/* local prototypes */ +void outbits(int bits, int data, int port); +void decvol(int port); +void incvol(int port); +void mute(int port); +void unmute(int port); + +/* public structurey-type things */ +int rt_port = CONFIG_RADIO_RTRACK_PORT; + +struct radio_cap rt_cap = { + 0, /* device index (not dealt with here) */ + RADIO_TYPE_RTRACK, /* type */ + 1, /* number of bandwidths */ + 0, 10 /* volmin, volmax */ +}; + +/* we only get one band/protocol with radiotrack */ +struct radio_band rt_band = { + 0, /* device index */ + 0, /* bandwidth "index" */ + RADIO_PROTOCOL_FM, + RADIO_BAND_FM_STD, + RADIO_FM_FRTOINT(88.0), /* freq range */ + RADIO_FM_FRTOINT(108.0), + 0,1 /* sig strength range */ +}; + +/* p'raps these should become struct radio_ops and struct radio_status? */ +struct radio_device rt_dev = { + &rt_cap, + &rt_band, + &rt_setvol, + 0, /* curvol */ + &rt_setband, + 0, /* curband */ + &rt_setfreq, + 0, /* curfreq */ + &rt_getsigstr, + NULL, /* next (to be filled in later) */ + &rt_port /* misc */ +}; + + +void radiotrack_init() +{ +int dev_num, i; + +/* XXX - probe here?? - XXX */ + +/* try to grab the i/o port */ + if(check_region(rt_port,2)) { + printk("rtrack.c: port 0x%x already used\n", rt_port); + return; + } + + request_region(rt_port,2,"rtrack"); + + dev_num = radio_add_device(&rt_dev); +/* initialise the card */ + /* set the volume very low */ + for(i=rt_cap.volmax; i>rt_cap.volmin; i--) + decvol(rt_port); + rt_dev.curvol = rt_cap.volmin; +} + + +int rt_setvol(struct radio_device *dev, int vol) +{ +int port, i; + + if(vol == dev->curvol) + return 0; + + port = *(int*)(dev->misc); + if(vol == 0) + mute(port); + + if(vol > dev->curvol) + for(i = dev->curvol; i < vol; i++) + incvol(port); + else + for(i = dev->curvol; i > vol; i--) + decvol(port); + + if(dev->curvol == 0) + unmute(port); + + return 0; +} + + +int rt_setband(struct radio_device *dev, int band) +{ +/* we know in advance that we only have one band, and + * the wrapper checks the validity of all the inputs + */ + return 0; +} + +int rt_setfreq(struct radio_device *dev, int freq) +{ +int myport = *(int*)(dev->misc); + + outbits(16, RTRACK_ENCODE(freq), myport); + outbits(8, 0xa0, myport); +/* XXX - get rid of this once setvol is implemented properly - XXX */ +/* these insist on turning the thing on. not sure I approve... */ + udelay(1000); + outb(0, myport); + outb(0xc8, myport); + + return 0; +} + +int rt_getsigstr(struct radio_device *dev) +{ +int res; +int myport = *(int*)(dev->misc); + + outb(0xf8, myport); + udelay(200000); + res = (int)inb(myport); + udelay(10000); + outb(0xe8, myport); + if(res == 0xfd) + return 1; + else + return 0; +} + + +/* local things */ +void outbits(int bits, int data, int port) +{ + while(bits--) { + if(data & 1) { + outw(5, port); + outw(5, port); + outw(7, port); + outw(7, port); + } else { + outw(1, port); + outw(1, port); + outw(3, port); + outw(3, port); + } + data>>=1; + } +} + +void decvol(int port) +{ + outb(0x48, port); + udelay(100000); + outb(0xc8, port); +} + +void incvol(int port) +{ + outb(0x88, port); + udelay(100000); + outb(0xc8, port); +} + +void mute(int port) +{ + outb(0, port); + outb(0xc0, port); +} + +void unmute(int port) +{ + outb(0, port); + outb(0xc8, port); +} diff -u --recursive --new-file v2.1.62/linux/drivers/char/rtrack.h linux/drivers/char/rtrack.h --- v2.1.62/linux/drivers/char/rtrack.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/rtrack.h Tue Nov 4 10:23:25 1997 @@ -0,0 +1,25 @@ +/* RadioTrack (RadioReveal) include file. + * (c) 1997 M. Kirkwood + * + * Not in include/linux/ because there's no need for anyone + * to know about these details, I reckon. + */ + +#ifndef __RTRACK_H +#define __RTRACK_H + +#include + +void radiotrack_init(void); +int rt_setvol(struct radio_device *dev, int vol); +int rt_setband(struct radio_device *dev, int vol); +int rt_setfreq(struct radio_device *dev, int vol); +int rt_getsigstr(struct radio_device *dev); + +/* frequency encoding stuff... */ +/* we have to careful not to introduce fp stuff here */ +#define RTRACK_ENCODE(x) (((((x)*2)/5)-(40*88))+0xf6c) +#define RTRACK_DECODE(x) (((((x)-0xf6c)+(40*88))*5)/2) +/* we shouldn't actually need the decode macro (or the excessive bracketing :-) */ + +#endif /* __RTRACK_H */ diff -u --recursive --new-file v2.1.62/linux/drivers/net/3c507.c linux/drivers/net/3c507.c --- v2.1.62/linux/drivers/net/3c507.c Mon Nov 3 13:04:26 1997 +++ linux/drivers/net/3c507.c Mon Nov 10 16:17:17 1997 @@ -447,7 +447,7 @@ { struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; - short *shmem = (short*)dev->mem_start; + unsigned long shmem = dev->mem_start; if (dev->tbusy) { @@ -458,7 +458,7 @@ return 1; if (net_debug > 1) printk("%s: transmit timed out, %s? ", dev->name, - shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" : + readw(shmem+iSCB_STATUS) & 0x8000 ? "IRQ conflict" : "network cable problem"); /* Try to restart the adaptor. */ if (lp->last_restart == lp->stats.tx_packets) { @@ -468,7 +468,7 @@ } else { /* Issue the channel attention signal and hope it "gets better". */ if (net_debug > 1) printk("Kicking board.\n"); - shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START; + writew(0xf000|CUC_START|RX_START,shmem+iSCB_CMD); outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */ lp->last_restart = lp->stats.tx_packets; } @@ -508,7 +508,7 @@ struct net_local *lp; int ioaddr, status, boguscount = 0; ushort ack_cmd = 0; - ushort *shmem; + unsigned long shmem; if (dev == NULL) { printk ("net_interrupt(): irq %d for unknown device.\n", irq); @@ -518,9 +518,9 @@ ioaddr = dev->base_addr; lp = (struct net_local *)dev->priv; - shmem = ((ushort*)dev->mem_start); + shmem = dev->mem_start; - status = shmem[iSCB_STATUS>>1]; + status = readw(shmem+iSCB_STATUS); if (net_debug > 4) { printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status); @@ -531,7 +531,7 @@ /* Reap the Tx packet buffers. */ while (lp->tx_reap != lp->tx_head) { - unsigned short tx_status = shmem[lp->tx_reap>>1]; + unsigned short tx_status = readw(shmem+lp->tx_reap); if (tx_status == 0) { if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap); @@ -586,11 +586,11 @@ printk("%s: Rx unit stopped, status %04x, restarting.\n", dev->name, status); init_rx_bufs(dev); - shmem[iSCB_RFA >> 1] = RX_BUF_START; + writew(RX_BUF_START,shmem+iSCB_RFA); ack_cmd |= RX_START; } - shmem[iSCB_CMD>>1] = ack_cmd; + writew(ack_cmd,shmem+iSCB_CMD); outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */ /* Clear the latched interrupt. */ @@ -605,13 +605,13 @@ static int el16_close(struct device *dev) { int ioaddr = dev->base_addr; - ushort *shmem = (short*)dev->mem_start; + unsigned long shmem = dev->mem_start; dev->tbusy = 1; dev->start = 0; /* Flush the Tx and disable Rx. */ - shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND; + writew(RX_SUSPEND | CUC_SUSPEND,shmem+iSCB_CMD); outb(0, ioaddr + SIGNAL_CA); /* Disable the 82586's input to the interrupt line. */ @@ -641,7 +641,7 @@ static void init_rx_bufs(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; - unsigned short *write_ptr; + unsigned long write_ptr; unsigned short SCB_base = SCB_BASE; int cur_rxbuf = lp->rx_head = RX_BUF_START; @@ -649,26 +649,26 @@ /* Initialize each Rx frame + data buffer. */ do { /* While there is room for one more. */ - write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf); + write_ptr = dev->mem_start + cur_rxbuf; - *write_ptr++ = 0x0000; /* Status */ - *write_ptr++ = 0x0000; /* Command */ - *write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */ - *write_ptr++ = cur_rxbuf + 22; /* Buffer offset */ - *write_ptr++ = 0x0000; /* Pad for dest addr. */ - *write_ptr++ = 0x0000; - *write_ptr++ = 0x0000; - *write_ptr++ = 0x0000; /* Pad for source addr. */ - *write_ptr++ = 0x0000; - *write_ptr++ = 0x0000; - *write_ptr++ = 0x0000; /* Pad for protocol. */ - - *write_ptr++ = 0x0000; /* Buffer: Actual count */ - *write_ptr++ = -1; /* Buffer: Next (none). */ - *write_ptr++ = cur_rxbuf + 0x20 + SCB_base; /* Buffer: Address low */ - *write_ptr++ = 0x0000; + writew(0x0000,write_ptr); /* Status */ + writew(0x0000,write_ptr+=2); /* Command */ + writew(cur_rxbuf + RX_BUF_SIZE,write_ptr+=2); /* Link */ + writew(cur_rxbuf + 22,write_ptr+=2); /* Buffer offset */ + writew(0x0000,write_ptr+=2); /* Pad for dest addr. */ + writew(0x0000,write_ptr+=2); + writew(0x0000,write_ptr+=2); + writew(0x0000,write_ptr+=2); /* Pad for source addr. */ + writew(0x0000,write_ptr+=2); + writew(0x0000,write_ptr+=2); + writew(0x0000,write_ptr+=2); /* Pad for protocol. */ + + writew(0x0000,write_ptr+=2); /* Buffer: Actual count */ + writew(-1,write_ptr+=2); /* Buffer: Next (none). */ + writew(cur_rxbuf + 0x20 + SCB_base,write_ptr+=2);/* Buffer: Address low */ + writew(0x0000,write_ptr+=2); /* Finally, the number of bytes in the buffer. */ - *write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20; + writew(0x8000 + RX_BUF_SIZE-0x20,write_ptr+=2); lp->rx_tail = cur_rxbuf; cur_rxbuf += RX_BUF_SIZE; @@ -676,18 +676,16 @@ /* Terminate the list by setting the EOL bit, and wrap the pointer to make the list a ring. */ - write_ptr = (unsigned short *) - (dev->mem_start + lp->rx_tail + 2); - *write_ptr++ = 0xC000; /* Command, mark as last. */ - *write_ptr++ = lp->rx_head; /* Link */ - + write_ptr = dev->mem_start + lp->rx_tail + 2; + writew(0xC000,write_ptr); /* Command, mark as last. */ + writew(lp->rx_head,write_ptr+2); /* Link */ } static void init_82586_mem(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; short ioaddr = dev->base_addr; - ushort *shmem = (short*)dev->mem_start; + unsigned long shmem = dev->mem_start; /* Enable loopback to protect the wire while starting up, and hold the 586 in reset during the memory initialization. */ @@ -698,13 +696,13 @@ init_words[7] = SCB_BASE; /* Write the words at 0xfff6 (address-aliased to 0xfffff6). */ - memcpy((void*)dev->mem_end-10, init_words, 10); + memcpy_toio(dev->mem_end-10, init_words, 10); /* Write the words at 0x0000. */ - memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10); + memcpy_toio(dev->mem_start, init_words + 5, sizeof(init_words) - 10); /* Fill in the station address. */ - memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr, + memcpy_toio(dev->mem_start+SA_OFFSET, dev->dev_addr, sizeof(dev->dev_addr)); /* The Tx-block list is written as needed. We just set up the values. */ @@ -722,11 +720,11 @@ { int boguscnt = 50; - while (shmem[iSCB_STATUS>>1] == 0) + while (readw(shmem+iSCB_STATUS) == 0) if (--boguscnt == 0) { printk("%s: i82586 initialization timed out with status %04x," "cmd %04x.\n", dev->name, - shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]); + readw(shmem+iSCB_STATUS), readw(shmem+iSCB_CMD)); break; } /* Issue channel-attn -- the 82586 won't start. */ @@ -737,7 +735,7 @@ outb(0x84, ioaddr + MISC_CTRL); if (net_debug > 4) printk("%s: Initialized 82586, status %04x.\n", dev->name, - shmem[iSCB_STATUS>>1]); + readw(shmem+iSCB_STATUS)); return; } @@ -746,30 +744,30 @@ struct net_local *lp = (struct net_local *)dev->priv; short ioaddr = dev->base_addr; ushort tx_block = lp->tx_head; - ushort *write_ptr = (ushort *)(dev->mem_start + tx_block); + unsigned long write_ptr = dev->mem_start + tx_block; /* Set the write pointer to the Tx block, and put out the header. */ - *write_ptr++ = 0x0000; /* Tx status */ - *write_ptr++ = CMD_INTR|CmdTx; /* Tx command */ - *write_ptr++ = tx_block+16; /* Next command is a NoOp. */ - *write_ptr++ = tx_block+8; /* Data Buffer offset. */ + writew(0x0000,write_ptr); /* Tx status */ + writew(CMD_INTR|CmdTx,write_ptr+=2); /* Tx command */ + writew(tx_block+16,write_ptr+=2); /* Next command is a NoOp. */ + writew(tx_block+8,write_ptr+=2); /* Data Buffer offset. */ /* Output the data buffer descriptor. */ - *write_ptr++ = length | 0x8000; /* Byte count parameter. */ - *write_ptr++ = -1; /* No next data buffer. */ - *write_ptr++ = tx_block+22+SCB_BASE;/* Buffer follows the NoOp command. */ - *write_ptr++ = 0x0000; /* Buffer address high bits (always zero). */ + writew(length | 0x8000,write_ptr+=2); /* Byte count parameter. */ + writew(-1,write_ptr+=2); /* No next data buffer. */ + writew(tx_block+22+SCB_BASE,write_ptr+=2); /* Buffer follows the NoOp command. */ + writew(0x0000,write_ptr+=2); /* Buffer address high bits (always zero). */ /* Output the Loop-back NoOp command. */ - *write_ptr++ = 0x0000; /* Tx status */ - *write_ptr++ = CmdNOp; /* Tx command */ - *write_ptr++ = tx_block+16; /* Next is myself. */ + writew(0x0000,write_ptr+=2); /* Tx status */ + writew(CmdNOp,write_ptr+=2); /* Tx command */ + writew(tx_block+16,write_ptr+=2); /* Next is myself. */ /* Output the packet at the write pointer. */ - memcpy(write_ptr, buf, length); + memcpy_toio(write_ptr+2, buf, length); /* Set the old command link pointing to this send packet. */ - *(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block; + writew(tx_block,dev->mem_start + lp->tx_cmd_link); lp->tx_cmd_link = tx_block + 20; /* Set the next free tx region. */ @@ -789,19 +787,19 @@ static void el16_rx(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; - short *shmem = (short*)dev->mem_start; + unsigned long shmem = dev->mem_start; ushort rx_head = lp->rx_head; ushort rx_tail = lp->rx_tail; ushort boguscount = 10; short frame_status; - while ((frame_status = shmem[rx_head>>1]) < 0) { /* Command complete */ - ushort *read_frame = (short *)(dev->mem_start + rx_head); - ushort rfd_cmd = read_frame[1]; - ushort next_rx_frame = read_frame[2]; - ushort data_buffer_addr = read_frame[3]; - ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr); - ushort pkt_len = data_frame[0]; + while ((frame_status = readw(shmem+rx_head)) < 0) { /* Command complete */ + unsigned long read_frame = dev->mem_start + rx_head; + ushort rfd_cmd = readw(read_frame+2); + ushort next_rx_frame = readw(read_frame+4); + ushort data_buffer_addr = readw(read_frame+6); + unsigned long data_frame = dev->mem_start + data_buffer_addr; + ushort pkt_len = readw(data_frame); if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22 || (pkt_len & 0xC000) != 0xC000) { @@ -833,7 +831,7 @@ skb->dev = dev; /* 'skb->data' points to the start of sk_buff data area. */ - memcpy(skb_put(skb,pkt_len), data_frame + 5, pkt_len); + memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len); skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); @@ -841,10 +839,10 @@ } /* Clear the status word and set End-of-List on the rx frame. */ - read_frame[0] = 0; - read_frame[1] = 0xC000; + writew(0,read_frame); + writew(0xC000,read_frame+2); /* Clear the end-of-list on the prev. RFD. */ - *(short*)(dev->mem_start + rx_tail + 2) = 0x0000; + writew(0x0000,dev->mem_start + rx_tail + 2); rx_tail = rx_head; rx_head = next_rx_frame; diff -u --recursive --new-file v2.1.62/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c --- v2.1.62/linux/drivers/net/dgrs.c Thu May 29 21:53:07 1997 +++ linux/drivers/net/dgrs.c Sun Nov 9 10:13:48 1997 @@ -176,7 +176,7 @@ int dgrs_hashexpire = -1; uchar dgrs_ipaddr[4] = { 0xff, 0xff, 0xff, 0xff}; uchar dgrs_iptrap[4] = { 0xff, 0xff, 0xff, 0xff}; -long dgrs_ipxnet = -1; +__u32 dgrs_ipxnet = -1; int dgrs_nicmode = 0; /* @@ -1507,7 +1507,7 @@ static int spantree = -1; static int ipaddr[4] = { -1 }; static int iptrap[4] = { -1 }; -static long ipxnet = -1; +static __u32 ipxnet = -1; static int nicmode = -1; MODULE_PARM(debug, "i"); diff -u --recursive --new-file v2.1.62/linux/drivers/net/ne.c linux/drivers/net/ne.c --- v2.1.62/linux/drivers/net/ne.c Mon Nov 3 13:04:26 1997 +++ linux/drivers/net/ne.c Tue Nov 11 08:17:49 1997 @@ -78,6 +78,8 @@ {PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_89C940}, {PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_RL2000}, {PCI_VENDOR_ID_KTI, PCI_DEVICE_ID_KTI_ET32P2}, + {PCI_VENDOR_ID_NETVIN, PCI_DEVICE_ID_NETVIN_NV5000SC}, + {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C926}, {0,} }; #endif diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.1.62/linux/drivers/scsi/Config.in Mon Oct 20 10:36:52 1997 +++ linux/drivers/scsi/Config.in Wed Nov 5 13:13:02 1997 @@ -49,6 +49,7 @@ int ' maximum number of queued commands' CONFIG_SCSI_EATA_MAX_TAGS 16 fi dep_tristate 'Future Domain 16xx SCSI/AHA 2920 support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI +dep_tristate 'GDT SCSI Disk Array Controller support' CONFIG_SCSI_GDTH $CONFIG_SCSI dep_tristate 'Generic NCR5380/53c400 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then bool ' Enable NCR53c400 extensions' CONFIG_SCSI_GENERIC_NCR53C400 @@ -95,6 +96,9 @@ fi fi dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI +dep_tristate 'PCI2000 support' CONFIG_SCSI_PCI2000 $CONFIG_SCSI +dep_tristate 'PCI2220i support' CONFIG_SCSI_PCI2220I $CONFIG_SCSI +dep_tristate 'PSI240i support' CONFIG_SCSI_PSI240I $CONFIG_SCSI dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI if [ "$CONFIG_PCI" = "y" ]; then dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.1.62/linux/drivers/scsi/Makefile Mon Nov 3 13:04:26 1997 +++ linux/drivers/scsi/Makefile Wed Nov 5 13:11:54 1997 @@ -15,6 +15,7 @@ SCSI_SRCS = $(wildcard $(L_OBJS:%.o=%.c)) AHA152X = -DDEBUG_AHA152X -DAUTOCONF +GDTH = #-DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ -DGDTH_STATISTICS .SUFFIXES: .SUFFIXES: .c .o .h .a @@ -91,6 +92,30 @@ endif endif +ifeq ($(CONFIG_SCSI_PCI2000),y) +L_OBJS += pci2000.o +else + ifeq ($(CONFIG_SCSI_PCI2000),m) + M_OBJS += pci2000.o + endif +endif + +ifeq ($(CONFIG_SCSI_PCI2220I),y) +L_OBJS += pci2220i.o +else + ifeq ($(CONFIG_SCSI_PCI2220I),m) + M_OBJS += pci2220i.o + endif +endif + +ifeq ($(CONFIG_SCSI_PSI240I),y) +L_OBJS += psi240i.o +else + ifeq ($(CONFIG_SCSI_PSI240I),m) + M_OBJS += psi240i.o + endif +endif + ifeq ($(CONFIG_A4000T_SCSI),y) L_OBJS += amiga7xx.o 53c7xx.o else @@ -285,6 +310,14 @@ endif endif +ifeq ($(CONFIG_SCSI_GDTH),y) +L_OBJS += gdth.o +else + ifeq ($(CONFIG_SCSI_GDTH),m) + M_OBJS += gdth.o + endif +endif + ifeq ($(CONFIG_SCSI_DEBUG),y) L_OBJS += scsi_debug.o else @@ -432,6 +465,9 @@ aha152x.o: aha152x.c $(CC) $(CFLAGS) $(AHA152X) -c aha152x.c + +gdth.o: gdth.c gdth.h gdth_proc.c gdth_proc.h + $(CC) $(CFLAGS) $(GDTH) -c gdth.c aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h $(CC) $(CFLAGS) -c -o $@ aic7xxx.c diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c --- v2.1.62/linux/drivers/scsi/fdomain.c Fri Dec 27 02:03:23 1996 +++ linux/drivers/scsi/fdomain.c Fri Nov 7 10:16:36 1997 @@ -470,6 +470,7 @@ { "Future Domain Corp. V1.0008/18/93", 5, 33, 3, 4, 0 }, { "Future Domain Corp. V1.0008/18/93", 26, 33, 3, 4, 1 }, { "Adaptec AHA-2920 PCI-SCSI Card", 42, 31, 3, -1, 1 }, + { "IBM F1 P264/32", 5, 14, 3, -1, 1 }, /* This next signature may not be a 3.5 bios */ { "Future Domain Corp. V2.0108/18/93", 5, 33, 3, 5, 0 }, { "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 }, diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/gdth.c linux/drivers/scsi/gdth.c --- v2.1.62/linux/drivers/scsi/gdth.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/gdth.c Tue Nov 4 09:17:30 1997 @@ -0,0 +1,3387 @@ +/************************************************************************ + * GDT ISA/EISA/PCI Disk Array Controller driver for Linux * + * * + * gdth.c * + * Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner * + * * + * * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published * + * by the Free Software Foundation; either version 2 of the License, * + * or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this kernel; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * * + * Tested with Linux 1.2.13, ..., 2.1.61 * + * * + * $Log: gdth.c,v $ + * Revision 1.10 1997/10/31 12:29:57 achim + * Read heads/sectors from host drive + * + * Revision 1.9 1997/09/04 10:07:25 achim + * IO-mapping with virt_to_bus(), readb(), writeb(), ... + * register_reboot_notifier() to get a notify on shutdown used + * + * Revision 1.8 1997/04/02 12:14:30 achim + * Version 1.00 (see gdth.h), tested with kernel 2.0.29 + * + * Revision 1.7 1997/03/12 13:33:37 achim + * gdth_reset() changed, new async. events + * + * Revision 1.6 1997/03/04 14:01:11 achim + * Shutdown routine gdth_halt() implemented + * + * Revision 1.5 1997/02/21 09:08:36 achim + * New controller included (RP, RP1, RP2 series) + * IOCTL interface implemented + * + * Revision 1.4 1996/07/05 12:48:55 achim + * Function gdth_bios_param() implemented + * New constant GDTH_MAXC_P_L inserted + * GDT_WRITE_THR, GDT_EXT_INFO implemented + * Function gdth_reset() changed + * + * Revision 1.3 1996/05/10 09:04:41 achim + * Small changes for Linux 1.2.13 + * + * Revision 1.2 1996/05/09 12:45:27 achim + * Loadable module support implemented + * /proc support corrections made + * + * Revision 1.1 1996/04/11 07:35:57 achim + * Initial revision + * + * + * $Id: gdth.c,v 1.10 1997/10/31 12:29:57 achim Exp $ + ************************************************************************/ + +#ifdef MODULE +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= 0x020100 +#include +#endif + +#include +#include +#include + +#if LINUX_VERSION_CODE >= 0x010300 +#include +#else +#include "../block/blk.h" +#endif +#include "scsi.h" +#include "hosts.h" +#include "sd.h" + +#include "gdth.h" + +#if LINUX_VERSION_CODE >= 0x010346 +static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs); +#else +static void gdth_interrupt(int irq,struct pt_regs *regs); +#endif +static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp); +static int gdth_async_event(int hanum,int service); + +static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority); +static void gdth_next(int hanum); +static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b); +static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b); +static gdth_evt_str *gdth_store_event(ushort source, ushort idx, + gdth_evt_data *evt); +static int gdth_read_event(int handle, gdth_evt_str *estr); +static void gdth_readapp_event(unchar application, gdth_evt_str *estr); +static void gdth_clear_events(void); + +static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count); +static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp, + unchar b,ulong *flags); +static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive); + +static int gdth_search_eisa(ushort eisa_adr); +static int gdth_search_isa(ulong bios_adr); +static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr); +static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha); +static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha); +static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha); + +static void gdth_enable_int(int hanum); +static int gdth_get_status(unchar *pIStatus,int irq); +static int gdth_test_busy(int hanum); +static int gdth_get_cmd_index(int hanum); +static void gdth_release_event(int hanum); +static int gdth_wait(int hanum,int index,ulong time); +static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1, + ulong p2,ulong p3); +static int gdth_search_drives(int hanum); + +static void *gdth_mmap(ulong paddr, ulong size); +static void gdth_munmap(void *addr); + +static const char *gdth_ctr_name(int hanum); +#if LINUX_VERSION_CODE >= 0x020100 +static int gdth_halt(struct notifier_block *nb, ulong event, void *buf); +#else +void gdth_halt(void); +#endif + +#ifdef DEBUG_GDTH +static unchar DebugState = DEBUG_GDTH; +extern int sys_syslog(int,char*,int); +#define LOGEN sys_syslog(7,NULL,0); +#define WAITSEC(a) {ulong idx; for(idx=0;idxjiffies);} +#define GDTOFFSOF(a,b) (size_t)&(((a*)0)->b) +#define INDEX_OK(i,t) ((i)hostdata)) +#define HADATA(a) (&((gdth_ext_str *)((a)->hostdata))->haext) +#define CMDDATA(a) (&((gdth_ext_str *)((a)->hostdata))->cmdext) +#define DMADATA(a) (&((gdth_ext_str *)((a)->hostdata))->dmaext) + + +#if LINUX_VERSION_CODE < 0x010300 +static void *gdth_mmap(ulong paddr, ulong size) +{ + if (paddr >= high_memory) + return NULL; + else + return (void *)paddr; +} +static void gdth_munmap(void *addr) +{ +} +inline ulong virt_to_phys(volatile void *addr) +{ + return (ulong)addr; +} +inline void *phys_to_virt(ulong addr) +{ + return (void *)addr; +} +#define virt_to_bus virt_to_phys +#define bus_to_virt phys_to_virt +#define readb(addr) (*(volatile unchar *)(addr)) +#define readw(addr) (*(volatile ushort *)(addr)) +#define readl(addr) (*(volatile ulong *)(addr)) +#define writeb(b,addr) (*(volatile unchar *)(addr) = (b)) +#define writew(b,addr) (*(volatile ushort *)(addr) = (b)) +#define writel(b,addr) (*(volatile ulong *)(addr) = (b)) +#define memset_io(a,b,c) memset((void *)(a),(b),(c)) +#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) + +#elif LINUX_VERSION_CODE < 0x020100 +static int remapped = FALSE; +static void *gdth_mmap(ulong paddr, ulong size) +{ + if ( paddr >= high_memory) { + remapped = TRUE; + return vremap(paddr, size); + } else { + return (void *)paddr; + } +} +static void gdth_munmap(void *addr) +{ + if (remapped) + vfree(addr); + remapped = FALSE; +} +#else +static void *gdth_mmap(ulong paddr, ulong size) +{ + return ioremap(paddr, size); +} +static void gdth_munmap(void *addr) +{ + return iounmap(addr); +} +#endif + + +static unchar gdth_drq_tab[4] = {5,6,7,7}; /* DRQ table */ +static unchar gdth_irq_tab[6] = {0,10,11,12,14,0}; /* IRQ table */ +static unchar gdth_polling; /* polling if TRUE */ +static unchar gdth_from_wait = FALSE; /* gdth_wait() */ +static int wait_index,wait_hanum; /* gdth_wait() */ +static int gdth_ctr_count = 0; /* controller count */ +static int gdth_ctr_vcount = 0; /* virt. ctr. count */ +static struct Scsi_Host *gdth_ctr_tab[MAXHA]; /* controller table */ +static struct Scsi_Host *gdth_ctr_vtab[MAXHA*MAXBUS]; /* virt. ctr. table */ +static unchar gdth_write_through = FALSE; /* write through */ +static char *gdth_ioctl_tab[4][MAXHA]; /* ioctl buffer */ +static gdth_evt_str ebuffer[MAX_EVENTS]; /* event buffer */ +static int elastidx; +static int eoldidx; + +static struct { + Scsi_Cmnd *cmnd; /* pending request */ + ushort service; /* service */ +} gdth_cmd_tab[GDTH_MAXCMDS][MAXHA]; /* table of pend. requests */ + +#define DIN 1 /* IN data direction */ +#define DOU 2 /* OUT data direction */ +#define DNO DIN /* no data transfer */ +#define DUN DIN /* unknown data direction */ +static unchar gdth_direction_tab[0x100] = { + DNO,DNO,DIN,DIN,DOU,DIN,DIN,DOU,DIN,DUN,DOU,DOU,DUN,DUN,DUN,DIN, + DNO,DIN,DIN,DOU,DIN,DOU,DNO,DNO,DOU,DNO,DIN,DNO,DIN,DOU,DNO,DUN, + DIN,DUN,DIN,DUN,DOU,DIN,DUN,DUN,DIN,DIN,DIN,DUN,DUN,DIN,DIN,DIN, + DIN,DIN,DIN,DNO,DIN,DNO,DNO,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN,DIN, + DIN,DIN,DIN,DIN,DIN,DNO,DUN,DNO,DNO,DNO,DUN,DNO,DIN,DIN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DIN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DNO,DNO,DUN,DIN,DNO,DIN,DUN,DNO,DUN,DIN,DIN, + DIN,DIN,DIN,DNO,DUN,DIN,DIN,DIN,DIN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN +}; + +/* LILO params: gdth= + * + * Where: is any of the valid IRQs for EISA controllers (10,11,12,14) + * Sets the IRQ of the GDT3000/3020 EISA controller to this value, + * if the IRQ can not automat. detect (controller BIOS disabled) + * See gdth_init_eisa() + * + * You can use the command line gdth=0 to disable the driver + */ +static unchar irqs[MAXHA] = {0xff}; +static unchar disable_gdth_scan = FALSE; + +/* /proc support */ +#if LINUX_VERSION_CODE >= 0x010300 +#include +struct proc_dir_entry proc_scsi_gdth = { + PROC_SCSI_GDTH, 4, "gdth", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; +#include "gdth_proc.h" +#include "gdth_proc.c" +#endif + +#if LINUX_VERSION_CODE >= 0x020100 +/* notifier block to get a notify on system shutdown/halt/reboot */ +static struct notifier_block gdth_notifier = { + gdth_halt, NULL, 0 +}; +#endif + +/* controller search and initialization functions */ + +static int gdth_search_eisa(ushort eisa_adr) +{ + ulong id; + + TRACE(("gdth_search_eisa() adr. %x\n",eisa_adr)); + id = inl(eisa_adr+ID0REG); + if (id == GDT3A_ID || id == GDT3B_ID) { /* GDT3000A or GDT3000B */ + if ((inb(eisa_adr+EISAREG) & 8) == 0) + return 0; /* not EISA configured */ + return 1; + } + if (id == GDT3_ID) /* GDT3000 */ + return 1; + + return 0; +} + + +static int gdth_search_isa(ulong bios_adr) +{ + void *addr; + ulong id; + + TRACE(("gdth_search_isa() bios adr. %lx\n",bios_adr)); + if ((addr = gdth_mmap(bios_adr+BIOS_ID_OFFS, sizeof(ulong))) != NULL) { + id = readl(addr); + gdth_munmap(addr); + if (id == GDT2_ID) /* GDT2000 */ + return 1; + } + return 0; +} + + +static int gdth_search_pci(ushort device_id,ushort index,gdth_pci_str *pcistr) +{ + int error; + ulong base0,base1,base2; + + TRACE(("gdth_search_pci() device_id %d, index %d\n", + device_id,index)); + + if (!pcibios_present()) + return 0; + + if (pcibios_find_device(PCI_VENDOR_ID_VORTEX,device_id,index, + &pcistr->bus,&pcistr->device_fn)) + return 0; + + /* GDT PCI controller found, now read resources from config space */ +#if LINUX_VERSION_CODE >= 0x010300 +#define GDTH_BASEP (int *) +#else +#define GDTH_BASEP +#endif + if ((error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn, + PCI_BASE_ADDRESS_0, + GDTH_BASEP&base0)) || + (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn, + PCI_BASE_ADDRESS_1, + GDTH_BASEP&base1)) || + (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn, + PCI_BASE_ADDRESS_2, + GDTH_BASEP&base2)) || + (error = pcibios_read_config_dword(pcistr->bus,pcistr->device_fn, + PCI_ROM_ADDRESS, + GDTH_BASEP&pcistr->bios)) || + (error = pcibios_read_config_byte(pcistr->bus,pcistr->device_fn, + PCI_INTERRUPT_LINE,&pcistr->irq))) { + printk("GDT-PCI: error %s reading configuration space", + pcibios_strerror(error)); + return -1; + } + + pcistr->device_id = device_id; + if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B || /* GDT6000 or GDT6000B */ + device_id >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) { /* MPR */ + if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY) + return -1; + pcistr->dpmem = base0 & PCI_BASE_ADDRESS_MEM_MASK; + } else { /* GDT6110, GDT6120, .. */ + if ((base0 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY || + (base2 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_MEMORY || + (base1 & PCI_BASE_ADDRESS_SPACE)!=PCI_BASE_ADDRESS_SPACE_IO) + return -1; + pcistr->dpmem = base2 & PCI_BASE_ADDRESS_MEM_MASK; + pcistr->io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK; + pcistr->io = base1 & PCI_BASE_ADDRESS_IO_MASK; + } + return 1; +} + + +static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha) +{ + ulong retries,id; + unchar prot_ver,eisacf,i,irq_found; + + TRACE(("gdth_init_eisa() adr. %x\n",eisa_adr)); + + /* disable board interrupts, deinitialize services */ + outb(0xff,eisa_adr+EDOORREG); + outb(0x00,eisa_adr+EDENABREG); + outb(0x00,eisa_adr+EINTENABREG); + + outb(0xff,eisa_adr+LDOORREG); + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (inb(eisa_adr+EDOORREG) != 0xff) { + if (--retries == 0) { + printk("GDT-EISA: Initialization error (DEINIT failed)\n"); + return 0; + } + udelay(1000); + TRACE2(("wait for DEINIT: retries=%ld\n",retries)); + } + prot_ver = inb(eisa_adr+MAILBOXREG); + outb(0xff,eisa_adr+EDOORREG); + if (prot_ver != PROTOCOL_VERSION) { + printk("GDT-EISA: Illegal protocol version\n"); + return 0; + } + ha->bmic = eisa_adr; + ha->brd_phys = (ulong)eisa_adr >> 12; + + outl(0,eisa_adr+MAILBOXREG); + outl(0,eisa_adr+MAILBOXREG+4); + outl(0,eisa_adr+MAILBOXREG+8); + outl(0,eisa_adr+MAILBOXREG+12); + + /* detect IRQ */ + if ((id = inl(eisa_adr+ID0REG)) == GDT3_ID) { + ha->type = GDT_EISA; + ha->stype = id; + outl(1,eisa_adr+MAILBOXREG+8); + outb(0xfe,eisa_adr+LDOORREG); + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (inb(eisa_adr+EDOORREG) != 0xfe) { + if (--retries == 0) { + printk("GDT-EISA: Initialization error (get IRQ failed)\n"); + return 0; + } + udelay(1000); + } + ha->irq = inb(eisa_adr+MAILBOXREG); + outb(0xff,eisa_adr+EDOORREG); + TRACE2(("GDT3000/3020: IRQ=%d\n",ha->irq)); + /* check the result */ + if (ha->irq == 0) { + TRACE2(("Unknown IRQ, check IRQ table from cmd line !\n")); + for (i=0,irq_found=FALSE; iirq = irqs[i]; + irqs[i] = 0; + printk("GDT-EISA: Can not detect controller IRQ,\n"); + printk("Use IRQ setting from command line (IRQ = %d)\n", + ha->irq); + } else { + printk("GDT-EISA: Initialization error (unknown IRQ), Enable\n"); + printk("the controller BIOS or use command line parameters\n"); + return 0; + } + } + } else { + eisacf = inb(eisa_adr+EISAREG) & 7; + if (eisacf > 4) /* level triggered */ + eisacf -= 4; + ha->irq = gdth_irq_tab[eisacf]; + ha->type = GDT_EISA; + ha->stype= id; + } + return 1; +} + + +static int gdth_init_isa(ulong bios_adr,gdth_ha_str *ha) +{ + register gdt2_dpram_str *dp2_ptr; + int i; + unchar irq_drq,prot_ver; + ulong retries; + + TRACE(("gdth_init_isa() bios adr. %lx\n",bios_adr)); + + ha->brd = gdth_mmap(bios_adr, sizeof(gdt2_dpram_str)); + if (ha->brd == NULL) { + printk("GDT-ISA: Initialization error (DPMEM remap error)\n"); + return 0; + } + dp2_ptr = (gdt2_dpram_str *)ha->brd; + writeb(1, &dp2_ptr->io.memlock); /* switch off write protection */ + /* reset interface area */ + memset_io((char *)&dp2_ptr->u,0,sizeof(dp2_ptr->u)); + + /* disable board interrupts, read DRQ and IRQ */ + writeb(0xff, &dp2_ptr->io.irqdel); + writeb(0x00, &dp2_ptr->io.irqen); + writeb(0x00, &dp2_ptr->u.ic.S_Status); + writeb(0x00, &dp2_ptr->u.ic.Cmd_Index); + + irq_drq = readb(&dp2_ptr->io.rq); + for (i=0; i<3; ++i) { + if ((irq_drq & 1)==0) + break; + irq_drq >>= 1; + } + ha->drq = gdth_drq_tab[i]; + + irq_drq = readb(&dp2_ptr->io.rq) >> 3; + for (i=1; i<5; ++i) { + if ((irq_drq & 1)==0) + break; + irq_drq >>= 1; + } + ha->irq = gdth_irq_tab[i]; + + /* deinitialize services */ + writel(bios_adr, &dp2_ptr->u.ic.S_Info[0]); + writeb(0xff, &dp2_ptr->u.ic.S_Cmd_Indx); + writeb(0, &dp2_ptr->io.event); + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (readb(&dp2_ptr->u.ic.S_Status) != 0xff) { + if (--retries == 0) { + printk("GDT-ISA: Initialization error (DEINIT failed)\n"); + gdth_munmap(ha->brd); + return 0; + } + udelay(1000); + } + prot_ver = (unchar)readl(&dp2_ptr->u.ic.S_Info[0]); + writeb(0, &dp2_ptr->u.ic.Status); + writeb(0xff, &dp2_ptr->io.irqdel); + if (prot_ver != PROTOCOL_VERSION) { + printk("GDT-ISA: Illegal protocol version\n"); + gdth_munmap(ha->brd); + return 0; + } + + ha->type = GDT_ISA; + ha->ic_all_size = sizeof(dp2_ptr->u); + ha->stype= GDT2_ID; + ha->brd_phys = bios_adr >> 4; + + /* special request to controller BIOS */ + writel(0x00, &dp2_ptr->u.ic.S_Info[0]); + writel(0x00, &dp2_ptr->u.ic.S_Info[1]); + writel(0x01, &dp2_ptr->u.ic.S_Info[2]); + writel(0x00, &dp2_ptr->u.ic.S_Info[3]); + writeb(0xfe, &dp2_ptr->u.ic.S_Cmd_Indx); + writeb(0, &dp2_ptr->io.event); + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (readb(&dp2_ptr->u.ic.S_Status) != 0xfe) { + if (--retries == 0) { + printk("GDT-ISA: Initialization error\n"); + gdth_munmap(ha->brd); + return 0; + } + udelay(1000); + } + writeb(0, &dp2_ptr->u.ic.Status); + writeb(0xff, &dp2_ptr->io.irqdel); + return 1; +} + + +static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha) +{ + register gdt6_dpram_str *dp6_ptr; + register gdt6c_dpram_str *dp6c_ptr; + register gdt6m_dpram_str *dp6m_ptr; + ulong retries; + unchar prot_ver; + + TRACE(("gdth_init_pci()\n")); + + ha->brd_phys = (pcistr->bus << 8) | (pcistr->device_fn & 0xf8); + ha->stype = (ulong)pcistr->device_id; + ha->irq = pcistr->irq; + + if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6000B) { /* GDT6000 or GDT6000B */ + TRACE2(("init_pci() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq)); + ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6_dpram_str)); + if (ha->brd == NULL) { + printk("GDT-PCI: Initialization error (DPMEM remap error)\n"); + return 0; + } + dp6_ptr = (gdt6_dpram_str *)ha->brd; + /* reset interface area */ + memset_io((char *)&dp6_ptr->u,0,sizeof(dp6_ptr->u)); + if (readl(&dp6_ptr->u) != 0) { + printk("GDT-PCI: Initialization error (DPMEM write error)\n"); + gdth_munmap(ha->brd); + return 0; + } + + /* disable board interrupts, deinit services */ + writeb(0xff, &dp6_ptr->io.irqdel); + writeb(0x00, &dp6_ptr->io.irqen);; + writeb(0x00, &dp6_ptr->u.ic.S_Status); + writeb(0x00, &dp6_ptr->u.ic.Cmd_Index); + + writel(pcistr->dpmem, &dp6_ptr->u.ic.S_Info[0]); + writeb(0xff, &dp6_ptr->u.ic.S_Cmd_Indx); + writeb(0, &dp6_ptr->io.event); + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (readb(&dp6_ptr->u.ic.S_Status) != 0xff) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error (DEINIT failed)\n"); + gdth_munmap(ha->brd); + return 0; + } + udelay(1000); + } + prot_ver = (unchar)readl(&dp6_ptr->u.ic.S_Info[0]); + writeb(0, &dp6_ptr->u.ic.S_Status); + writeb(0xff, &dp6_ptr->io.irqdel); + if (prot_ver != PROTOCOL_VERSION) { + printk("GDT-PCI: Illegal protocol version\n"); + gdth_munmap(ha->brd); + return 0; + } + + ha->type = GDT_PCI; + ha->ic_all_size = sizeof(dp6_ptr->u); + + /* special command to controller BIOS */ + writel(0x00, &dp6_ptr->u.ic.S_Info[0]); + writel(0x00, &dp6_ptr->u.ic.S_Info[1]); + writel(0x01, &dp6_ptr->u.ic.S_Info[2]); + writel(0x00, &dp6_ptr->u.ic.S_Info[3]); + writeb(0xfe, &dp6_ptr->u.ic.S_Cmd_Indx); + writeb(0, &dp6_ptr->io.event); + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (readb(&dp6_ptr->u.ic.S_Status) != 0xfe) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error\n"); + gdth_munmap(ha->brd); + return 0; + } + udelay(1000); + } + writeb(0, &dp6_ptr->u.ic.S_Status); + writeb(0xff, &dp6_ptr->io.irqdel); + + } else if (ha->stype <= PCI_DEVICE_ID_VORTEX_GDT6555) { /* GDT6110, GDT6120, .. */ + ha->plx = (gdt6c_plx_regs *)pcistr->io; + TRACE2(("init_pci_new() dpmem %lx io %lx irq %d\n", + pcistr->dpmem,(ulong)ha->plx,ha->irq)); + ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6c_dpram_str)); + if (ha->brd == NULL) { + printk("GDT-PCI: Initialization error (DPMEM remap error)\n"); + gdth_munmap(ha->brd); + return 0; + } + dp6c_ptr = (gdt6c_dpram_str *)ha->brd; + /* reset interface area */ + memset_io((char *)&dp6c_ptr->u,0,sizeof(dp6c_ptr->u)); + if (readl(&dp6c_ptr->u) != 0) { + printk("GDT-PCI: Initialization error (DPMEM write error)\n"); + gdth_munmap(ha->brd); + return 0; + } + + /* disable board interrupts, deinit services */ + outb(0x00,PTR2USHORT(&ha->plx->control1)); + outb(0xff,PTR2USHORT(&ha->plx->edoor_reg)); + + writeb(0x00, &dp6c_ptr->u.ic.S_Status); + writeb(0x00, &dp6c_ptr->u.ic.Cmd_Index); + + writel(pcistr->dpmem, &dp6c_ptr->u.ic.S_Info[0]); + writeb(0xff, &dp6c_ptr->u.ic.S_Cmd_Indx); + + outb(1,PTR2USHORT(&ha->plx->ldoor_reg)); + + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (readb(&dp6c_ptr->u.ic.S_Status) != 0xff) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error (DEINIT failed)\n"); + gdth_munmap(ha->brd); + return 0; + } + udelay(1000); + } + prot_ver = (unchar)readl(&dp6c_ptr->u.ic.S_Info[0]); + writeb(0, &dp6c_ptr->u.ic.Status); + if (prot_ver != PROTOCOL_VERSION) { + printk("GDT-PCI: Illegal protocol version\n"); + gdth_munmap(ha->brd); + return 0; + } + + ha->type = GDT_PCINEW; + ha->ic_all_size = sizeof(dp6c_ptr->u); + + /* special command to controller BIOS */ + writel(0x00, &dp6c_ptr->u.ic.S_Info[0]); + writel(0x00, &dp6c_ptr->u.ic.S_Info[1]); + writel(0x01, &dp6c_ptr->u.ic.S_Info[2]); + writel(0x00, &dp6c_ptr->u.ic.S_Info[3]); + writeb(0xfe, &dp6c_ptr->u.ic.S_Cmd_Indx); + + outb(1,PTR2USHORT(&ha->plx->ldoor_reg)); + + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (readb(&dp6c_ptr->u.ic.S_Status) != 0xfe) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error\n"); + gdth_munmap(ha->brd); + return 0; + } + udelay(1000); + } + writeb(0, &dp6c_ptr->u.ic.S_Status); + + } else { /* MPR */ + TRACE2(("init_pci_mpr() dpmem %lx irq %d\n",pcistr->dpmem,ha->irq)); + ha->brd = gdth_mmap(pcistr->dpmem, sizeof(gdt6m_dpram_str)); + if (ha->brd == NULL) { + printk("GDT-PCI: Initialization error (DPMEM remap error)\n"); + return 0; + } + + dp6m_ptr = (gdt6m_dpram_str *)ha->brd; + /* reset interface area */ + memset_io((char *)&dp6m_ptr->u,0,sizeof(dp6m_ptr->u)); + if (readl(&dp6m_ptr->u) != 0) { + printk("GDT-PCI: Initialization error (DPMEM write error)\n"); + gdth_munmap(ha->brd); + return 0; + } + + /* disable board interrupts, deinit services */ + writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) | 4, + &dp6m_ptr->i960r.edoor_en_reg); + writeb(0xff, &dp6m_ptr->i960r.edoor_reg); + writeb(0x00, &dp6m_ptr->u.ic.S_Status); + writeb(0x00, &dp6m_ptr->u.ic.Cmd_Index); + + writel(pcistr->dpmem, &dp6m_ptr->u.ic.S_Info[0]); + writeb(0xff, &dp6m_ptr->u.ic.S_Cmd_Indx); + writeb(1, &dp6m_ptr->i960r.ldoor_reg); + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (readb(&dp6m_ptr->u.ic.S_Status) != 0xff) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error (DEINIT failed)\n"); + gdth_munmap(ha->brd); + return 0; + } + udelay(1000); + } + prot_ver = (unchar)readl(&dp6m_ptr->u.ic.S_Info[0]); + writeb(0, &dp6m_ptr->u.ic.S_Status); + if (prot_ver != PROTOCOL_VERSION) { + printk("GDT-PCI: Illegal protocol version\n"); + gdth_munmap(ha->brd); + return 0; + } + + ha->type = GDT_PCIMPR; + ha->ic_all_size = sizeof(dp6m_ptr->u); + + /* special command to controller BIOS */ + writel(0x00, &dp6m_ptr->u.ic.S_Info[0]); + writel(0x00, &dp6m_ptr->u.ic.S_Info[1]); + writel(0x01, &dp6m_ptr->u.ic.S_Info[2]); + writel(0x00, &dp6m_ptr->u.ic.S_Info[3]); + writeb(0xfe, &dp6m_ptr->u.ic.S_Cmd_Indx); + writeb(1, &dp6m_ptr->i960r.ldoor_reg); + retries = INIT_RETRIES; + JIFFYWAIT(2); + while (readb(&dp6m_ptr->u.ic.S_Status) != 0xfe) { + if (--retries == 0) { + printk("GDT-PCI: Initialization error\n"); + gdth_munmap(ha->brd); + return 0; + } + udelay(1000); + } + writeb(0, &dp6m_ptr->u.ic.S_Status); + } + + return 1; +} + + +/* controller protocol functions */ + +static void gdth_enable_int(int hanum) +{ + gdth_ha_str *ha; + ulong flags; + gdt2_dpram_str *dp2_ptr; + gdt6_dpram_str *dp6_ptr; + gdt6m_dpram_str *dp6m_ptr; + + TRACE(("gdth_enable_int() hanum %d\n",hanum)); + ha = HADATA(gdth_ctr_tab[hanum]); + + save_flags(flags); + cli(); + + if (ha->type == GDT_EISA) { + outb(0xff, ha->bmic + EDOORREG); + outb(0xff, ha->bmic + EDENABREG); + outb(0x01, ha->bmic + EINTENABREG); + } else if (ha->type == GDT_ISA) { + dp2_ptr = (gdt2_dpram_str *)ha->brd; + writeb(1, &dp2_ptr->io.irqdel); + writeb(0, &dp2_ptr->u.ic.Cmd_Index); + writeb(1, &dp2_ptr->io.irqen); + } else if (ha->type == GDT_PCI) { + dp6_ptr = (gdt6_dpram_str *)ha->brd; + writeb(1, &dp6_ptr->io.irqdel); + writeb(0, &dp6_ptr->u.ic.Cmd_Index); + writeb(1, &dp6_ptr->io.irqen); + } else if (ha->type == GDT_PCINEW) { + outb(0xff, PTR2USHORT(&ha->plx->edoor_reg)); + outb(0x03, PTR2USHORT(&ha->plx->control1)); + } else if (ha->type == GDT_PCIMPR) { + dp6m_ptr = (gdt6m_dpram_str *)ha->brd; + writeb(0xff, &dp6m_ptr->i960r.edoor_reg); + writeb(readb(&dp6m_ptr->i960r.edoor_en_reg) & ~4, + &dp6m_ptr->i960r.edoor_en_reg); + } + restore_flags(flags); +} + + +static int gdth_get_status(unchar *pIStatus,int irq) +{ + register gdth_ha_str *ha; + int i; + + TRACE(("gdth_get_status() irq %d ctr_count %d\n", + irq,gdth_ctr_count)); + + *pIStatus = 0; + for (i=0; iirq != (unchar)irq) /* check IRQ */ + continue; + if (ha->type == GDT_EISA) + *pIStatus = inb((ushort)ha->bmic + EDOORREG); + else if (ha->type == GDT_ISA) + *pIStatus = readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Cmd_Index); + else if (ha->type == GDT_PCI) + *pIStatus = readb(&((gdt6_dpram_str *)ha->brd)->u.ic.Cmd_Index); + else if (ha->type == GDT_PCINEW) + *pIStatus = inb(PTR2USHORT(&ha->plx->edoor_reg)); + else if (ha->type == GDT_PCIMPR) + *pIStatus = readb(&((gdt6m_dpram_str *)ha->brd)->i960r.edoor_reg); + + if (*pIStatus) + return i; /* board found */ + } + return -1; +} + + +static int gdth_test_busy(int hanum) +{ + register gdth_ha_str *ha; + register int gdtsema0 = 0; + + TRACE(("gdth_test_busy() hanum %d\n",hanum)); + + ha = HADATA(gdth_ctr_tab[hanum]); + if (ha->type == GDT_EISA) + gdtsema0 = (int)inb(ha->bmic + SEMA0REG); + else if (ha->type == GDT_ISA) + gdtsema0 = (int)readb(&((gdt2_dpram_str *)ha->brd)->u.ic.Sema0); + else if (ha->type == GDT_PCI) + gdtsema0 = (int)readb(&((gdt6_dpram_str *)ha->brd)->u.ic.Sema0); + else if (ha->type == GDT_PCINEW) + gdtsema0 = (int)inb(PTR2USHORT(&ha->plx->sema0_reg)); + else if (ha->type == GDT_PCIMPR) + gdtsema0 = (int)readb(&((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg); + + return (gdtsema0 & 1); +} + + +static int gdth_get_cmd_index(int hanum) +{ + register gdth_ha_str *ha; + int i; + + TRACE(("gdth_get_cmd_index() hanum %d\n",hanum)); + + ha = HADATA(gdth_ctr_tab[hanum]); + for (i=0; ipccb->RequestBuffer; + gdth_cmd_tab[i][hanum].service = ha->pccb->Service; + ha->pccb->CommandIndex = (ulong)i+2; + return (i+2); + } + } + return 0; +} + + +static void gdth_set_sema0(int hanum) +{ + register gdth_ha_str *ha; + + TRACE(("gdth_set_sema0() hanum %d\n",hanum)); + + ha = HADATA(gdth_ctr_tab[hanum]); + if (ha->type == GDT_EISA) + outb(1, ha->bmic + SEMA0REG); + else if (ha->type == GDT_ISA) + writeb(1, &((gdt2_dpram_str *)ha->brd)->u.ic.Sema0); + else if (ha->type == GDT_PCI) + writeb(1, &((gdt6_dpram_str *)ha->brd)->u.ic.Sema0); + else if (ha->type == GDT_PCINEW) + outb(1, PTR2USHORT(&ha->plx->sema0_reg)); + else if (ha->type == GDT_PCIMPR) + writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.sema0_reg); + +} + + +static void gdth_copy_command(int hanum) +{ + register gdth_ha_str *ha; + register gdth_cmd_str *cmd_ptr; + register gdt6m_dpram_str *dp6m_ptr; + register gdt6c_dpram_str *dp6c_ptr; + gdt6_dpram_str *dp6_ptr; + gdt2_dpram_str *dp2_ptr; + ushort cp_count,dp_offset,cmd_no; + + TRACE(("gdth_copy_command() hanum %d\n",hanum)); + + ha = HADATA(gdth_ctr_tab[hanum]); + cp_count = ha->cmd_len; + dp_offset= ha->cmd_offs_dpmem; + cmd_no = ha->cmd_cnt; + cmd_ptr = ha->pccb; + + ++ha->cmd_cnt; + if (ha->type == GDT_EISA) + return; /* no DPMEM, no copy */ + + /* set cpcount dword aligned */ + if (cp_count & 3) + cp_count += (4 - (cp_count & 3)); + + ha->cmd_offs_dpmem += cp_count; + + /* set offset and service, copy command to DPMEM */ + if (ha->type == GDT_ISA) { + dp2_ptr = (gdt2_dpram_str *)ha->brd; + writew(dp_offset + DPMEM_COMMAND_OFFSET, + &dp2_ptr->u.ic.comm_queue[cmd_no].offset); + writew((ushort)cmd_ptr->Service, + &dp2_ptr->u.ic.comm_queue[cmd_no].serv_id); + memcpy_toio(&dp2_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + } else if (ha->type == GDT_PCI) { + dp6_ptr = (gdt6_dpram_str *)ha->brd; + writew(dp_offset + DPMEM_COMMAND_OFFSET, + &dp6_ptr->u.ic.comm_queue[cmd_no].offset); + writew((ushort)cmd_ptr->Service, + &dp6_ptr->u.ic.comm_queue[cmd_no].serv_id); + memcpy_toio(&dp6_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + } else if (ha->type == GDT_PCINEW) { + dp6c_ptr = (gdt6c_dpram_str *)ha->brd; + writew(dp_offset + DPMEM_COMMAND_OFFSET, + &dp6c_ptr->u.ic.comm_queue[cmd_no].offset); + writew((ushort)cmd_ptr->Service, + &dp6c_ptr->u.ic.comm_queue[cmd_no].serv_id); + memcpy_toio(&dp6c_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + } else if (ha->type == GDT_PCIMPR) { + dp6m_ptr = (gdt6m_dpram_str *)ha->brd; + writew(dp_offset + DPMEM_COMMAND_OFFSET, + &dp6m_ptr->u.ic.comm_queue[cmd_no].offset); + writew((ushort)cmd_ptr->Service, + &dp6m_ptr->u.ic.comm_queue[cmd_no].serv_id); + memcpy_toio(&dp6m_ptr->u.ic.gdt_dpr_cmd[dp_offset],cmd_ptr,cp_count); + } +} + + +static void gdth_release_event(int hanum) +{ + register gdth_ha_str *ha; + +#ifdef GDTH_STATISTICS + ulong i,j; + for (i=0,j=0; jpccb->OpCode == GDT_INIT) + ha->pccb->Service |= 0x80; + + if (ha->type == GDT_EISA) { + outb(ha->pccb->Service, ha->bmic + LDOORREG); + if (ha->pccb->OpCode == GDT_INIT) /* store DMA buffer */ + outl((ulong)ha->pccb, ha->bmic + MAILBOXREG); + } else if (ha->type == GDT_ISA) + writeb(0, &((gdt2_dpram_str *)ha->brd)->io.event); + else if (ha->type == GDT_PCI) + writeb(0, &((gdt6_dpram_str *)ha->brd)->io.event); + else if (ha->type == GDT_PCINEW) + outb(1, PTR2USHORT(&ha->plx->ldoor_reg)); + else if (ha->type == GDT_PCIMPR) + writeb(1, &((gdt6m_dpram_str *)ha->brd)->i960r.ldoor_reg); +} + + +static int gdth_wait(int hanum,int index,ulong time) +{ + gdth_ha_str *ha; + int answer_found = FALSE; + + TRACE(("gdth_wait() hanum %d index %d time %ld\n",hanum,index,time)); + + ha = HADATA(gdth_ctr_tab[hanum]); + if (index == 0) + return 1; /* no wait required */ + + gdth_from_wait = TRUE; + do { +#if LINUX_VERSION_CODE >= 0x010346 + gdth_interrupt((int)ha->irq,NULL,NULL); +#else + gdth_interrupt((int)ha->irq,NULL); +#endif + if (wait_hanum==hanum && wait_index==index) { + answer_found = TRUE; + break; + } + udelay(1000); + } while (--time); + gdth_from_wait = FALSE; + + while (gdth_test_busy(hanum)) + udelay(1); + + return (answer_found); +} + + +static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong p1, + ulong p2,ulong p3) +{ + register gdth_ha_str *ha; + register gdth_cmd_str *cmd_ptr; + int retries,index; + + TRACE2(("gdth_internal_cmd() service %d opcode %d\n",service,opcode)); + + ha = HADATA(gdth_ctr_tab[hanum]); + cmd_ptr = ha->pccb; + memset((char*)cmd_ptr,0,sizeof(gdth_cmd_str)); + + /* make command */ + for (retries = INIT_RETRIES;;) { + cmd_ptr->Service = service; + cmd_ptr->RequestBuffer = INTERNAL_CMND; + if (!(index=gdth_get_cmd_index(hanum))) { + TRACE(("GDT: No free command index found\n")); + return 0; + } + gdth_set_sema0(hanum); + cmd_ptr->OpCode = opcode; + cmd_ptr->BoardNode = LOCALBOARD; + if (service == CACHESERVICE) { + if (opcode == GDT_IOCTL) { + cmd_ptr->u.ioctl.subfunc = p1; + cmd_ptr->u.ioctl.channel = p2; + cmd_ptr->u.ioctl.param_size = (ushort)p3; + cmd_ptr->u.ioctl.p_param = virt_to_bus(ha->pscratch); + } else { + cmd_ptr->u.cache.DeviceNo = (ushort)p1; + cmd_ptr->u.cache.BlockNo = p2; + } + } else if (service == SCSIRAWSERVICE) { + cmd_ptr->u.raw.direction = p1; + cmd_ptr->u.raw.bus = (unchar)p2; + cmd_ptr->u.raw.target = (unchar)p3; + cmd_ptr->u.raw.lun = 0; + } + ha->cmd_len = sizeof(gdth_cmd_str); + ha->cmd_offs_dpmem = 0; + ha->cmd_cnt = 0; + gdth_copy_command(hanum); + gdth_release_event(hanum); + JIFFYWAIT(2); + if (!gdth_wait(hanum,index,INIT_TIMEOUT)) { + printk("GDT: Initialization error (timeout service %d)\n",service); + return 0; + } + if (ha->status != S_BSY || --retries == 0) + break; + udelay(1000); + } + + return (ha->status != S_OK ? 0:1); +} + + +/* search for devices */ + +static int gdth_search_drives(int hanum) +{ + register gdth_ha_str *ha; + ushort cdev_cnt,i; + unchar b,t,pos_found; + ulong drv_cyls, drv_hds, drv_secs; + ulong bus_no; + gdth_getch_str *chn; + + TRACE(("gdth_search_drives() hanum %d\n",hanum)); + ha = HADATA(gdth_ctr_tab[hanum]); + + /* initialize controller services, at first: screen service */ + if (!gdth_internal_cmd(hanum,SCREENSERVICE,GDT_INIT,0,0,0)) { + printk("GDT: Initialization error screen service (code %d)\n", + ha->status); + return 0; + } + TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n")); + + /* initialize cache service */ + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) { + printk("GDT: Initialization error cache service (code %d)\n", + ha->status); + return 0; + } + TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n")); + cdev_cnt = (ushort)ha->info; + + /* mount all cache devices */ + gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0); + TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n")); + + /* initialize cache service after mountall */ + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) { + printk("GDT: Initialization error cache service (code %d)\n", + ha->status); + return 0; + } + TRACE2(("gdth_search_drives() CACHES. init. after mountall\n")); + cdev_cnt = (ushort)ha->info; + + /* detect number of SCSI buses */ + chn = (gdth_getch_str *)DMADATA(gdth_ctr_tab[hanum]); + for (bus_no=0; bus_nochannel_no = bus_no; + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL, + SCSI_CHAN_CNT | L_CTRL_PATTERN, + IO_CHANNEL | INVALID_CHANNEL, + sizeof(gdth_getch_str))) { + if (bus_no == 0) { + printk("GDT: Error detecting SCSI channel count (0x%x)\n", + ha->status); + return 0; + } + break; + } + if (chn->siop_id < MAXID) + ha->id[bus_no][chn->siop_id].type = SIOP_DTYP; + } + ha->bus_cnt = (unchar)bus_no; + TRACE2(("gdth_search_drives() %d SCSI channels\n",ha->bus_cnt)); + + /* read cache configuration */ + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,CACHE_INFO, + INVALID_CHANNEL,sizeof(gdth_cinfo_str))) { + printk("GDT: Initialization error cache service (code %d)\n", + ha->status); + return 0; + } + ha->cpar = ((gdth_cinfo_str *)DMADATA(gdth_ctr_tab[hanum]))->cpar; + TRACE2(("gdth_search_drives() cinfo: vs %lx sta %d str %d dw %d b %d\n", + ha->cpar.version,ha->cpar.state,ha->cpar.strategy, + ha->cpar.write_back,ha->cpar.block_size)); + + /* initialize raw service */ + if (!gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INIT,0,0,0)) { + printk("GDT: Initialization error raw service (code %d)\n", + ha->status); + return 0; + } + TRACE2(("gdth_search_drives(): RAWSERVICE initialized\n")); + + /* set/get features raw service (scatter/gather) */ + ha->raw_feat = 0; + if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_SET_FEAT,SCATTER_GATHER, + 0,0)) { + TRACE2(("gdth_search_drives(): set features RAWSERVICE OK\n")); + if (gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_GET_FEAT,0,0,0)) + { + TRACE2(("gdth_search_dr(): get feat RAWSERVICE %ld\n", + ha->info)); + ha->raw_feat = (ushort)ha->info; + } + } + + /* set/get features cache service (equal to raw service) */ + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_SET_FEAT,0, + SCATTER_GATHER,0)) { + TRACE2(("gdth_search_drives(): set features CACHESERVICE OK\n")); + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_GET_FEAT,0,0,0)) { + TRACE2(("gdth_search_dr(): get feat CACHESERV. %ld\n", + ha->info)); + ha->cache_feat = (ushort)ha->info; + } + } + + /* scanning for raw devices */ + for (b=0; bbus_cnt; ++b) { + for (t=0; tid[b][t].type != SIOP_DTYP && + gdth_internal_cmd(hanum,SCSIRAWSERVICE,GDT_INFO,0,b,t)) { + ha->id[b][t].type = RAW_DTYP; + } + } + } + + /* scanning for cache devices */ + for (i=0; ibus_cnt; ++b) { + for (t=0; tid[b][t].type == EMPTY_DTYP) { + pos_found = TRUE; + break; + } + } + if (pos_found) + break; + } + TRACE(("gdth_search_dr() drive %d free pos at bus/id %d/%d\n", + i,b,t)); + + ha->id[b][t].type = CACHE_DTYP; + ha->id[b][t].devtype = 0; + ha->id[b][t].size = ha->info; + ha->id[b][t].hostdrive = i; + + /* evaluate mapping (sectors per head, heads per cylinder) */ + ha->id[b][t].size &= ~SECS32; + if (ha->info2 == 0) { + drv_cyls = ha->id[b][t].size /HEADS/SECS; + if (drv_cyls <= MAXCYLS) { + drv_hds = HEADS; + drv_secs= SECS; + } else { /* too high for 64*32 */ + drv_cyls = ha->id[b][t].size /MEDHEADS/MEDSECS; + if (drv_cyls <= MAXCYLS) { + drv_hds = MEDHEADS; + drv_secs= MEDSECS; + } else { /* too high for 127*63 */ + drv_cyls = ha->id[b][t].size /BIGHEADS/BIGSECS; + drv_hds = BIGHEADS; + drv_secs= BIGSECS; + } + } + } else { + drv_hds = ha->info2 & 0xff; + drv_secs = (ha->info2 >> 8) & 0xff; + drv_cyls = ha->id[b][t].size /drv_hds/drv_secs; + } + ha->id[b][t].heads = (unchar)drv_hds; + ha->id[b][t].secs = (unchar)drv_secs; + /* round size */ + ha->id[b][t].size = drv_cyls * drv_hds * drv_secs; + TRACE2(("gdth_search_dr() cdr. %d size %ld hds %ld scs %ld\n", + i,ha->id[b][t].size,drv_hds,drv_secs)); + + /* get informations about device */ + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i, + 0,0)) { + TRACE(("gdth_search_dr() cache drive %d devtype %ld\n", + i,ha->info)); + ha->id[b][t].devtype = (ushort)ha->info; + } + } + } + + TRACE(("gdth_search_drives() OK\n")); + return 1; +} + + +/* command queueing/sending functions */ + +static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority) +{ + register gdth_ha_str *ha; + register Scsi_Cmnd *pscp; + register Scsi_Cmnd *nscp; + ulong flags; + unchar b, t; + + TRACE(("gdth_putq() priority %d\n",priority)); + save_flags(flags); + cli(); + + ha = HADATA(gdth_ctr_tab[hanum]); + scp->SCp.this_residual = (int)priority; + gdth_update_timeout(scp, scp->timeout * 6); +#if LINUX_VERSION_CODE >= 0x020000 + b = scp->channel; +#else + b = NUMDATA(nscp->host)->busnum; +#endif + t = scp->target; +#if LINUX_VERSION_CODE >= 0x010300 + if (priority >= DEFAULT_PRI && ha->id[b][t].lock) { + TRACE2(("gdth_putq(): locked IO -> update_timeout()\n")); + scp->SCp.buffers_residual = gdth_update_timeout(scp, 0); + } +#endif + + if (ha->req_first==NULL) { + ha->req_first = scp; /* queue was empty */ + scp->SCp.ptr = NULL; + } else { /* queue not empty */ + pscp = ha->req_first; + nscp = (Scsi_Cmnd *)pscp->SCp.ptr; + /* priority: 0-highest,..,0xff-lowest */ + while (nscp && (unchar)nscp->SCp.this_residual <= priority) { + pscp = nscp; + nscp = (Scsi_Cmnd *)pscp->SCp.ptr; + } + pscp->SCp.ptr = (char *)scp; + scp->SCp.ptr = (char *)nscp; + } + restore_flags(flags); + +#ifdef GDTH_STATISTICS + flags = 0; + for (nscp=ha->req_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr) + ++flags; + if (max_rq < flags) { + max_rq = flags; + TRACE3(("GDT: max_rq = %d\n",(ushort)max_rq)); + } +#endif +} + +static void gdth_next(int hanum) +{ + register gdth_ha_str *ha; + register Scsi_Cmnd *pscp; + register Scsi_Cmnd *nscp; + unchar b, t, next_cmd, firsttime; + ushort hdrive; + ulong flags; + int cmd_index; + + TRACE(("gdth_next() hanum %d\n",hanum)); + save_flags(flags); + cli(); + + ha = HADATA(gdth_ctr_tab[hanum]); + ha->cmd_cnt = ha->cmd_offs_dpmem = 0; + next_cmd = firsttime = TRUE; + cmd_index = 0; + + for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) { + if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr) + pscp = (Scsi_Cmnd *)pscp->SCp.ptr; +#if LINUX_VERSION_CODE >= 0x020000 + b = nscp->channel; +#else + b = NUMDATA(nscp->host)->busnum; +#endif + t = nscp->target; + if (nscp->SCp.this_residual < DEFAULT_PRI || !ha->id[b][t].lock) { + + if (firsttime) { + if (gdth_test_busy(hanum)) { /* controller busy ? */ + TRACE(("gdth_next() controller %d busy !\n",hanum)); + if (!gdth_polling) { + restore_flags(flags); + return; + } + while (gdth_test_busy(hanum)) + udelay(1000); + } + firsttime = FALSE; + } + +#if LINUX_VERSION_CODE >= 0x010300 + if (nscp->done == gdth_scsi_done) { + if (!(cmd_index=gdth_special_cmd(hanum,nscp,b))) + next_cmd = FALSE; + } else +#endif + if (ha->id[b][t].type != CACHE_DTYP) { + if (!(cmd_index=gdth_fill_raw_cmd(hanum,nscp,b))) + next_cmd = FALSE; + } else { + hdrive = ha->id[b][t].hostdrive; + switch (nscp->cmnd[0]) { + case TEST_UNIT_READY: + case INQUIRY: + case REQUEST_SENSE: + case READ_CAPACITY: + case VERIFY: + case START_STOP: + case MODE_SENSE: + TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0], + nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3], + nscp->cmnd[4],nscp->cmnd[5])); + gdth_internal_cache_cmd(hanum,nscp,b,&flags); + break; + + case ALLOW_MEDIUM_REMOVAL: + TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0], + nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3], + nscp->cmnd[4],nscp->cmnd[5])); + if ( (nscp->cmnd[4]&1) && !(ha->id[b][t].devtype&1) ) { + TRACE2(("Prevent r. nonremov. drive->do nothing\n")); + nscp->result = DID_OK << 16; + restore_flags( flags ); + nscp->scsi_done(nscp); + save_flags( flags ); + cli(); + } else { + nscp->cmnd[3] = (ha->id[b][t].devtype&1) ? 1:0; + TRACE2(("Prevent/allow r. %d rem. drive %d\n", + nscp->cmnd[4],nscp->cmnd[3])); + if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive))) + next_cmd = FALSE; + } + break; + + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,hdrive))) + next_cmd = FALSE; + break; + + default: + TRACE2(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0], + nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3], + nscp->cmnd[4],nscp->cmnd[5])); + printk("GDT: Unknown SCSI command 0x%x to cache service !\n", + nscp->cmnd[0]); + nscp->result = DID_ABORT << 16; + restore_flags( flags ); + nscp->scsi_done( nscp ); + save_flags( flags ); + cli(); + break; + } + } + + if (!next_cmd) + break; + if (nscp == ha->req_first) + ha->req_first = pscp = (Scsi_Cmnd *)nscp->SCp.ptr; + else + pscp->SCp.ptr = nscp->SCp.ptr; + if (gdth_polling) + break; + } + } + + if (ha->cmd_cnt > 0) { + gdth_release_event(hanum); + } + + restore_flags(flags); + + if (gdth_polling && ha->cmd_cnt > 0) { + if (!gdth_wait(hanum,cmd_index,POLL_TIMEOUT)) + printk("GDT: Controller %d: Command %d timed out !\n", + hanum,cmd_index); + } +} + +static void gdth_copy_internal_data(Scsi_Cmnd *scp,char *buffer,ushort count) +{ + ushort cpcount,i; + ushort cpsum,cpnow; + struct scatterlist *sl; + + cpcount = count<=(ushort)scp->bufflen ? count:(ushort)scp->bufflen; + if (scp->use_sg) { + sl = (struct scatterlist *)scp->request_buffer; + for (i=0,cpsum=0; iuse_sg; ++i,++sl) { + cpnow = (ushort)sl->length; + TRACE(("copy_internal() now %d sum %d count %d %d\n", + cpnow,cpsum,cpcount,(ushort)scp->bufflen)); + if (cpsum+cpnow > cpcount) + cpnow = cpcount - cpsum; + cpsum += cpnow; + memcpy((char*)sl->address,buffer,cpnow); + if (cpsum == cpcount) + break; + buffer += cpnow; + } + } else { + TRACE(("copy_internal() count %d\n",cpcount)); + memcpy((char*)scp->request_buffer,buffer,cpcount); + } +} + +static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp, + unchar b,ulong *flags) +{ + register gdth_ha_str *ha; + ushort hdrive; + unchar t; + gdth_inq_data inq; + gdth_rdcap_data rdc; + gdth_sense_data sd; + gdth_modep_data mpd; + + ha = HADATA(gdth_ctr_tab[hanum]); + t = scp->target; + hdrive = ha->id[b][t].hostdrive; + TRACE(("gdth_internal_cache_cmd() cmd 0x%x hdrive %d\n", + scp->cmnd[0],hdrive)); + + if (scp->lun !=0) + scp->result = DID_BAD_TARGET << 16; + else { + switch (scp->cmnd[0]) { + case TEST_UNIT_READY: + case VERIFY: + case START_STOP: + TRACE2(("Test/Verify/Start hdrive %d\n",hdrive)); + break; + + case INQUIRY: + TRACE2(("Inquiry hdrive %d devtype %d\n", + hdrive,ha->id[b][t].devtype)); + inq.type_qual = (ha->id[b][t].devtype&4) ? TYPE_ROM:TYPE_DISK; + /* you can here set all disks to removable, if you want to do + a flush using the ALLOW_MEDIUM_REMOVAL command */ + inq.modif_rmb = ha->id[b][t].devtype&1 ? 0x80:0x00; + inq.version = 2; + inq.resp_aenc = 2; + inq.add_length= 32; + strcpy(inq.vendor,"ICP "); + sprintf(inq.product,"Host Drive #%02d",hdrive); + strcpy(inq.revision," "); + gdth_copy_internal_data(scp,(char*)&inq,sizeof(gdth_inq_data)); + break; + + case REQUEST_SENSE: + TRACE2(("Request sense hdrive %d\n",hdrive)); + sd.errorcode = 0x70; + sd.segno = 0x00; + sd.key = NO_SENSE; + sd.info = 0; + sd.add_length= 0; + gdth_copy_internal_data(scp,(char*)&sd,sizeof(gdth_sense_data)); + break; + + case MODE_SENSE: + TRACE2(("Mode sense hdrive %d\n",hdrive)); + memset((char*)&mpd,0,sizeof(gdth_modep_data)); + mpd.hd.data_length = sizeof(gdth_modep_data); + mpd.hd.dev_par = (ha->id[b][t].devtype&2) ? 0x80:0; + mpd.hd.bd_length = sizeof(mpd.bd); + mpd.bd.block_length[0] = (SECTOR_SIZE & 0x00ff0000) >> 16; + mpd.bd.block_length[1] = (SECTOR_SIZE & 0x0000ff00) >> 8; + mpd.bd.block_length[2] = (SECTOR_SIZE & 0x000000ff); + gdth_copy_internal_data(scp,(char*)&mpd,sizeof(gdth_modep_data)); + break; + + case READ_CAPACITY: + TRACE2(("Read capacity hdrive %d\n",hdrive)); + rdc.last_block_no = ntohl(ha->id[b][t].size-1); + rdc.block_length = ntohl(SECTOR_SIZE); + gdth_copy_internal_data(scp,(char*)&rdc,sizeof(gdth_rdcap_data)); + break; + + default: + TRACE2(("Internal cache cmd 0x%x unknown\n",scp->cmnd[0])); + break; + } + scp->result = DID_OK << 16; + } + + restore_flags(*flags); + scp->scsi_done(scp); + save_flags(*flags); + cli(); + return 1; +} + +static int gdth_fill_cache_cmd(int hanum,Scsi_Cmnd *scp,ushort hdrive) +{ + register gdth_ha_str *ha; + register gdth_cmd_str *cmdp; + struct scatterlist *sl; + ushort i; + int cmd_index; + + ha = HADATA(gdth_ctr_tab[hanum]); + cmdp = ha->pccb; + TRACE(("gdth_fill_cache_cmd() cmd 0x%x cmdsize %d hdrive %d\n", + scp->cmnd[0],scp->cmd_len,hdrive)); + + if (ha->type==GDT_EISA && ha->cmd_cnt>0) + return 0; + + cmdp->Service = CACHESERVICE; + cmdp->RequestBuffer = scp; + /* search free command index */ + if (!(cmd_index=gdth_get_cmd_index(hanum))) { + TRACE(("GDT: No free command index found\n")); + return 0; + } + /* if it's the first command, set command semaphore */ + if (ha->cmd_cnt == 0) + gdth_set_sema0(hanum); + + /* fill command */ + if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) { + if (scp->cmnd[4] & 1) /* prevent ? */ + cmdp->OpCode = GDT_MOUNT; + else if (scp->cmnd[3] & 1) /* removable drive ? */ + cmdp->OpCode = GDT_UNMOUNT; + else + cmdp->OpCode = GDT_FLUSH; + } else { + if (scp->cmnd[0]==WRITE_6 || scp->cmnd[0]==WRITE_10) { + if (gdth_write_through) + cmdp->OpCode = GDT_WRITE_THR; + else + cmdp->OpCode = GDT_WRITE; + } else { + cmdp->OpCode = GDT_READ; + } + } + + cmdp->BoardNode = LOCALBOARD; + cmdp->u.cache.DeviceNo = hdrive; + + if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) { + cmdp->u.cache.BlockNo = 1; + cmdp->u.cache.sg_canz = 0; + } else { + if (scp->cmd_len != 6) { + cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[2]); + cmdp->u.cache.BlockCnt= (ulong)ntohs(*(ushort*)&scp->cmnd[7]); + } else { + cmdp->u.cache.BlockNo = ntohl(*(ulong*)&scp->cmnd[0]) & 0x001fffffUL; + cmdp->u.cache.BlockCnt= scp->cmnd[4]==0 ? 0x100 : scp->cmnd[4]; + } + + if (scp->use_sg) { + cmdp->u.cache.DestAddr= -1UL; + sl = (struct scatterlist *)scp->request_buffer; + for (i=0; iuse_sg; ++i,++sl) { + cmdp->u.cache.sg_lst[i].sg_ptr = virt_to_bus(sl->address); + cmdp->u.cache.sg_lst[i].sg_len = (ulong)sl->length; + } + cmdp->u.cache.sg_canz = (ulong)i; + +#ifdef GDTH_STATISTICS + if (max_sg < (ulong)i) { + max_sg = (ulong)i; + TRACE3(("GDT: max_sg = %d\n",i)); + } +#endif + if (iu.cache.sg_lst[i].sg_len = 0; + } else { + if (ha->cache_feat & SCATTER_GATHER) { + cmdp->u.cache.DestAddr = -1UL; + cmdp->u.cache.sg_canz = 1; + cmdp->u.cache.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer); + cmdp->u.cache.sg_lst[0].sg_len = scp->request_bufflen; + cmdp->u.cache.sg_lst[1].sg_len = 0; + } else { + cmdp->u.cache.DestAddr = virt_to_bus(scp->request_buffer); + cmdp->u.cache.sg_canz= 0; + } + } + } + TRACE(("cache cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n", + cmdp->u.cache.DestAddr,cmdp->u.cache.sg_canz, + cmdp->u.cache.sg_lst[0].sg_ptr, + cmdp->u.cache.sg_lst[0].sg_len)); + TRACE(("cache cmd: cmd %d blockno. %ld, blockcnt %ld\n", + cmdp->OpCode,cmdp->u.cache.BlockNo,cmdp->u.cache.BlockCnt)); + + /* evaluate command size, check space */ + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + + (ushort)cmdp->u.cache.sg_canz * sizeof(gdth_sg_str); + if (ha->cmd_len & 3) + ha->cmd_len += (4 - (ha->cmd_len & 3)); + + if (ha->cmd_cnt > 0) { + if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) > + ha->ic_all_size) { + TRACE2(("gdth_fill_cache() DPMEM overflow\n")); + gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND; + return 0; + } + } + + /* copy command */ + gdth_copy_command(hanum); + return cmd_index; +} + +static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b) +{ + register gdth_ha_str *ha; + register gdth_cmd_str *cmdp; + struct scatterlist *sl; + ushort i; + int cmd_index; + unchar t,l; + + ha = HADATA(gdth_ctr_tab[hanum]); + t = scp->target; + l = scp->lun; + cmdp = ha->pccb; + TRACE(("gdth_fill_raw_cmd() cmd 0x%x bus %d ID %d LUN %d\n", + scp->cmnd[0],b,t,l)); + + if (ha->type==GDT_EISA && ha->cmd_cnt>0) + return 0; + + cmdp->Service = SCSIRAWSERVICE; + cmdp->RequestBuffer = scp; + /* search free command index */ + if (!(cmd_index=gdth_get_cmd_index(hanum))) { + TRACE(("GDT: No free command index found\n")); + return 0; + } + /* if it's the first command, set command semaphore */ + if (ha->cmd_cnt == 0) + gdth_set_sema0(hanum); + + /* fill command */ + cmdp->OpCode = GDT_WRITE; /* always */ + cmdp->BoardNode = LOCALBOARD; + cmdp->u.raw.reserved = 0; + cmdp->u.raw.mdisc_time = 0; + cmdp->u.raw.mcon_time = 0; + cmdp->u.raw.clen = scp->cmd_len; + cmdp->u.raw.target = t; + cmdp->u.raw.lun = l; + cmdp->u.raw.bus = b; + cmdp->u.raw.priority = 0; + cmdp->u.raw.link_p = NULL; + cmdp->u.raw.sdlen = scp->request_bufflen; + cmdp->u.raw.sense_len = 16; + cmdp->u.raw.sense_data = virt_to_bus(scp->sense_buffer); + cmdp->u.raw.direction = + gdth_direction_tab[scp->cmnd[0]]==DOU ? DATA_OUT : DATA_IN; + memcpy(cmdp->u.raw.cmd,scp->cmnd,12); + + if (scp->use_sg) { + cmdp->u.raw.sdata = -1UL; + sl = (struct scatterlist *)scp->request_buffer; + for (i=0; iuse_sg; ++i,++sl) { + cmdp->u.raw.sg_lst[i].sg_ptr = virt_to_bus(sl->address); + cmdp->u.raw.sg_lst[i].sg_len = (ulong)sl->length; + } + cmdp->u.raw.sg_ranz = (ulong)i; + +#ifdef GDTH_STATISTICS + if (max_sg < (ulong)i) { + max_sg = (ulong)i; + TRACE3(("GDT: max_sg = %d\n",i)); + } +#endif + if (iu.raw.sg_lst[i].sg_len = 0; + } else { + if (ha->raw_feat & SCATTER_GATHER) { + cmdp->u.raw.sdata = -1UL; + cmdp->u.raw.sg_ranz= 1; + cmdp->u.raw.sg_lst[0].sg_ptr = virt_to_bus(scp->request_buffer); + cmdp->u.raw.sg_lst[0].sg_len = scp->request_bufflen; + cmdp->u.raw.sg_lst[1].sg_len = 0; + } else { + cmdp->u.raw.sdata = virt_to_bus(scp->request_buffer); + cmdp->u.raw.sg_ranz= 0; + } + } + TRACE(("raw cmd: addr. %lx sganz %lx sgptr0 %lx sglen0 %lx\n", + cmdp->u.raw.sdata,cmdp->u.raw.sg_ranz, + cmdp->u.raw.sg_lst[0].sg_ptr, + cmdp->u.raw.sg_lst[0].sg_len)); + + /* evaluate command size, check space */ + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) + + (ushort)cmdp->u.raw.sg_ranz * sizeof(gdth_sg_str); + if (ha->cmd_len & 3) + ha->cmd_len += (4 - (ha->cmd_len & 3)); + + if (ha->cmd_cnt > 0) { + if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) > + ha->ic_all_size) { + TRACE2(("gdth_fill_raw() DPMEM overflow\n")); + gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND; + return 0; + } + } + + /* copy command */ + gdth_copy_command(hanum); + return cmd_index; +} + +static int gdth_special_cmd(int hanum,Scsi_Cmnd *scp,unchar b) +{ + register gdth_ha_str *ha; + register gdth_cmd_str *cmdp; + int cmd_index; + + ha = HADATA(gdth_ctr_tab[hanum]); + cmdp= ha->pccb; + TRACE2(("gdth_special_cmd(): ")); + + if (ha->type==GDT_EISA && ha->cmd_cnt>0) + return 0; + + memcpy( cmdp, scp->request_buffer, sizeof(gdth_cmd_str)); + cmdp->RequestBuffer = scp; + + /* search free command index */ + if (!(cmd_index=gdth_get_cmd_index(hanum))) { + TRACE(("GDT: No free command index found\n")); + return 0; + } + + /* if it's the first command, set command semaphore */ + if (ha->cmd_cnt == 0) + gdth_set_sema0(hanum); + + /* evaluate command size, check space */ + if (cmdp->OpCode == GDT_IOCTL) { + TRACE2(("IOCTL\n")); + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.ioctl.p_param) + sizeof(ulong); + } else if (cmdp->Service == CACHESERVICE) { + TRACE2(("cache command %d\n",cmdp->OpCode)); + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.cache.sg_lst) + sizeof(gdth_sg_str); + } else if (cmdp->Service == SCSIRAWSERVICE) { + TRACE2(("raw command %d/%d\n",cmdp->OpCode,cmdp->u.raw.cmd[0])); + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.raw.sg_lst) + sizeof(gdth_sg_str); + } + + if (ha->cmd_len & 3) + ha->cmd_len += (4 - (ha->cmd_len & 3)); + + if (ha->cmd_cnt > 0) { + if ((ha->cmd_offs_dpmem + ha->cmd_len + DPMEM_COMMAND_OFFSET) > + ha->ic_all_size) { + TRACE2(("gdth_special_cmd() DPMEM overflow\n")); + gdth_cmd_tab[cmd_index-2][hanum].cmnd = UNUSED_CMND; + return 0; + } + } + + /* copy command */ + gdth_copy_command(hanum); + return cmd_index; +} + + +/* Controller event handling functions */ +static gdth_evt_str *gdth_store_event(ushort source, ushort idx, + gdth_evt_data *evt) +{ + gdth_evt_str *e; + ulong flags; + struct timeval tv; + + TRACE2(("gdth_store_event() source %d idx %d\n", source, idx)); + if (source == 0) /* no source -> no event */ + return 0; + + save_flags(flags); + cli(); + if (ebuffer[elastidx].event_source == source && + ebuffer[elastidx].event_idx == idx && + !memcmp((char *)&ebuffer[elastidx].event_data.eu, + (char *)&evt->eu, evt->size)) { + e = &ebuffer[elastidx]; + do_gettimeofday(&tv); + e->last_stamp = tv.tv_sec; + ++e->same_count; + } else { + if (ebuffer[elastidx].event_source != 0) { /* entry not free ? */ + ++elastidx; + if (elastidx == MAX_EVENTS) + elastidx = 0; + if (elastidx == eoldidx) { /* reached mark ? */ + ++eoldidx; + if (eoldidx == MAX_EVENTS) + eoldidx = 0; + } + } + e = &ebuffer[elastidx]; + e->event_source = source; + e->event_idx = idx; + do_gettimeofday(&tv); + e->first_stamp = e->last_stamp = tv.tv_sec; + e->same_count = 1; + e->event_data = *evt; + } + restore_flags(flags); + return e; +} + +static int gdth_read_event(int handle, gdth_evt_str *estr) +{ + gdth_evt_str *e; + int eindex; + ulong flags; + + TRACE2(("gdth_read_event() handle %d\n", handle)); + save_flags(flags); + cli(); + if (handle == -1) + eindex = eoldidx; + else + eindex = handle; + estr->event_source = 0; + + if (eindex >= MAX_EVENTS) { + restore_flags(flags); + return eindex; + } + e = &ebuffer[eindex]; + if (e->event_source != 0) { + if (eindex != elastidx) { + if (++eindex == MAX_EVENTS) + eindex = 0; + } else { + eindex = -1; + } + memcpy(estr, e, sizeof(gdth_evt_str)); + } + restore_flags(flags); + return eindex; +} + +static void gdth_readapp_event(unchar application, gdth_evt_str *estr) +{ + gdth_evt_str *e; + int eindex; + ulong flags; + unchar found = FALSE; + + TRACE2(("gdth_readapp_event() app. %d\n", application)); + save_flags(flags); + cli(); + eindex = eoldidx; + for (;;) { + e = &ebuffer[eindex]; + if (e->event_source == 0) + break; + if ((e->application & application) == 0) { + e->application |= application; + found = TRUE; + break; + } + if (eindex == elastidx) + break; + if (++eindex == MAX_EVENTS) + eindex = 0; + } + if (found) + memcpy(estr, e, sizeof(gdth_evt_str)); + else + estr->event_source = 0; + restore_flags(flags); +} + +static void gdth_clear_events() +{ + ulong flags; + + TRACE(("gdth_clear_events()")); + save_flags(flags); + cli(); + + eoldidx = elastidx = 0; + ebuffer[0].event_source = 0; + restore_flags(flags); +} + + +/* SCSI interface functions */ + +#if LINUX_VERSION_CODE >= 0x010346 +static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs) +#else +static void gdth_interrupt(int irq,struct pt_regs *regs) +#endif +{ + register gdth_ha_str *ha; + gdt6m_dpram_str *dp6m_ptr; + gdt6_dpram_str *dp6_ptr; + gdt2_dpram_str *dp2_ptr; + Scsi_Cmnd *scp; + int hanum; + unchar IStatus; + ushort CmdStatus, Service = 0; + ulong InfoBytes, InfoBytes2 = 0; + gdth_evt_data dvr; + + TRACE(("gdth_interrupt() IRQ %d\n",irq)); + + /* if polling and not from gdth_wait() -> return */ + if (gdth_polling) { + if (!gdth_from_wait) { + return; + } + } + + wait_index = 0; + + /* search controller */ + if ((hanum = gdth_get_status(&IStatus,irq)) == -1) { + /* + TRACE2(("gdth_interrupt(): Spurious interrupt received\n")); + */ + return; + } + +#ifdef GDTH_STATISTICS + ++act_ints; +#endif + + ha = HADATA(gdth_ctr_tab[hanum]); + if (ha->type == GDT_EISA) { + if (IStatus & 0x80) { /* error flag */ + IStatus &= ~0x80; + CmdStatus = inw(ha->bmic + MAILBOXREG+8); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); + if (IStatus == ASYNCINDEX) { /* async. event ? */ + Service = inw(ha->bmic + MAILBOXREG+10); + InfoBytes2 = inl(ha->bmic + MAILBOXREG+4); + } + } else /* no error */ + CmdStatus = S_OK; + InfoBytes = inl(ha->bmic + MAILBOXREG+12); + if (gdth_polling) /* init. -> more info */ + InfoBytes2 = inl(ha->bmic + MAILBOXREG+4); + outb(0xff, ha->bmic + EDOORREG); /* acknowledge interrupt */ + outb(0x00, ha->bmic + SEMA1REG); /* reset status semaphore */ + } else if (ha->type == GDT_ISA) { + dp2_ptr = (gdt2_dpram_str *)ha->brd; + if (IStatus & 0x80) { /* error flag */ + IStatus &= ~0x80; + CmdStatus = readw(&dp2_ptr->u.ic.Status); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); + if (IStatus == ASYNCINDEX) { /* async. event ? */ + Service = readw(&dp2_ptr->u.ic.Service); + InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]); + } + } else /* no error */ + CmdStatus = S_OK; + InfoBytes = readl(&dp2_ptr->u.ic.Info[0]); + if (gdth_polling) /* init. -> more info */ + InfoBytes2 = readl(&dp2_ptr->u.ic.Info[1]); + writeb(0xff, &dp2_ptr->io.irqdel); /* acknowledge interrupt */ + writeb(0, &dp2_ptr->u.ic.Cmd_Index); /* reset command index */ + writeb(0, &dp2_ptr->io.Sema1); /* reset status semaphore */ + } else if (ha->type == GDT_PCI) { + dp6_ptr = (gdt6_dpram_str *)ha->brd; + if (IStatus & 0x80) { /* error flag */ + IStatus &= ~0x80; + CmdStatus = readw(&dp6_ptr->u.ic.Status); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); + if (IStatus == ASYNCINDEX) { /* async. event ? */ + Service = readw(&dp6_ptr->u.ic.Service); + InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]); + } + } else /* no error */ + CmdStatus = S_OK; + InfoBytes = readl(&dp6_ptr->u.ic.Info[0]); + if (gdth_polling) /* init. -> more info */ + InfoBytes2 = readl(&dp6_ptr->u.ic.Info[1]); + writeb(0xff, &dp6_ptr->io.irqdel); /* acknowledge interrupt */ + writeb(0, &dp6_ptr->u.ic.Cmd_Index); /* reset command index */ + writeb(0, &dp6_ptr->io.Sema1); /* reset status semaphore */ + } else if (ha->type == GDT_PCINEW) { + if (IStatus & 0x80) { /* error flag */ + IStatus &= ~0x80; + CmdStatus = inw(PTR2USHORT(&ha->plx->status)); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); + if (IStatus == ASYNCINDEX) { /* async. event ? */ + Service = inw(PTR2USHORT(&ha->plx->service)); + InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1])); + } + } else + CmdStatus = S_OK; + + InfoBytes = inl(PTR2USHORT(&ha->plx->info[0])); + if (gdth_polling) /* init. -> more info */ + InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1])); + outb(0xff, PTR2USHORT(&ha->plx->edoor_reg)); + outb(0x00, PTR2USHORT(&ha->plx->sema1_reg)); + } else if (ha->type == GDT_PCIMPR) { + dp6m_ptr = (gdt6m_dpram_str *)ha->brd; + if (IStatus & 0x80) { /* error flag */ + IStatus &= ~0x80; + CmdStatus = readw(&dp6m_ptr->i960r.status); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); + if (IStatus == ASYNCINDEX) { /* async. event ? */ + Service = readw(&dp6m_ptr->i960r.service); + InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]); + } + } else /* no error */ + CmdStatus = S_OK; + InfoBytes = readl(&dp6m_ptr->i960r.info[0]); + if (gdth_polling) /* init. -> more info */ + InfoBytes2 = readl(&dp6m_ptr->i960r.info[1]); + writeb(0xff, &dp6m_ptr->i960r.edoor_reg); + writeb(0, &dp6m_ptr->i960r.sema1_reg); + } else { + TRACE2(("gdth_interrupt() unknown controller type\n")); + return; + } + + TRACE(("gdth_interrupt() index %d stat %d info %ld\n", + IStatus,CmdStatus,InfoBytes)); + ha->status = CmdStatus; + ha->info = InfoBytes; + ha->info2 = InfoBytes2; + + if (gdth_from_wait) { + wait_hanum = hanum; + wait_index = (int)IStatus; + } + + if (IStatus == ASYNCINDEX) { + TRACE2(("gdth_interrupt() async. event\n")); + gdth_async_event(hanum,Service); + } else { + if (IStatus == SPEZINDEX) { + TRACE2(("Service unknown or not initialized !\n")); + dvr.size = sizeof(dvr.eu.driver); + dvr.eu.driver.ionode = hanum; + gdth_store_event(ES_DRIVER, 4, &dvr); + return; + } + scp = gdth_cmd_tab[IStatus-2][hanum].cmnd; + Service = gdth_cmd_tab[IStatus-2][hanum].service; + gdth_cmd_tab[IStatus-2][hanum].cmnd = UNUSED_CMND; + if (scp == UNUSED_CMND) { + TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus)); + dvr.size = sizeof(dvr.eu.driver); + dvr.eu.driver.ionode = hanum; + dvr.eu.driver.index = IStatus; + gdth_store_event(ES_DRIVER, 1, &dvr); + return; + } + if (scp == INTERNAL_CMND) { + TRACE(("gdth_interrupt() answer to internal command\n")); + return; + } + TRACE(("gdth_interrupt() sync. status\n")); + gdth_sync_event(hanum,Service,IStatus,scp); + } + gdth_next(hanum); +} + +static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) +{ + register gdth_ha_str *ha; + gdth_msg_str *msg; + gdth_cmd_str *cmdp; + char c='\r'; + ushort i; + gdth_evt_data dvr; + + ha = HADATA(gdth_ctr_tab[hanum]); + cmdp = ha->pccb; + TRACE(("gdth_sync_event() scp %lx serv %d status %d\n", + (ulong)scp,service,ha->status)); + + if (service == SCREENSERVICE) { + msg = (gdth_msg_str *)ha->pscratch; + TRACE(("len: %ld, answer: %d, ext: %d, alen: %ld\n", + msg->msg_len,msg->msg_answer,msg->msg_ext,msg->msg_alen)); + if (msg->msg_len) + if (!(msg->msg_answer && msg->msg_ext)) { + msg->msg_text[msg->msg_len] = '\0'; + printk("%s",msg->msg_text); + } + + if (msg->msg_ext && !msg->msg_answer) { + while (gdth_test_busy(hanum)) + udelay(1); + cmdp->Service = SCREENSERVICE; + cmdp->RequestBuffer = SCREEN_CMND; + gdth_get_cmd_index(hanum); + gdth_set_sema0(hanum); + cmdp->OpCode = GDT_READ; + cmdp->BoardNode = LOCALBOARD; + cmdp->u.screen.reserved = 0; + cmdp->u.screen.msg_handle= msg->msg_handle; + cmdp->u.screen.msg_addr = (ulong)msg; + ha->cmd_offs_dpmem = 0; + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) + + sizeof(ulong); + ha->cmd_cnt = 0; + gdth_copy_command(hanum); + gdth_release_event(hanum); + return 1; + } + + if (msg->msg_answer && msg->msg_alen) { + for (i=0; imsg_alen && imsg_text[i] = c; + } + msg->msg_alen -= i; + if (c!='\r' && msg->msg_alen!=0) { + msg->msg_answer = 1; + msg->msg_ext = 1; + } else { + msg->msg_ext = 0; + msg->msg_answer = 0; + } + msg->msg_len = i; + while (gdth_test_busy(hanum)) + udelay(1); + cmdp->Service = SCREENSERVICE; + cmdp->RequestBuffer = SCREEN_CMND; + gdth_get_cmd_index(hanum); + gdth_set_sema0(hanum); + cmdp->OpCode = GDT_WRITE; + cmdp->BoardNode = LOCALBOARD; + cmdp->u.screen.reserved = 0; + cmdp->u.screen.msg_handle= msg->msg_handle; + cmdp->u.screen.msg_addr = (ulong)msg; + ha->cmd_offs_dpmem = 0; + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) + + sizeof(ulong); + ha->cmd_cnt = 0; + gdth_copy_command(hanum); + gdth_release_event(hanum); + return 1; + } + printk("\n"); + + } else { + scp->SCp.Message = (int)ha->status; + /* cache or raw service */ + if (ha->status == S_OK) { + scp->result = DID_OK << 16; + } else if (ha->status == S_BSY) { + TRACE2(("Controller busy -> retry !\n")); + gdth_putq(hanum,scp,DEFAULT_PRI); + return 1; + } else { + if (service == CACHESERVICE) { + memset((char*)scp->sense_buffer,0,16); + scp->sense_buffer[0] = 0x70; + scp->sense_buffer[2] = NOT_READY; + scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); + + if (scp->done != gdth_scsi_done) { + dvr.size = sizeof(dvr.eu.sync); + dvr.eu.sync.ionode = hanum; + dvr.eu.sync.service = service; + dvr.eu.sync.status = ha->status; + dvr.eu.sync.info = ha->info; + dvr.eu.sync.hostdrive = +#if LINUX_VERSION_CODE >= 0x020000 + ha->id[scp->channel][scp->target].hostdrive; +#else + ha->id[NUMDATA(scp->host)->busnum][scp->target].hostdrive; +#endif + if (ha->status >= 0x8000) + gdth_store_event(ES_SYNC, 0, &dvr); + else + gdth_store_event(ES_SYNC, service, &dvr); + } + } else { + if (ha->status!=S_RAW_SCSI || ha->status==S_RAW_ILL) { + scp->result = DID_BAD_TARGET << 16; + } else { + scp->result = (DID_OK << 16) | ha->info; + } + } + } + scp->SCp.have_data_in++; + scp->scsi_done(scp); + } + + return 1; +} + +static char *async_cache_tab[] = { +/* 0*/ "\011\000\002\002\002\004\002\006\004" + "GDT HA %u, service %u, async. status %u/%lu unknown", +/* 1*/ "\011\000\002\002\002\004\002\006\004" + "GDT HA %u, service %u, async. status %u/%lu unknown", +/* 2*/ "\005\000\002\006\004" + "GDT HA %u, Host Drive %lu not ready", +/* 3*/ "\005\000\002\006\004" + "GDT HA %u, Host Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced", +/* 4*/ "\005\000\002\006\004" + "GDT HA %u, mirror update on Host Drive %lu failed", +/* 5*/ "\005\000\002\006\004" + "GDT HA %u, Mirror Drive %lu failed", +/* 6*/ "\005\000\002\006\004" + "GDT HA %u, Mirror Drive %lu: REASSIGN not successful and/or data error on reassigned blocks. Drive may crash in the future and should be replaced", +/* 7*/ "\005\000\002\006\004" + "GDT HA %u, Host Drive %lu write protected", +/* 8*/ "\005\000\002\006\004" + "GDT HA %u, media changed in Host Drive %lu", +/* 9*/ "\005\000\002\006\004" + "GDT HA %u, Host Drive %lu is offline", +/*10*/ "\005\000\002\006\004" + "GDT HA %u, media change of Mirror Drive %lu", +/*11*/ "\005\000\002\006\004" + "GDT HA %u, Mirror Drive %lu is write protected", +/*12*/ "\005\000\002\006\004" + "GDT HA %u, general error on Host Drive %lu. Please check the devices of this drive!", +/*13*/ "\007\000\002\006\002\010\002" + "GDT HA %u, Array Drive %u: Cache Drive %u failed", +/*14*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: FAIL state entered", +/*15*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: error", +/*16*/ "\007\000\002\006\002\010\002" + "GDT HA %u, Array Drive %u: failed drive replaced by Cache Drive %u", +/*17*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: parity build failed", +/*18*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: drive rebuild failed", +/*19*/ "\007\000\002\010\002" + "GDT HA %u, Test of Hot Fix %u failed", +/*20*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: drive build finished successfully", +/*21*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: drive rebuild finished successfully", +/*22*/ "\007\000\002\006\002\010\002" + "GDT HA %u, Array Drive %u: Hot Fix %u activated", +/*23*/ "\005\000\002\006\002" + "GDT HA %u, Host Drive %u: processing of i/o aborted due to serious drive error", +/*24*/ "\005\000\002\010\002" + "GDT HA %u, mirror update on Cache Drive %u completed", +/*25*/ "\005\000\002\010\002" + "GDT HA %u, mirror update on Cache Drive %lu failed", +/*26*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: drive rebuild started", +/*27*/ "\005\000\002\012\001" + "GDT HA %u, Fault bus %u: SHELF OK detected", +/*28*/ "\005\000\002\012\001" + "GDT HA %u, Fault bus %u: SHELF not OK detected", +/*29*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug started", +/*30*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: new disk detected", +/*31*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: old disk detected", +/*32*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: plugging an active disk is illegal", +/*33*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: illegal device detected", +/*34*/ "\011\000\002\012\001\013\001\006\004" + "GDT HA %u, Fault bus %u, ID %u: insufficient disk capacity (%lu MB required)", +/*35*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: disk write protected", +/*36*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: disk not available", +/*37*/ "\007\000\002\012\001\006\004" + "GDT HA %u, Fault bus %u: swap detected (%lu)", +/*38*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug finished successfully", +/*39*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted due to user Hot Plug", +/*40*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug aborted", +/*41*/ "\007\000\002\012\001\013\001" + "GDT HA %u, Fault bus %u, ID %u: Auto Hot Plug for Hot Fix started", +/*42*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: drive build started", +/*43*/ "\003\000\002" + "GDT HA %u, DRAM parity error detected", +/*44*/ "\005\000\002\006\002" + "GDT HA %u, Mirror Drive %u: update started", +/*45*/ "\007\000\002\006\002\010\002" + "GDT HA %u, Mirror Drive %u: Hot Fix %u activated", +/*46*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: no matching Pool Hot Fix Drive available", +/*47*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: Pool Hot Fix Drive available", +/*48*/ "\005\000\002\006\002" + "GDT HA %u, Mirror Drive %u: no matching Pool Hot Fix Drive available", +/*49*/ "\005\000\002\006\002" + "GDT HA %u, Mirror Drive %u: Pool Hot Fix Drive available", +/*50*/ "\007\000\002\012\001\013\001" + "GDT HA %u, SCSI bus %u, ID %u: IGNORE_WIDE_RESIDUE message received", +/*51*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: expand started", +/*52*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: expand finished successfully", +/*53*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: expand failed", +/*54*/ "\003\000\002" + "GDT HA %u, CPU temperature critical", +/*55*/ "\003\000\002" + "GDT HA %u, CPU temperature OK", +/*56*/ "\005\000\002\006\004" + "GDT HA %u, Host drive %lu created", +/*57*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: expand restarted", +/*58*/ "\005\000\002\006\002" + "GDT HA %u, Array Drive %u: expand stopped", +}; + + +static int gdth_async_event(int hanum,int service) +{ + gdth_stackframe stack; + gdth_evt_data dvr; + char *f = NULL; + int i,j; + gdth_ha_str *ha; + gdth_msg_str *msg; + gdth_cmd_str *cmdp; + int cmd_index; + + ha = HADATA(gdth_ctr_tab[hanum]); + cmdp= ha->pccb; + msg = (gdth_msg_str *)ha->pscratch; + TRACE2(("gdth_async_event() ha %d serv %d\n", + hanum,service)); + + if (service == SCREENSERVICE) { + if (ha->status == MSG_REQUEST) { + while (gdth_test_busy(hanum)) + udelay(1); + cmdp->Service = SCREENSERVICE; + cmdp->RequestBuffer = SCREEN_CMND; + cmd_index = gdth_get_cmd_index(hanum); + gdth_set_sema0(hanum); + cmdp->OpCode = GDT_READ; + cmdp->BoardNode = LOCALBOARD; + cmdp->u.screen.reserved = 0; + cmdp->u.screen.msg_handle= MSG_INV_HANDLE; + cmdp->u.screen.msg_addr = (ulong)msg; + ha->cmd_offs_dpmem = 0; + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) + + sizeof(ulong); + ha->cmd_cnt = 0; + gdth_copy_command(hanum); + if (ha->type == GDT_EISA) + printk("[EISA slot %d] ",(ushort)ha->brd_phys); + else if (ha->type == GDT_ISA) + printk("[DPMEM 0x%4X] ",(ushort)ha->brd_phys); + else + printk("[PCI %d/%d] ",(ushort)(ha->brd_phys>>8), + (ushort)((ha->brd_phys>>3)&0x1f)); + gdth_release_event(hanum); + } + + } else { + dvr.size = sizeof(dvr.eu.async); + dvr.eu.async.ionode = hanum; + dvr.eu.async.service = service; + dvr.eu.async.status = ha->status; + dvr.eu.async.info = ha->info; + *(ulong *)dvr.eu.async.scsi_coord = ha->info2; + gdth_store_event(ES_ASYNC, service, &dvr); + + if (service==CACHESERVICE && INDEX_OK(ha->status,async_cache_tab)) { + TRACE2(("GDT: Async. event cache service, event no.: %d\n", + ha->status)); + + f = async_cache_tab[ha->status]; + + /* i: parameter to push, j: stack element to fill */ + for (j=0,i=1; i < f[0]; i+=2) { + switch (f[i+1]) { + case 4: + stack.b[j++] = *(ulong*)&dvr.eu.stream[(int)f[i]]; + break; + case 2: + stack.b[j++] = *(ushort*)&dvr.eu.stream[(int)f[i]]; + break; + case 1: + stack.b[j++] = *(unchar*)&dvr.eu.stream[(int)f[i]]; + break; + default: + break; + } + } + + printk(&f[f[0]],stack); printk("\n"); + + } else { + printk("GDT: Unknown async. event service %d event no. %d\n", + service,ha->status); + } + } + return 1; +} + +#ifdef GDTH_STATISTICS +void gdth_timeout(void) +{ + ulong flags,i; + Scsi_Cmnd *nscp; + gdth_ha_str *ha; + int hanum = 0; + + save_flags(flags); + cli(); + + for (act_stats=0,i=0; ireq_first; nscp; nscp=(Scsi_Cmnd*)nscp->SCp.ptr) + ++act_rq; + + TRACE2(("gdth_to(): ints %ld, ios %ld, act_stats %ld, act_rq %ld\n", + act_ints, act_ios, act_stats, act_rq)); + act_ints = act_ios = 0; + + timer_table[GDTH_TIMER].expires = jiffies + 30*HZ; + timer_active |= 1<irq,ha->drq); + + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) +#else + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) +#endif + { + printk("GDT-ISA: Unable to allocate IRQ\n"); + restore_flags(flags); + scsi_unregister(shp); + continue; + } + if (request_dma(ha->drq,"gdth")) { + printk("GDT-ISA: Unable to allocate DMA channel\n"); +#if LINUX_VERSION_CODE >= 0x010346 + free_irq(ha->irq,NULL); +#else + free_irq(ha->irq); +#endif + restore_flags(flags); + scsi_unregister(shp); + continue; + } + set_dma_mode(ha->drq,DMA_MODE_CASCADE); + enable_dma(ha->drq); + shp->unchecked_isa_dma = 1; + shp->irq = ha->irq; + shp->dma_channel = ha->drq; + for (i=0; iid[0][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + hanum = gdth_ctr_count; + gdth_ctr_tab[gdth_ctr_count++] = shp; + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum= 0; + + ha->pccb = CMDDATA(shp); + ha->pscratch = DMADATA(shp); + ha->req_first = NULL; + for (i=0; iid[i][j].type = EMPTY_DTYP; + ha->id[i][j].lock = 0; + } + } + restore_flags(flags); + + if (!gdth_search_drives(hanum)) { + printk("GDT-ISA: Error during device scan\n"); + --gdth_ctr_count; + --gdth_ctr_vcount; + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + free_irq(ha->irq,NULL); +#else + free_irq(ha->irq); +#endif + restore_flags(flags); + scsi_unregister(shp); + continue; + } + +#if LINUX_VERSION_CODE >= 0x020000 + shp->max_id = 8; + shp->max_lun = 8; + shp->max_channel = ha->bus_cnt - 1; +#else + /* register addit. SCSI channels as virtual controllers */ + for (b=1; bbus_cnt; ++b) { + shp = scsi_register(shtp,sizeof(gdth_num_str)); + shp->unchecked_isa_dma = 1; + shp->irq = ha->irq; + shp->dma_channel = ha->drq; + for (i=0; iid[b][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum = b; + } +#endif + + gdth_enable_int(hanum); + } + } + + /* scanning for EISA controllers */ + for (eisa_slot=0x1000; eisa_slot<=0x8000; eisa_slot+=0x1000) { + if (gdth_search_eisa(eisa_slot)) { /* controller found */ + shp = scsi_register(shtp,sizeof(gdth_ext_str)); + ha = HADATA(shp); + if (!gdth_init_eisa(eisa_slot,ha)) { + scsi_unregister(shp); + continue; + } + /* controller found and initialized */ + printk("Configuring GDT-EISA HA at Slot %d IRQ %u\n", + eisa_slot>>12,ha->irq); + + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) +#else + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) +#endif + { + printk("GDT-EISA: Unable to allocate IRQ\n"); + restore_flags(flags); + scsi_unregister(shp); + continue; + } + shp->unchecked_isa_dma = 0; + shp->irq = ha->irq; + shp->dma_channel = 0xff; + for (i=0; iid[0][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + hanum = gdth_ctr_count; + gdth_ctr_tab[gdth_ctr_count++] = shp; + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum= 0; + TRACE2(("EISA detect Bus 0: shp %lx hanum %d\n", + (ulong)shp,NUMDATA(shp)->hanum)); + + ha->pccb = CMDDATA(shp); + ha->pscratch = DMADATA(shp); + ha->req_first = NULL; + for (i=0; iid[i][j].type = EMPTY_DTYP; + ha->id[i][j].lock = 0; + } + } + restore_flags(flags); + + if (!gdth_search_drives(hanum)) { + printk("GDT-EISA: Error during device scan\n"); + --gdth_ctr_count; + --gdth_ctr_vcount; + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + free_irq(ha->irq,NULL); +#else + free_irq(ha->irq); +#endif + restore_flags(flags); + scsi_unregister(shp); + continue; + } + +#if LINUX_VERSION_CODE >= 0x020000 + shp->max_id = 8; + shp->max_lun = 8; + shp->max_channel = ha->bus_cnt - 1; +#else + /* register addit. SCSI channels as virtual controllers */ + for (b=1; bbus_cnt; ++b) { + shp = scsi_register(shtp,sizeof(gdth_num_str)); + shp->unchecked_isa_dma = 0; + shp->irq = ha->irq; + shp->dma_channel = 0xff; + for (i=0; iid[b][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum = b; + TRACE2(("EISA detect Bus %d: shp %lx hanum %d\n", + NUMDATA(shp)->busnum,(ulong)shp, + NUMDATA(shp)->hanum)); + } +#endif + + gdth_enable_int(hanum); + } + } + + /* scanning for PCI controllers */ + for (device_id = 0; device_id <= PCI_DEVICE_ID_VORTEX_GDT6x21RP2; ++device_id) { + if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 && + device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP) + continue; + for (index = 0; ; ++index) { + if (!gdth_search_pci(device_id,index,&pcistr)) + break; /* next device_id */ + shp = scsi_register(shtp,sizeof(gdth_ext_str)); + ha = HADATA(shp); + if (!gdth_init_pci(&pcistr,ha)) { + scsi_unregister(shp); + continue; + } + /* controller found and initialized */ + printk("Configuring GDT-PCI HA at %d/%d IRQ %u\n", + pcistr.bus,pcistr.device_fn>>3,ha->irq); + + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth",NULL)) +#else + if (request_irq(ha->irq,gdth_interrupt,SA_INTERRUPT,"gdth")) +#endif + { + printk("GDT-PCI: Unable to allocate IRQ\n"); + restore_flags(flags); + scsi_unregister(shp); + continue; + } + shp->unchecked_isa_dma = 0; + shp->irq = ha->irq; + shp->dma_channel = 0xff; + for (i=0; iid[0][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + hanum = gdth_ctr_count; + gdth_ctr_tab[gdth_ctr_count++] = shp; + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum= 0; + + ha->pccb = CMDDATA(shp); + ha->pscratch = DMADATA(shp); + ha->req_first = NULL; + for (i=0; iid[i][j].type = EMPTY_DTYP; + ha->id[i][j].lock = 0; + } + } + restore_flags(flags); + + if (!gdth_search_drives(hanum)) { + printk("GDT-PCI: Error during device scan\n"); + --gdth_ctr_count; + --gdth_ctr_vcount; + save_flags(flags); + cli(); +#if LINUX_VERSION_CODE >= 0x010346 + free_irq(ha->irq,NULL); +#else + free_irq(ha->irq); +#endif + restore_flags(flags); + scsi_unregister(shp); + continue; + } + +#if LINUX_VERSION_CODE >= 0x020000 + shp->max_id = 8; + shp->max_lun = 8; + shp->max_channel = ha->bus_cnt - 1; +#else + /* register addit. SCSI channels as virtual controllers */ + for (b=1; bbus_cnt; ++b) { + shp = scsi_register(shtp,sizeof(gdth_num_str)); + shp->unchecked_isa_dma = 0; + shp->irq = ha->irq; + shp->dma_channel = 0xff; + for (i=0; iid[b][i].type==SIOP_DTYP) { + shp->this_id = i; + break; + } + } + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum = b; + } +#endif + + gdth_enable_int(hanum); + } + } + + TRACE2(("gdth_detect() %d controller detected\n",gdth_ctr_count)); + if (gdth_ctr_count > 0) { +#ifdef GDTH_STATISTICS + TRACE2(("gdth_detect(): Initializing timer !\n")); + timer_table[GDTH_TIMER].fn = gdth_timeout; + timer_table[GDTH_TIMER].expires = jiffies + HZ; + timer_active |= 1<= 0x020100 + register_reboot_notifier(&gdth_notifier); +#endif + } + gdth_polling = FALSE; + return gdth_ctr_vcount; +} + + +int gdth_release(struct Scsi_Host *shp) +{ + unsigned long flags; + + TRACE2(("gdth_release()\n")); + + save_flags(flags); + cli(); + if (NUMDATA(shp)->busnum == 0) { + if (shp->irq) { +#if LINUX_VERSION_CODE >= 0x010346 + free_irq(shp->irq,NULL); +#else + free_irq(shp->irq); +#endif + } + if (shp->dma_channel != 0xff) { + free_dma(shp->dma_channel); + } + } + + restore_flags(flags); + scsi_unregister(shp); + return 0; +} + + +static const char *gdth_ctr_name(int hanum) +{ + gdth_ha_str *ha; + + TRACE2(("gdth_ctr_name()\n")); + + ha = HADATA(gdth_ctr_tab[hanum]); + + if (ha->type == GDT_EISA) { + switch (ha->stype) { + case GDT3_ID: + return("GDT3000/3020 (EISA)"); + case GDT3A_ID: + return("GDT3000A/3020A/3050A (EISA)"); + case GDT3B_ID: + return("GDT3000B/3010A (EISA)"); + } + } else if (ha->type == GDT_ISA) { + return("GDT2000/2020 (ISA)"); + } else if (ha->type == GDT_PCI) { + switch (ha->stype) { + case PCI_DEVICE_ID_VORTEX_GDT60x0: + return("GDT6000/6020/6050 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6000B: + return("GDT6000B/6010 (PCI)"); + } + } else if (ha->type == GDT_PCINEW) { + switch (ha->stype) { + case PCI_DEVICE_ID_VORTEX_GDT6x10: + return("GDT6110/6510 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x20: + return("GDT6120/6520 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6530: + return("GDT6530 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6550: + return("GDT6550 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x17: + return("GDT6117/6517 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x27: + return("GDT6127/6527 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6537: + return("GDT6537 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6557: + return("GDT6557/6557-ECC (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x15: + return("GDT6115/6515 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x25: + return("GDT6125/6525 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6535: + return("GDT6535 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6555: + return("GDT6555/6555-ECC (PCI)"); + } + } else if (ha->type == GDT_PCIMPR) { + switch (ha->stype) { + case PCI_DEVICE_ID_VORTEX_GDT6x17RP: + return("GDT6117RP/GDT6517RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x27RP: + return("GDT6127RP/GDT6527RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6537RP: + return("GDT6537RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6557RP: + return("GDT6557RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x11RP: + return("GDT6111RP/GDT6511RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x21RP: + return("GDT6121RP/GDT6521RP (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x17RP1: + return("GDT6117RP1/GDT6517RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x27RP1: + return("GDT6127RP1/GDT6527RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6537RP1: + return("GDT6537RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6557RP1: + return("GDT6557RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x11RP1: + return("GDT6111RP1/GDT6511RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x21RP1: + return("GDT6121RP1/GDT6521RP1 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x17RP2: + return("GDT6117RP2/GDT6517RP2 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x27RP2: + return("GDT6127RP2/GDT6527RP2 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6537RP2: + return("GDT6537RP2 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6557RP2: + return("GDT6557RP2 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x11RP2: + return("GDT6111RP2/GDT6511RP2 (PCI)"); + case PCI_DEVICE_ID_VORTEX_GDT6x21RP2: + return("GDT6121RP2/GDT6521RP2 (PCI)"); + } + } + return(""); +} + +const char *gdth_info(struct Scsi_Host *shp) +{ + int hanum; + + TRACE2(("gdth_info()\n")); + hanum = NUMDATA(shp)->hanum; + + return (gdth_ctr_name(hanum)); +} + + +int gdth_abort(Scsi_Cmnd *scp) +{ + TRACE2(("gdth_abort() reason %d\n",scp->abort_reason)); + return SCSI_ABORT_SNOOZE; +} + +#if LINUX_VERSION_CODE >= 0x010346 +int gdth_reset(Scsi_Cmnd *scp, unsigned int reset_flags) +#else +int gdth_reset(Scsi_Cmnd *scp) +#endif +{ + TRACE2(("gdth_reset()\n")); + return SCSI_RESET_PUNT; +} + + +#if LINUX_VERSION_CODE >= 0x010300 +int gdth_bios_param(Disk *disk,kdev_t dev,int *ip) +#else +int gdth_bios_param(Disk *disk,int dev,int *ip) +#endif +{ + unchar b, t; + int hanum; + gdth_ha_str *ha; + + hanum = NUMDATA(disk->device->host)->hanum; + b = disk->device->channel; + t = disk->device->id; + TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", hanum, b, t)); + ha = HADATA(gdth_ctr_tab[hanum]); + + ip[0] = ha->id[b][t].heads; + ip[1] = ha->id[b][t].secs; + ip[2] = disk->capacity / ip[0] / ip[1]; + + TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n", + ip[0],ip[1],ip[2])); + return 0; +} + + +static void internal_done(Scsi_Cmnd *scp) +{ + scp->SCp.sent_command++; +} + +int gdth_command(Scsi_Cmnd *scp) +{ + TRACE2(("gdth_command()\n")); + + scp->SCp.sent_command = 0; + gdth_queuecommand(scp,internal_done); + + while (!scp->SCp.sent_command) + barrier(); + return scp->result; +} + + +int gdth_queuecommand(Scsi_Cmnd *scp,void (*done)(Scsi_Cmnd *)) +{ + int hanum; + int priority; + + TRACE(("gdth_queuecommand() cmd 0x%x id %d lun %d\n", + scp->cmnd[0],scp->target,scp->lun)); + + scp->scsi_done = (void *)done; + scp->SCp.have_data_in = 0; + hanum = NUMDATA(scp->host)->hanum; +#ifdef GDTH_STATISTICS + ++act_ios; +#endif + + priority = DEFAULT_PRI; +#if LINUX_VERSION_CODE >= 0x010300 + if (scp->done == gdth_scsi_done) + priority = scp->SCp.this_residual; +#endif + gdth_putq( hanum, scp, priority ); + gdth_next( hanum ); + return 0; +} + + +/* shutdown routine */ +#if LINUX_VERSION_CODE >= 0x020100 +static int gdth_halt(struct notifier_block *nb, ulong event, void *buf) +#else +void gdth_halt(void) +#endif +{ + int hanum, i, j; + gdth_ha_str *ha; + Scsi_Cmnd scp; + Scsi_Device sdev; + gdth_cmd_str gdtcmd; + char cmnd[12]; + +#if LINUX_VERSION_CODE >= 0x020100 + TRACE2(("gdth_halt() event %d\n",event)); + if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF) + return NOTIFY_DONE; +#else + TRACE2(("gdth_halt()\n")); +#endif + printk("GDT: Flushing all host drives .. "); + for (hanum = 0; hanum < gdth_ctr_count; ++hanum) { + ha = HADATA(gdth_ctr_tab[hanum]); + memset(&sdev,0,sizeof(Scsi_Device)); + memset(&scp, 0,sizeof(Scsi_Cmnd)); + sdev.host = gdth_ctr_tab[hanum]; + sdev.id = sdev.host->this_id; + scp.cmd_len = 12; + scp.host = gdth_ctr_tab[hanum]; + scp.target = sdev.host->this_id; + scp.device = &sdev; + scp.use_sg = 0; + + /* flush */ + for (i = 0; i < MAXBUS; ++i) { + for (j = 0; j < MAXID; ++j) { + if (ha->id[i][j].type == CACHE_DTYP) { + gdtcmd.BoardNode = LOCALBOARD; + gdtcmd.Service = CACHESERVICE; + gdtcmd.OpCode = GDT_FLUSH; + gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive; + gdtcmd.u.cache.BlockNo = 1; + gdtcmd.u.cache.sg_canz = 0; + TRACE2(("gdth_halt(): flush ha %d drive %d\n", + hanum, ha->id[i][j].hostdrive)); + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scsi_do_cmd(&scp, cmnd, &gdtcmd, + sizeof(gdth_cmd_str), gdth_scsi_done, + 30*HZ, 1); + down(&sem); + } + } + } + } + + /* controller reset */ + gdtcmd.BoardNode = LOCALBOARD; + gdtcmd.Service = CACHESERVICE; + gdtcmd.OpCode = GDT_RESET; + TRACE2(("gdth_halt(): reset controller %d\n", hanum)); + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scsi_do_cmd(&scp, cmnd, &gdtcmd, + sizeof(gdth_cmd_str), gdth_scsi_done, + 10*HZ, 1); + down(&sem); + } + } + printk("Done.\n"); + +#ifdef GDTH_STATISTICS + timer_active &= ~(1<= 0x020100 + unregister_reboot_notifier(&gdth_notifier); + return NOTIFY_OK; +#endif +} + + +/* called from init/main.c */ +void gdth_setup(char *str,int *ints) +{ + static size_t setup_idx = 0; + + TRACE2(("gdth_setup() str %s ints[0] %d ints[1] %d\n", + str ? str:"NULL", ints[0], + ints[0] ? ints[1]:0)); + + if (setup_idx >= MAXHA) { + printk("GDT: gdth_setup() called too many times. Bad LILO params ?\n"); + return; + } + if (ints[0] != 1) { + printk("GDT: Illegal command line !\n"); + printk("Usage: gdth=\n"); + printk("Where: : valid EISA controller IRQ (10,11,12,14)\n"); + printk(" or 0 to disable controller driver\n"); + return; + } + if (ints[1] == 10 || ints[1] == 11 || ints[1] == 12 || ints[1] == 14) { + irqs[setup_idx++] = ints[1]; + irqs[setup_idx] = 0xff; + return; + } + if (ints[1] == 0) { + disable_gdth_scan = TRUE; + return; + } + printk("GDT: Invalid IRQ (%d) specified\n",ints[1]); +} + + +#ifdef MODULE +Scsi_Host_Template driver_template = GDTH; +#include "scsi_module.c" +#endif + diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/gdth.h linux/drivers/scsi/gdth.h --- v2.1.62/linux/drivers/scsi/gdth.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/gdth.h Wed Nov 5 13:16:29 1997 @@ -0,0 +1,720 @@ +#ifndef _GDTH_H +#define _GDTH_H + +/* + * Header file for the GDT ISA/EISA/PCI Disk Array Controller driver for Linux + * + * gdth.h Copyright (C) 1995-97 ICP vortex Computersysteme GmbH, Achim Leubner + * See gdth.c for further informations and + * below for supported controller types + * + * + * + * $Id: gdth.h,v 1.9 1997/11/04 09:55:42 achim Exp $ + */ + +#include +#include + +#ifndef NULL +#define NULL 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* defines, macros */ + +/* driver version */ +#define GDTH_VERSION_STR "1.02" +#define GDTH_VERSION 1 +#define GDTH_SUBVERSION 2 + +/* protocol version */ +#define PROTOCOL_VERSION 1 + +/* controller classes */ +#define GDT_ISA 0x01 /* ISA controller */ +#define GDT_EISA 0x02 /* EISA controller */ +#define GDT_PCI 0x03 /* PCI controller */ +#define GDT_PCINEW 0x04 /* new PCI controller */ +#define GDT_PCIMPR 0x05 /* PCI MPR controller */ +/* GDT_EISA, controller subtypes EISA */ +#define GDT3_ID 0x0130941c /* GDT3000/3020 */ +#define GDT3A_ID 0x0230941c /* GDT3000A/3020A/3050A */ +#define GDT3B_ID 0x0330941c /* GDT3000B/3010A */ +/* GDT_ISA */ +#define GDT2_ID 0x0120941c /* GDT2000/2020 */ +/* vendor ID, device IDs (PCI) */ +/* these defines should already exist in */ +#ifndef PCI_VENDOR_ID_VORTEX +#define PCI_VENDOR_ID_VORTEX 0x1119 /* PCI controller vendor ID */ +#endif +#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0 +/* GDT_PCI */ +#define PCI_DEVICE_ID_VORTEX_GDT60x0 0 /* GDT6000/6020/6050 */ +#define PCI_DEVICE_ID_VORTEX_GDT6000B 1 /* GDT6000B/6010 */ +/* GDT_PCINEW */ +#define PCI_DEVICE_ID_VORTEX_GDT6x10 2 /* GDT6110/6510 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x20 3 /* GDT6120/6520 */ +#define PCI_DEVICE_ID_VORTEX_GDT6530 4 /* GDT6530 */ +#define PCI_DEVICE_ID_VORTEX_GDT6550 5 /* GDT6550 */ +/* GDT_PCINEW, wide/ultra SCSI controllers */ +#define PCI_DEVICE_ID_VORTEX_GDT6x17 6 /* GDT6117/6517 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x27 7 /* GDT6127/6527 */ +#define PCI_DEVICE_ID_VORTEX_GDT6537 8 /* GDT6537 */ +#define PCI_DEVICE_ID_VORTEX_GDT6557 9 /* GDT6557/6557-ECC */ +/* GDT_PCINEW, wide SCSI controllers */ +#define PCI_DEVICE_ID_VORTEX_GDT6x15 10 /* GDT6115/6515 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x25 11 /* GDT6125/6525 */ +#define PCI_DEVICE_ID_VORTEX_GDT6535 12 /* GDT6535 */ +#define PCI_DEVICE_ID_VORTEX_GDT6555 13 /* GDT6555/6555-ECC */ +#endif + +#ifndef PCI_DEVICE_ID_VORTEX_GDT6x17RP +/* GDT_MPR, RP series, wide/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x100 /* GDT6117RP/GDT6517RP */ +#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x101 /* GDT6127RP/GDT6527RP */ +#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x102 /* GDT6537RP */ +#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x103 /* GDT6557RP */ +/* GDT_MPR, RP series, narrow/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x104 /* GDT6111RP/GDT6511RP */ +#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x105 /* GDT6121RP/GDT6521RP */ +/* GDT_MPR, RP1 series, wide/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x17RP1 0x110 /* GDT6117RP1/GDT6517RP1 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x27RP1 0x111 /* GDT6127RP1/GDT6527RP1 */ +#define PCI_DEVICE_ID_VORTEX_GDT6537RP1 0x112 /* GDT6537RP1 */ +#define PCI_DEVICE_ID_VORTEX_GDT6557RP1 0x113 /* GDT6557RP1 */ +/* GDT_MPR, RP1 series, narrow/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x11RP1 0x114 /* GDT6111RP1/GDT6511RP1 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x21RP1 0x115 /* GDT6121RP1/GDT6521RP1 */ +/* GDT_MPR, RP2 series, wide/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x17RP2 0x120 /* GDT6117RP2/GDT6517RP2 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x27RP2 0x121 /* GDT6127RP2/GDT6527RP2 */ +#define PCI_DEVICE_ID_VORTEX_GDT6537RP2 0x122 /* GDT6537RP2 */ +#define PCI_DEVICE_ID_VORTEX_GDT6557RP2 0x123 /* GDT6557RP2 */ +/* GDT_MPR, RP2 series, narrow/ultra SCSI */ +#define PCI_DEVICE_ID_VORTEX_GDT6x11RP2 0x124 /* GDT6111RP2/GDT6511RP2 */ +#define PCI_DEVICE_ID_VORTEX_GDT6x21RP2 0x125 /* GDT6121RP2/GDT6521RP2 */ +#endif + +/* limits */ +#define GDTH_SCRATCH 4096 /* 4KB scratch buffer */ +#define GDTH_MAXCMDS 124 +#define GDTH_MAXC_P_L 16 /* max. cmds per lun */ +#define MAXOFFSETS 128 +#define MAXHA 8 +#define MAXID 8 +#define MAXLUN 8 +#define MAXBUS 5 +#define MAX_HDRIVES 35 /* max. host drive count */ +#define MAX_EVENTS 100 /* event buffer count */ +#define MAXCYLS 1024 +#define HEADS 64 +#define SECS 32 /* mapping 64*32 */ +#define MEDHEADS 127 +#define MEDSECS 63 /* mapping 127*63 */ +#define BIGHEADS 255 +#define BIGSECS 63 /* mapping 255*63 */ + +/* special command ptr. */ +#define UNUSED_CMND ((Scsi_Cmnd *)-1) +#define INTERNAL_CMND ((Scsi_Cmnd *)-2) +#define SCREEN_CMND ((Scsi_Cmnd *)-3) +#define SPECIAL_SCP(p) (p==UNUSED_CMND || p==INTERNAL_CMND || p==SCREEN_CMND) + +/* device types */ +#define EMPTY_DTYP 0 +#define CACHE_DTYP 1 +#define RAW_DTYP 2 +#define SIOP_DTYP 3 /* the SCSI processor */ + +/* controller services */ +#define SCSIRAWSERVICE 3 +#define CACHESERVICE 9 +#define SCREENSERVICE 11 + +/* screenservice defines */ +#define MSG_INV_HANDLE -1 /* special message handle */ +#define MSGLEN 16 /* size of message text */ +#define MSG_SIZE 34 /* size of message structure */ +#define MSG_REQUEST 0 /* async. event: message */ + +/* cacheservice defines */ +#define SECTOR_SIZE 0x200 /* always 512 bytes per sector */ + +/* DPMEM constants */ +#define IC_HEADER_BYTES 48 +#define IC_QUEUE_BYTES 4 +#define DPMEM_COMMAND_OFFSET IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS + +/* service commands */ +#define GDT_INIT 0 /* service initialization */ +#define GDT_READ 1 /* read command */ +#define GDT_WRITE 2 /* write command */ +#define GDT_INFO 3 /* information about devices */ +#define GDT_FLUSH 4 /* flush dirty cache buffers */ +#define GDT_IOCTL 5 /* ioctl command */ +#define GDT_DEVTYPE 9 /* additional information */ +#define GDT_MOUNT 10 /* mount cache device */ +#define GDT_UNMOUNT 11 /* unmount cache device */ +#define GDT_SET_FEAT 12 /* set feat. (scatter/gather) */ +#define GDT_GET_FEAT 13 /* get features */ +#define GDT_RESERVE 14 /* reserve dev. to raw service */ +#define GDT_WRITE_THR 16 /* write through */ +#define GDT_EXT_INFO 18 /* extended info */ +#define GDT_RESET 19 /* controller reset */ + +/* IOCTL command defines */ +#define SCSI_CHAN_CNT 5 /* subfunctions */ +#define L_CTRL_PATTERN 0x20000000L +#define CACHE_INFO 4 +#define CACHE_CONFIG 5 +#define IO_CHANNEL 0x00020000L /* channels */ +#define INVALID_CHANNEL 0x0000ffffL + +/* IOCTLs */ +#define GDTIOCTL_MASK ('J'<<8) +#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */ +#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */ +#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */ +#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */ +#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */ +#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */ +#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */ + +/* service errors */ +#define S_OK 1 /* no error */ +#define S_BSY 7 /* controller busy */ +#define S_RAW_SCSI 12 /* raw serv.: target error */ +#define S_RAW_ILL 0xff /* raw serv.: illegal */ + +/* timeout values */ +#define INIT_RETRIES 10000 /* 10000 * 1ms = 10s */ +#define INIT_TIMEOUT 100000 /* 1000 * 1ms = 1s */ +#define POLL_TIMEOUT 10000 /* 10000 * 1ms = 10s */ + +/* priorities */ +#define DEFAULT_PRI 0x20 +#define IOCTL_PRI 0x10 + +/* data directions */ +#define DATA_IN 0x01000000L /* data from target */ +#define DATA_OUT 0x00000000L /* data to target */ + +/* BMIC registers (EISA controllers) */ +#define ID0REG 0x0c80 /* board ID */ +#define EINTENABREG 0x0c89 /* interrupt enable */ +#define SEMA0REG 0x0c8a /* command semaphore */ +#define SEMA1REG 0x0c8b /* status semaphore */ +#define LDOORREG 0x0c8d /* local doorbell */ +#define EDENABREG 0x0c8e /* EISA system doorbell enable */ +#define EDOORREG 0x0c8f /* EISA system doorbell */ +#define MAILBOXREG 0x0c90 /* mailbox reg. (16 bytes) */ +#define EISAREG 0x0cc0 /* EISA configuration */ + +/* other defines */ +#define LINUX_OS 8 /* used for cache optim. */ +#define SCATTER_GATHER 1 /* s/g feature */ +#define GDTH_MAXSG 32 /* max. s/g elements */ +#define SECS32 0x1f /* round capacity */ +#define BIOS_ID_OFFS 0x10 /* offset contr. ID in ISABIOS */ +#define LOCALBOARD 0 /* board node always 0 */ +#define ASYNCINDEX 0 /* cmd index async. event */ +#define SPEZINDEX 1 /* cmd index unknown service */ +#define GDT_WR_THROUGH 0x100 /* WRITE_THROUGH supported */ + +/* typedefs */ + +#pragma pack(1) + +typedef struct { + char buffer[GDTH_SCRATCH]; /* scratch buffer */ +} gdth_scratch_str; + +/* screenservice message */ +typedef struct { + ulong msg_handle; /* message handle */ + ulong msg_len; /* size of message */ + ulong msg_alen; /* answer length */ + unchar msg_answer; /* answer flag */ + unchar msg_ext; /* more messages */ + unchar msg_reserved[2]; + char msg_text[MSGLEN+2]; /* the message text */ +} gdth_msg_str; + +/* get channel count IOCTL */ +typedef struct { + ulong channel_no; /* number of channel */ + ulong drive_cnt; /* number of drives */ + unchar siop_id; /* SCSI processor ID */ + unchar siop_state; /* SCSI processor state */ +} gdth_getch_str; + +/* cache info/config IOCTL */ +typedef struct { + ulong version; /* firmware version */ + ushort state; /* cache state (on/off) */ + ushort strategy; /* cache strategy */ + ushort write_back; /* write back state (on/off) */ + ushort block_size; /* cache block size */ +} gdth_cpar_str; + +typedef struct { + ulong csize; /* cache size */ + ulong read_cnt; /* read/write counter */ + ulong write_cnt; + ulong tr_hits; /* hits */ + ulong sec_hits; + ulong sec_miss; /* misses */ +} gdth_cstat_str; + +typedef struct { + gdth_cpar_str cpar; + gdth_cstat_str cstat; +} gdth_cinfo_str; + +/* scatter/gather element */ +typedef struct { + ulong sg_ptr; /* address */ + ulong sg_len; /* length */ +} gdth_sg_str; + +/* command structure */ +typedef struct { + ulong BoardNode; /* board node (always 0) */ + ulong CommandIndex; /* command number */ + ushort OpCode; /* the command (READ,..) */ + union { + struct { + ushort DeviceNo; /* number of cache drive */ + ulong BlockNo; /* block number */ + ulong BlockCnt; /* block count */ + ulong DestAddr; /* dest. addr. (if s/g: -1) */ + ulong sg_canz; /* s/g element count */ + gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */ + } cache; /* cache service cmd. str. */ + struct { + ushort param_size; /* size of p_param buffer */ + ulong subfunc; /* IOCTL function */ + ulong channel; /* device */ + ulong p_param; /* buffer */ + } ioctl; /* IOCTL command structure */ + struct { + ushort reserved; + ulong msg_handle; /* message handle */ + ulong msg_addr; /* message buffer address */ + } screen; /* screen service cmd. str. */ + struct { + ushort reserved; + ulong direction; /* data direction */ + ulong mdisc_time; /* disc. time (0: no timeout)*/ + ulong mcon_time; /* connect time(0: no to.) */ + ulong sdata; /* dest. addr. (if s/g: -1) */ + ulong sdlen; /* data length (bytes) */ + ulong clen; /* SCSI cmd. length(6,10,12) */ + unchar cmd[12]; /* SCSI command */ + unchar target; /* target ID */ + unchar lun; /* LUN */ + unchar bus; /* SCSI bus number */ + unchar priority; /* only 0 used */ + ulong sense_len; /* sense data length */ + ulong sense_data; /* sense data addr. */ + struct raw *link_p; /* linked cmds (not supp.) */ + ulong sg_ranz; /* s/g element count */ + gdth_sg_str sg_lst[GDTH_MAXSG]; /* s/g list */ + } raw; /* raw service cmd. struct. */ + } u; + /* additional variables */ + unchar Service; /* controller service */ + ushort Status; /* command result */ + ulong Info; /* additional information */ + Scsi_Cmnd *RequestBuffer; /* request buffer */ +} gdth_cmd_str; + +/* controller event structure */ +#define ES_ASYNC 1 +#define ES_DRIVER 2 +#define ES_TEST 3 +#define ES_SYNC 4 +typedef struct { + ushort size; /* size of structure */ + union { + char stream[16]; + struct { + ushort ionode; + ushort service; + ulong index; + } driver; + struct { + ushort ionode; + ushort service; + ushort status; + ulong info; + unchar scsi_coord[3]; + } async; + struct { + ushort ionode; + ushort service; + ushort status; + ulong info; + ushort hostdrive; + unchar scsi_coord[3]; + unchar sense_key; + } sync; + struct { + ulong l1, l2, l3, l4; + } test; + } eu; +} gdth_evt_data; + +typedef struct { + ulong first_stamp; + ulong last_stamp; + ushort same_count; + ushort event_source; + ushort event_idx; + unchar application; + unchar reserved; + gdth_evt_data event_data; +} gdth_evt_str; + + +/* DPRAM structures */ + +/* interface area ISA/PCI */ +typedef struct { + unchar S_Cmd_Indx; /* special command */ + unchar volatile S_Status; /* status special command */ + ushort reserved1; + ulong S_Info[4]; /* add. info special command */ + unchar volatile Sema0; /* command semaphore */ + unchar reserved2[3]; + unchar Cmd_Index; /* command number */ + unchar reserved3[3]; + ushort volatile Status; /* command status */ + ushort Service; /* service(for async.events) */ + ulong Info[2]; /* additional info */ + struct { + ushort offset; /* command offs. in the DPRAM*/ + ushort serv_id; /* service */ + } comm_queue[MAXOFFSETS]; /* command queue */ + ulong bios_reserved[2]; + unchar gdt_dpr_cmd[1]; /* commands */ +} gdt_dpr_if; + +/* SRAM structure PCI controllers */ +typedef struct { + ulong magic; /* controller ID from BIOS */ + ushort need_deinit; /* switch betw. BIOS/driver */ + unchar switch_support; /* see need_deinit */ + unchar padding[9]; + unchar os_used[16]; /* OS code per service */ + unchar unused[28]; + unchar fw_magic; /* contr. ID from firmware */ +} gdt_pci_sram; + +/* SRAM structure EISA controllers (but NOT GDT3000/3020) */ +typedef struct { + unchar os_used[16]; /* OS code per service */ + ushort need_deinit; /* switch betw. BIOS/driver */ + unchar switch_support; /* see need_deinit */ + unchar padding; +} gdt_eisa_sram; + + +/* DPRAM ISA controllers */ +typedef struct { + union { + struct { + unchar bios_used[0x3c00-32]; /* 15KB - 32Bytes BIOS */ + ulong magic; /* controller (EISA) ID */ + ushort need_deinit; /* switch betw. BIOS/driver */ + unchar switch_support; /* see need_deinit */ + unchar padding[9]; + unchar os_used[16]; /* OS code per service */ + } dp_sram; + unchar bios_area[0x4000]; /* 16KB reserved for BIOS */ + } bu; + union { + gdt_dpr_if ic; /* interface area */ + unchar if_area[0x3000]; /* 12KB for interface */ + } u; + struct { + unchar memlock; /* write protection DPRAM */ + unchar event; /* release event */ + unchar irqen; /* board interrupts enable */ + unchar irqdel; /* acknowledge board int. */ + unchar volatile Sema1; /* status semaphore */ + unchar rq; /* IRQ/DRQ configuration */ + } io; +} gdt2_dpram_str; + +/* DPRAM PCI controllers */ +typedef struct { + union { + gdt_dpr_if ic; /* interface area */ + unchar if_area[0xff0-sizeof(gdt_pci_sram)]; + } u; + gdt_pci_sram gdt6sr; /* SRAM structure */ + struct { + unchar unused0[1]; + unchar volatile Sema1; /* command semaphore */ + unchar unused1[3]; + unchar irqen; /* board interrupts enable */ + unchar unused2[2]; + unchar event; /* release event */ + unchar unused3[3]; + unchar irqdel; /* acknowledge board int. */ + unchar unused4[3]; + } io; +} gdt6_dpram_str; + +/* PLX register structure (new PCI controllers) */ +typedef struct { + unchar cfg_reg; /* DPRAM cfg.(2:below 1MB,0:anywhere)*/ + unchar unused1[0x3f]; + unchar volatile sema0_reg; /* command semaphore */ + unchar volatile sema1_reg; /* status semaphore */ + unchar unused2[2]; + ushort volatile status; /* command status */ + ushort service; /* service */ + ulong info[2]; /* additional info */ + unchar unused3[0x10]; + unchar ldoor_reg; /* PCI to local doorbell */ + unchar unused4[3]; + unchar volatile edoor_reg; /* local to PCI doorbell */ + unchar unused5[3]; + unchar control0; /* control0 register(unused) */ + unchar control1; /* board interrupts enable */ + unchar unused6[0x16]; +} gdt6c_plx_regs; + +/* DPRAM new PCI controllers */ +typedef struct { + union { + gdt_dpr_if ic; /* interface area */ + unchar if_area[0x4000-sizeof(gdt_pci_sram)]; + } u; + gdt_pci_sram gdt6sr; /* SRAM structure */ +} gdt6c_dpram_str; + +/* i960 register structure (PCI MPR controllers) */ +typedef struct { + unchar unused1[16]; + unchar volatile sema0_reg; /* command semaphore */ + unchar unused2; + unchar volatile sema1_reg; /* status semaphore */ + unchar unused3; + ushort volatile status; /* command status */ + ushort service; /* service */ + ulong info[2]; /* additional info */ + unchar ldoor_reg; /* PCI to local doorbell */ + unchar unused4[11]; + unchar volatile edoor_reg; /* local to PCI doorbell */ + unchar unused5[7]; + unchar edoor_en_reg; /* board interrupts enable */ + unchar unused6[27]; + ulong unused7[1004]; /* size: 4 KB */ +} gdt6m_i960_regs; + +/* DPRAM PCI MPR controllers */ +typedef struct { + gdt6m_i960_regs i960r; /* 4KB i960 registers */ + union { + gdt_dpr_if ic; /* interface area */ + unchar if_area[0x3000-sizeof(gdt_pci_sram)]; + } u; + gdt_pci_sram gdt6sr; /* SRAM structure */ +} gdt6m_dpram_str; + + +/* PCI resources */ +typedef struct { + ushort device_id; /* device ID (0,..,9) */ + unchar bus; /* PCI bus */ + unchar device_fn; /* PCI device/function no. */ + ulong dpmem; /* DPRAM address */ + ulong io; /* IO address */ + ulong io_mm; /* IO address mem. mapped */ + ulong bios; /* BIOS address */ + unchar irq; /* IRQ */ +} gdth_pci_str; + + +/* controller information structure */ +typedef struct { + unchar bus_cnt; /* SCSI bus count */ + unchar type; /* controller class */ + ushort raw_feat; /* feat. raw service (s/g,..) */ + ulong stype; /* controller subtype */ + ushort cache_feat; /* feat. cache serv. (s/g,..) */ + ushort bmic; /* BMIC address (EISA) */ + void *brd; /* DPRAM address */ + ulong brd_phys; /* slot number/BIOS address */ + gdt6c_plx_regs *plx; /* PLX regs (new PCI contr.) */ + gdth_cmd_str *pccb; /* address command structure */ + gdth_scratch_str *pscratch; + unchar irq; /* IRQ */ + unchar drq; /* DRQ (ISA controllers) */ + ushort status; /* command status */ + ulong info; + ulong info2; /* additional info */ + Scsi_Cmnd *req_first; /* top of request queue */ + struct { + unchar type; /* device type */ + unchar heads; /* mapping */ + unchar secs; + unchar lock; /* drive locked ? (hot plug) */ + ushort hostdrive; /* host drive number */ + ushort devtype; /* further information */ + ulong size; /* capacity */ + } id[MAXBUS][MAXID]; + ushort cmd_cnt; /* command count in DPRAM */ + ushort cmd_len; /* length of actual command */ + ushort cmd_offs_dpmem; /* actual offset in DPRAM */ + ushort ic_all_size; /* sizeof DPRAM interf. area */ + unchar reserved; + unchar mode; /* information from /proc */ + ushort param_size; + gdth_cpar_str cpar; /* controller cache par. */ +} gdth_ha_str; + +/* structure for scsi_register(), SCSI bus != 0 */ +typedef struct { + ushort hanum; + ushort busnum; +} gdth_num_str; + +/* structure for scsi_register() */ +typedef struct { + gdth_num_str numext; /* must be the first element */ + gdth_ha_str haext; + gdth_cmd_str cmdext; + gdth_scratch_str dmaext; +} gdth_ext_str; + + +/* INQUIRY data format */ +typedef struct { + unchar type_qual; + unchar modif_rmb; + unchar version; + unchar resp_aenc; + unchar add_length; + unchar reserved1; + unchar reserved2; + unchar misc; + unchar vendor[8]; + unchar product[16]; + unchar revision[4]; +} gdth_inq_data; + +/* READ_CAPACITY data format */ +typedef struct { + ulong last_block_no; + ulong block_length; +} gdth_rdcap_data; + +/* REQUEST_SENSE data format */ +typedef struct { + unchar errorcode; + unchar segno; + unchar key; + ulong info; + unchar add_length; + ulong cmd_info; + unchar adsc; + unchar adsq; + unchar fruc; + unchar key_spec[3]; +} gdth_sense_data; + +/* MODE_SENSE data format */ +typedef struct { + struct { + unchar data_length; + unchar med_type; + unchar dev_par; + unchar bd_length; + } hd; + struct { + unchar dens_code; + unchar block_count[3]; + unchar reserved; + unchar block_length[3]; + } bd; +} gdth_modep_data; + +typedef struct { + ulong b[10]; /* 32 bit compiler ! */ +} gdth_stackframe; + +#pragma pack() + +/* function prototyping */ + +int gdth_detect(Scsi_Host_Template *); +int gdth_release(struct Scsi_Host *); +int gdth_command(Scsi_Cmnd *); +int gdth_queuecommand(Scsi_Cmnd *,void (*done)(Scsi_Cmnd *)); +int gdth_abort(Scsi_Cmnd *); +#if LINUX_VERSION_CODE >= 0x010346 +int gdth_reset(Scsi_Cmnd *, unsigned int reset_flags); +#else +int gdth_reset(Scsi_Cmnd *); +#endif +const char *gdth_info(struct Scsi_Host *); + + +#if LINUX_VERSION_CODE >= 0x010300 +int gdth_bios_param(Disk *,kdev_t,int *); +extern struct proc_dir_entry proc_scsi_gdth; +int gdth_proc_info(char *,char **,off_t,int,int,int); +#define GDTH { NULL, NULL, \ + &proc_scsi_gdth, \ + gdth_proc_info, \ + "GDT SCSI Disk Array Controller", \ + gdth_detect, \ + gdth_release, \ + gdth_info, \ + gdth_command, \ + gdth_queuecommand, \ + gdth_abort, \ + gdth_reset, \ + NULL, \ + gdth_bios_param, \ + GDTH_MAXCMDS, \ + -1, \ + GDTH_MAXSG, \ + GDTH_MAXC_P_L, \ + 0, \ + 1, \ + ENABLE_CLUSTERING} +#else +int gdth_bios_param(Disk *,int,int *); +#define GDTH { NULL, NULL, \ + "GDT SCSI Disk Array Controller", \ + gdth_detect, \ + gdth_release, \ + gdth_info, \ + gdth_command, \ + gdth_queuecommand, \ + gdth_abort, \ + gdth_reset, \ + NULL, \ + gdth_bios_param, \ + GDTH_MAXCMDS, \ + -1, \ + GDTH_MAXSG, \ + GDTH_MAXC_P_L, \ + 0, \ + 1, \ + ENABLE_CLUSTERING} +#endif + +#endif + diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/gdth_ioctl.h linux/drivers/scsi/gdth_ioctl.h --- v2.1.62/linux/drivers/scsi/gdth_ioctl.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/gdth_ioctl.h Tue Nov 4 09:17:30 1997 @@ -0,0 +1,86 @@ +#ifndef _GDTH_IOCTL_H +#define _GDTH_IOCTL_H + +/* gdth_ioctl.h + * $Id: gdth_ioctl.h,v 1.1 1997/02/21 08:07:27 achim Exp $ + */ + +/* IOCTLs */ +#define GDTIOCTL_MASK ('J'<<8) +#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */ +#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */ +#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */ +#define GDTIOCTL_OSVERS (GDTIOCTL_MASK | 3) /* get OS version */ +#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */ +#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */ +#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */ +#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */ + +#define GDTIOCTL_MAGIC 0x06030f07UL + + +/* IOCTL structure (write) */ +typedef struct { + ulong magic; /* IOCTL magic */ + ushort ioctl; /* IOCTL */ + ushort ionode; /* controller number */ + ushort service; /* controller service */ + ushort timeout; /* timeout */ + union { + struct { + unchar command[512]; /* controller command */ + unchar data[1]; /* add. data */ + } general; + struct { + unchar lock; /* lock/unlock */ + unchar drive_cnt; /* drive count */ + ushort drives[35]; /* drives */ + } lockdrv; + struct { + unchar lock; /* lock/unlock */ + unchar channel; /* channel */ + } lockchn; + struct { + int erase; /* erase event ? */ + int handle; + } event; + } iu; +} gdth_iowr_str; + +/* IOCTL structure (read) */ +typedef struct { + ulong size; /* buffer size */ + ulong status; /* IOCTL error code */ + union { + struct { + unchar data[1]; /* data */ + } general; + struct { + ushort version; /* driver version */ + } drvers; + struct { + unchar type; /* controller type */ + ushort info; /* slot etc. */ + ushort oem_id; /* OEM ID */ + ushort bios_ver; /* not used */ + ushort access; /* not used */ + ushort ext_type; /* extended type */ + } ctrtype; + struct { + unchar version; /* OS version */ + unchar subversion; /* OS subversion */ + ushort revision; /* revision */ + } osvers; + struct { + ushort count; /* controller count */ + } ctrcnt; + struct { + int handle; + unchar evt[32]; /* event structure */ + } event; + } iu; +} gdth_iord_str; + + +#endif + diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/gdth_proc.c linux/drivers/scsi/gdth_proc.c --- v2.1.62/linux/drivers/scsi/gdth_proc.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/gdth_proc.c Tue Nov 4 09:17:30 1997 @@ -0,0 +1,635 @@ +/* gdth_proc.c + * $Id: gdth_proc.c,v 1.6 1997/10/31 10:36:24 achim Exp $ + */ + +#include "gdth_ioctl.h" + +int gdth_proc_info(char *buffer,char **start,off_t offset,int length, + int hostno,int inout) +{ + int hanum,busnum,i; + + TRACE2(("gdth_proc_info() length %d ha %d offs %d inout %d\n", + length,hostno,offset,inout)); + + for (i=0; ihost_no == hostno) + break; + } + if (i==gdth_ctr_vcount) + return(-EINVAL); + + hanum = NUMDATA(gdth_ctr_vtab[i])->hanum; + busnum= NUMDATA(gdth_ctr_vtab[i])->busnum; + + if (inout) + return(gdth_set_info(buffer,length,i,hanum,busnum)); + else + return(gdth_get_info(buffer,start,offset,length,i,hanum,busnum)); +} + +static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum) +{ + int ret_val; + Scsi_Cmnd scp; + Scsi_Device sdev; + gdth_iowr_str *piowr; + + TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum)); + piowr = (gdth_iowr_str *)buffer; + + memset(&sdev,0,sizeof(Scsi_Device)); + memset(&scp, 0,sizeof(Scsi_Cmnd)); + sdev.host = gdth_ctr_vtab[vh]; + sdev.id = sdev.host->this_id; + scp.cmd_len = 12; + scp.host = gdth_ctr_vtab[vh]; + scp.target = sdev.host->this_id; + scp.device = &sdev; + scp.use_sg = 0; + + if (length >= 4) { + if (strncmp(buffer,"gdth",4) == 0) { + buffer += 5; + length -= 5; + ret_val = gdth_set_asc_info( buffer, length, hanum, scp ); + } else if (piowr->magic == GDTIOCTL_MAGIC) { + ret_val = gdth_set_bin_info( buffer, length, hanum, scp ); + } else { + printk("GDT: Wrong signature: %6s\n",buffer); + ret_val = -EINVAL; + } + } else { + ret_val = -EINVAL; + } + return ret_val; +} + +static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) +{ + int orig_length, drive, wb_mode; + char cmnd[12]; + int i, j, found; + gdth_ha_str *ha; + gdth_cmd_str gdtcmd; + gdth_cpar_str *pcpar; + + TRACE2(("gdth_set_asc_info() ha %d\n",hanum)); + ha = HADATA(gdth_ctr_tab[hanum]); + memset(cmnd, 0,10); + orig_length = length + 5; + drive = -1; + wb_mode = 0; + found = FALSE; + + if (length >= 5 && strncmp(buffer,"flush",5)==0) { + buffer += 6; + length -= 6; + if (length && *buffer>='0' && *buffer<='9') { + drive = (int)(*buffer-'0'); + ++buffer; --length; + if (length && *buffer>='0' && *buffer<='9') { + drive = drive*10 + (int)(*buffer-'0'); + ++buffer; --length; + } + printk("GDT: Flushing host drive %d .. ",drive); + } else { + printk("GDT: Flushing all host drives .. "); + } + for (i = 0; i < MAXBUS; ++i) { + for (j = 0; j < MAXID; ++j) { + if (ha->id[i][j].type == CACHE_DTYP) { + if (drive != -1 && + ha->id[i][j].hostdrive != (ushort)drive) + continue; + found = TRUE; + gdtcmd.BoardNode = LOCALBOARD; + gdtcmd.Service = CACHESERVICE; + gdtcmd.OpCode = GDT_FLUSH; + gdtcmd.u.cache.DeviceNo = ha->id[i][j].hostdrive; + gdtcmd.u.cache.BlockNo = 1; + gdtcmd.u.cache.sg_canz = 0; + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scsi_do_cmd(&scp, cmnd, &gdtcmd, + sizeof(gdth_cmd_str), gdth_scsi_done, + 30*HZ, 1); + down(&sem); + } + } + } + } + if (!found) + printk("\nNo host drive found !\n"); + else + printk("Done.\n"); + return(orig_length); + } + + if (length >= 7 && strncmp(buffer,"wbp_off",7)==0) { + buffer += 8; + length -= 8; + printk("GDT: Disabling write back permanently .. "); + wb_mode = 1; + } else if (length >= 6 && strncmp(buffer,"wbp_on",6)==0) { + buffer += 7; + length -= 7; + printk("GDT: Enabling write back permanently .. "); + wb_mode = 2; + } else if (length >= 6 && strncmp(buffer,"wb_off",6)==0) { + buffer += 7; + length -= 7; + printk("GDT: Disabling write back commands .. "); + if (ha->cache_feat & GDT_WR_THROUGH) { + gdth_write_through = TRUE; + printk("Done.\n"); + } else { + printk("Not supported !\n"); + } + return(orig_length); + } else if (length >= 5 && strncmp(buffer,"wb_on",5)==0) { + buffer += 6; + length -= 6; + printk("GDT: Enabling write back commands .. "); + gdth_write_through = FALSE; + printk("Done.\n"); + return(orig_length); + } + + if (wb_mode) { + pcpar = (gdth_cpar_str *)kmalloc( sizeof(gdth_cpar_str), + GFP_ATOMIC | GFP_DMA ); + if (pcpar == NULL) { + TRACE2(("gdth_set_info(): Unable to allocate memory.\n")); + printk("Unable to allocate memory.\n"); + return(-EINVAL); + } + memcpy( pcpar, &ha->cpar, sizeof(gdth_cpar_str) ); + gdtcmd.BoardNode = LOCALBOARD; + gdtcmd.Service = CACHESERVICE; + gdtcmd.OpCode = GDT_IOCTL; + gdtcmd.u.ioctl.p_param = virt_to_bus(pcpar); + gdtcmd.u.ioctl.param_size = sizeof(gdth_cpar_str); + gdtcmd.u.ioctl.subfunc = CACHE_CONFIG; + gdtcmd.u.ioctl.channel = INVALID_CHANNEL; + pcpar->write_back = wb_mode==1 ? 0:1; + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scsi_do_cmd(&scp, cmnd, &gdtcmd, sizeof(gdth_cmd_str), + gdth_scsi_done, 30*HZ, 1); + down(&sem); + } + kfree( pcpar ); + printk("Done.\n"); + return(orig_length); + } + + printk("GDT: Unknown command: %s Length: %d\n",buffer,length); + return(-EINVAL); +} + +static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) +{ + char cmnd[12]; + int id; + unchar i, j, k, found; + gdth_ha_str *ha; + gdth_iowr_str *piowr; + gdth_iord_str *piord; + gdth_cmd_str *pcmd; + ulong *ppadd; + ulong add_size, flags; + + + TRACE2(("gdth_set_bin_info() ha %d\n",hanum)); + ha = HADATA(gdth_ctr_tab[hanum]); + memset(cmnd, 0,10); + piowr = (gdth_iowr_str *)buffer; + piord = NULL; + pcmd = NULL; + + if (length < GDTOFFSOF(gdth_iowr_str,iu)) + return(-EINVAL); + + switch (piowr->ioctl) { + case GDTIOCTL_GENERAL: + if (length < GDTOFFSOF(gdth_iowr_str,iu.general.data[0])) + return(-EINVAL); + pcmd = (gdth_cmd_str *)piowr->iu.general.command; + pcmd->Service = piowr->service; + if (pcmd->OpCode == GDT_IOCTL) { + ppadd = &pcmd->u.ioctl.p_param; + add_size = pcmd->u.ioctl.param_size; + } else if (piowr->service == CACHESERVICE) { + add_size = pcmd->u.cache.BlockCnt * SECTOR_SIZE; + if (ha->cache_feat & SCATTER_GATHER) { + ppadd = &pcmd->u.cache.sg_lst[0].sg_ptr; + pcmd->u.cache.DestAddr = -1UL; + pcmd->u.cache.sg_lst[0].sg_len = add_size; + pcmd->u.cache.sg_canz = 1; + } else { + ppadd = &pcmd->u.cache.DestAddr; + pcmd->u.cache.sg_canz = 0; + } + } else if (piowr->service == SCSIRAWSERVICE) { + add_size = pcmd->u.raw.sdlen; + if (ha->raw_feat & SCATTER_GATHER) { + ppadd = &pcmd->u.raw.sg_lst[0].sg_ptr; + pcmd->u.raw.sdata = -1UL; + pcmd->u.raw.sg_lst[0].sg_len = add_size; + pcmd->u.raw.sg_ranz = 1; + } else { + ppadd = &pcmd->u.raw.sdata; + pcmd->u.raw.sg_ranz = 0; + } + } else { + return(-EINVAL); + } + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) + add_size ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + + piord->size = sizeof(gdth_iord_str) + add_size; + if (add_size > 0) { + memcpy(piord->iu.general.data, piowr->iu.general.data, add_size); + *ppadd = virt_to_bus(piord->iu.general.data); + } + /* do IOCTL */ + { + struct semaphore sem = MUTEX_LOCKED; + scp.request.rq_status = RQ_SCSI_BUSY; + scp.request.sem = &sem; + scp.SCp.this_residual = IOCTL_PRI; + scsi_do_cmd(&scp, cmnd, pcmd, + sizeof(gdth_cmd_str), gdth_scsi_done, + piowr->timeout*HZ, 1); + down(&sem); + piord->status = (ulong)scp.SCp.Message; + } + break; + + case GDTIOCTL_DRVERS: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + piord->iu.drvers.version = (GDTH_VERSION<<8) | GDTH_SUBVERSION; + break; + + case GDTIOCTL_CTRTYPE: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + if (ha->type == GDT_ISA || ha->type == GDT_EISA) { + piord->iu.ctrtype.type = (unchar)((ha->stype>>20) - 10); + } else if (ha->type != GDT_PCIMPR) { + piord->iu.ctrtype.type = (unchar)((ha->stype<<8) + 6); + } else { + piord->iu.ctrtype.type = 0xfe; + piord->iu.ctrtype.ext_type = 0x6000 | ha->stype; + } + piord->iu.ctrtype.info = ha->brd_phys; + piord->iu.ctrtype.oem_id = (ushort)GDT3_ID; + break; + + case GDTIOCTL_CTRCNT: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + piord->iu.ctrcnt.count = (ushort)gdth_ctr_count; + break; + + case GDTIOCTL_OSVERS: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + piord->iu.osvers.version = (unchar)(LINUX_VERSION_CODE >> 16); + piord->iu.osvers.subversion = (unchar)(LINUX_VERSION_CODE >> 8); + piord->iu.osvers.revision = (ushort)(LINUX_VERSION_CODE & 0xff); + break; + + case GDTIOCTL_LOCKDRV: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + for (i = k = 0; i < piowr->iu.lockdrv.drive_cnt; ++i) { + found = FALSE; + for (j = 0; j < ha->bus_cnt; ++j) { + for (k = 0; k < MAXID; ++k) { + if (ha->id[j][k].type == CACHE_DTYP && + ha->id[j][k].hostdrive == piowr->iu.lockdrv.drives[i]) { + found = TRUE; + break; + } + } + if (found) + break; + } + if (!found) + continue; + + if (piowr->iu.lockdrv.lock) { + save_flags( flags ); + cli(); + ha->id[j][k].lock = 1; + restore_flags( flags ); + gdth_wait_completion( hanum, j, k ); + gdth_stop_timeout( hanum, j, k ); + } else { + save_flags( flags ); + cli(); + ha->id[j][k].lock = 0; + restore_flags( flags ); + gdth_start_timeout( hanum, j, k ); + gdth_next( hanum ); + } + } + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + break; + + case GDTIOCTL_LOCKCHN: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + for (k = 0, j = piowr->iu.lockchn.channel; k < MAXID; ++k) { + if (ha->id[j][k].type != RAW_DTYP) + continue; + + if (piowr->iu.lockchn.lock) { + save_flags( flags ); + cli(); + ha->id[j][k].lock = 1; + restore_flags( flags ); + gdth_wait_completion( hanum, j, k ); + gdth_stop_timeout( hanum, j, k ); + } else { + save_flags( flags ); + cli(); + ha->id[j][k].lock = 0; + restore_flags( flags ); + gdth_start_timeout( hanum, j, k ); + gdth_next( hanum ); + } + } + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + break; + + case GDTIOCTL_EVENT: + id = gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ); + if (id == -1) + return(-EBUSY); + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + if (piowr->iu.event.erase == 0) { + piord->iu.event.handle = gdth_read_event( piowr->iu.event.handle, + (gdth_evt_str *)piord->iu.event.evt ); + } else { + piord->iu.event.handle = piowr->iu.event.handle; + gdth_readapp_event( (unchar)piowr->iu.event.erase, + (gdth_evt_str *)piord->iu.event.evt ); + } + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + break; + + default: + return(-EINVAL); + } + /* we return a buffer ID to detect the right buffer during READ-IOCTL */ + return id; +} + +static int gdth_get_info(char *buffer,char **start,off_t offset, + int length,int vh,int hanum,int busnum) +{ + int size = 0,len = 0; + off_t begin = 0,pos = 0; + gdth_ha_str *ha; + gdth_iord_str *piord; + int id; + + TRACE2(("gdth_get_info() ha %d bus %d\n",hanum,busnum)); + ha = HADATA(gdth_ctr_tab[hanum]); + id = length; + + /* look for buffer ID in length */ + if (id > 4) { +#if LINUX_VERSION_CODE >= 0x020000 + size = sprintf(buffer+len, + "%s SCSI Disk Array Controller\n", + gdth_ctr_name(hanum)); +#else + size = sprintf(buffer+len, + "%s SCSI Disk Array Controller (SCSI Bus %d)\n", + gdth_ctr_name(hanum),busnum); +#endif + len += size; pos = begin + len; + size = sprintf(buffer+len, + "Firmware Version: %d.%2d\tDriver Version: %s\n", + (unchar)(ha->cpar.version>>8), + (unchar)(ha->cpar.version),GDTH_VERSION_STR); + len += size; pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + goto stop_output; + + } else { + piord = (gdth_iord_str *)gdth_ioctl_tab[id-1][hanum]; + if (piord == NULL) + goto stop_output; + length = piord->size; + memcpy(buffer+len, (char *)piord, length); + gdth_ioctl_free(hanum, id); + len += length; pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + goto stop_output; + } + +stop_output: + *start = buffer +(offset-begin); + len -= (offset-begin); + if (len > length) + len = length; + TRACE2(("get_info() len %d pos %d begin %d offset %d length %d size %d\n", + len,pos,begin,offset,length,size)); + return(len); +} + + +void gdth_scsi_done(Scsi_Cmnd *scp) +{ + TRACE2(("gdth_scsi_done()\n")); + + scp->request.rq_status = RQ_SCSI_DONE; + + if (scp->request.sem != NULL) + up(scp->request.sem); +} + +static int gdth_ioctl_alloc(int hanum, ushort size) +{ + ulong flags; + int i; + + if (size == 0) + return -1; + + save_flags(flags); + cli(); + + for (i = 0; i < 4; ++i) { + if (gdth_ioctl_tab[i][hanum] == NULL) { + gdth_ioctl_tab[i][hanum] = kmalloc( size, GFP_ATOMIC | GFP_DMA ); + break; + } + } + + restore_flags(flags); + if (i == 4 || gdth_ioctl_tab[i][hanum] == NULL) + return -1; + return (i+1); +} + +static void gdth_ioctl_free(int hanum, int idx) +{ + ulong flags; + + save_flags(flags); + cli(); + + kfree( gdth_ioctl_tab[idx-1][hanum] ); + gdth_ioctl_tab[idx-1][hanum] = NULL; + + restore_flags(flags); +} + +static void gdth_wait_completion(int hanum, int busnum, int id) +{ + ulong flags; + int i; + Scsi_Cmnd *scp; + + save_flags(flags); + cli(); + + for (i = 0; i < GDTH_MAXCMDS; ++i) { + scp = gdth_cmd_tab[i][hanum].cmnd; + if (!SPECIAL_SCP(scp) && scp->target == (unchar)id && +#if LINUX_VERSION_CODE >= 0x020000 + scp->channel == (unchar)busnum) +#else + NUMDATA(scp->host)->busnum == (unchar)busnum) +#endif + { + restore_flags(flags); + while (!scp->SCp.have_data_in) + barrier(); + save_flags(flags); + cli(); + } + } + restore_flags(flags); +} + +static void gdth_stop_timeout(int hanum, int busnum, int id) +{ + ulong flags; + Scsi_Cmnd *scp; + gdth_ha_str *ha; + + save_flags(flags); + cli(); + ha = HADATA(gdth_ctr_tab[hanum]); + + for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { + if (scp->target == (unchar)id && +#if LINUX_VERSION_CODE >= 0x020000 + scp->channel == (unchar)busnum) +#else + NUMDATA(scp->host)->busnum == (unchar)busnum) +#endif + { + TRACE2(("gdth_stop_timeout(): update_timeout()\n")); + scp->SCp.buffers_residual = gdth_update_timeout(scp, 0); + } + } + restore_flags(flags); +} + +static void gdth_start_timeout(int hanum, int busnum, int id) +{ + ulong flags; + Scsi_Cmnd *scp; + gdth_ha_str *ha; + + save_flags(flags); + cli(); + ha = HADATA(gdth_ctr_tab[hanum]); + + for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { + if (scp->target == (unchar)id && +#if LINUX_VERSION_CODE >= 0x020000 + scp->channel == (unchar)busnum) +#else + NUMDATA(scp->host)->busnum == (unchar)busnum) +#endif + { + TRACE2(("gdth_start_timeout(): update_timeout()\n")); + gdth_update_timeout(scp, scp->SCp.buffers_residual); + } + } + restore_flags(flags); +} + +static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout) +{ + ulong flags; + int oldto; + + save_flags(flags); + cli(); + + oldto = scp->timeout; + scp->timeout = timeout; + if (timeout > 0) { + if (timer_table[SCSI_TIMER].expires == 0) { + timer_table[SCSI_TIMER].expires = jiffies + timeout; + timer_active |= 1 << SCSI_TIMER; + } else { + if (jiffies + timeout < timer_table[SCSI_TIMER].expires) + timer_table[SCSI_TIMER].expires = jiffies + timeout; + } + } + + restore_flags(flags); + return oldto; +} + diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/gdth_proc.h linux/drivers/scsi/gdth_proc.h --- v2.1.62/linux/drivers/scsi/gdth_proc.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/gdth_proc.h Tue Nov 4 09:17:30 1997 @@ -0,0 +1,24 @@ +#ifndef _GDTH_PROC_H +#define _GDTH_PROC_H + +/* gdth_proc.h + * $Id: gdth_proc.h,v 1.2 1997/02/21 08:08:51 achim Exp $ + */ + +static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum); +static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp); +static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp); +static int gdth_get_info(char *buffer,char **start,off_t offset, + int length,int vh,int hanum,int busnum); + +static int gdth_ioctl_alloc(int hanum, ushort size); +static void gdth_ioctl_free(int hanum, int id); +static void gdth_wait_completion(int hanum, int busnum, int id); +static void gdth_stop_timeout(int hanum, int busnum, int id); +static void gdth_start_timeout(int hanum, int busnum, int id); +static int gdth_update_timeout(Scsi_Cmnd *scp, int timeout); + +void gdth_scsi_done(Scsi_Cmnd *scp); + +#endif + diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- v2.1.62/linux/drivers/scsi/hosts.c Mon Aug 18 18:19:46 1997 +++ linux/drivers/scsi/hosts.c Wed Nov 5 13:17:26 1997 @@ -194,6 +194,22 @@ #include "mac53c94.h" #endif +#ifdef CONFIG_SCSI_GDTH +#include "gdth.h" +#endif + +#ifdef CONFIG_SCSI_PCI2000 +#include "pci2000.h" +#endif + +#ifdef CONFIG_SCSI_PCI2220I +#include "pci2220i.h" +#endif + +#ifdef CONFIG_SCSI_PSI240I +#include "psi240i.h" +#endif + #ifdef CONFIG_SCSI_DEBUG #include "scsi_debug.h" #endif @@ -256,6 +272,17 @@ #ifdef CONFIG_SCSI_ADVANSYS ADVANSYS, #endif + +#ifdef CONFIG_SCSI_PCI2000 + PCI2000, +#endif +#ifdef CONFIG_SCSI_PCI2220I + PCI2220I, +#endif +#ifdef CONFIG_SCSI_PSI240I + PSI240I, +#endif + /* BusLogic must come before aha1542.c */ #ifdef CONFIG_SCSI_BUSLOGIC BUSLOGIC, @@ -340,6 +367,9 @@ #endif #ifdef CONFIG_SCSI_SUNESP SCSI_SPARC_ESP, +#endif +#ifdef CONFIG_SCSI_GDTH + GDTH, #endif #ifdef CONFIG_SCSI_QLOGICPTI QLOGICPTI, diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/pci2000.c linux/drivers/scsi/pci2000.c --- v2.1.62/linux/drivers/scsi/pci2000.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/pci2000.c Wed Nov 5 12:59:49 1997 @@ -0,0 +1,660 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: pci2000i.c + * + *-M*************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include "hosts.h" + +#include "pci2000.h" +#include "psi_roy.h" + +#include + +struct proc_dir_entry Proc_Scsi_Pci2000 = + { PROC_SCSI_PCI2000, 7, "pci2000", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; + +//#define DEBUG 1 + +#ifdef DEBUG +#define DEB(x) x +#define STOP_HERE {int st;for(st=0;st<100;st++){st=1;}} +#else +#define DEB(x) +#define STOP_HERE +#endif + +typedef struct + { + ULONG address; + ULONG length; + } SCATGATH, *PSCATGATH; + +typedef struct + { + Scsi_Cmnd *SCpnt; + SCATGATH scatGath[16]; + UCHAR tag; + } DEV2000, *PDEV2000; + +typedef struct + { + USHORT basePort; + USHORT mb0; + USHORT mb1; + USHORT mb2; + USHORT mb3; + USHORT mb4; + USHORT cmd; + USHORT tag; + DEV2000 dev[MAX_BUS][MAX_UNITS]; + } ADAPTER2000, *PADAPTER2000; + +#define HOSTDATA(host) ((PADAPTER2000)&host->hostdata) + + +static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapter +static int NumAdapters = 0; + +/**************************************************************** + * Name: WaitReady :LOCAL + * + * Description: Wait for controller ready. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: TRUE on not ready. + * + ****************************************************************/ +static int WaitReady (PADAPTER2000 padapter) + { + ULONG timer; + + timer = jiffies + TIMEOUT_COMMAND; // calculate the timeout value + do { + if ( !inb_p (padapter->cmd) ) + return FALSE; + } while ( timer > jiffies ); // test for timeout + return TRUE; + } +/**************************************************************** + * Name: OpDone :LOCAL + * + * Description: Clean up operation and issue done to caller. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * status - Caller status. + * + * Returns: Nothing. + * + ****************************************************************/ +static void OpDone (Scsi_Cmnd *SCpnt, ULONG status) + { + SCpnt->result = status; + SCpnt->scsi_done (SCpnt); + } +/**************************************************************** + * Name: Command :LOCAL + * + * Description: Issue queued command to the PCI-2000. + * + * Parameters: padapter - Pointer to adapter information structure. + * cmd - PCI-2000 command byte. + * + * Returns: Non-zero command tag if operation is accepted. + * + ****************************************************************/ +static UCHAR Command (PADAPTER2000 padapter, UCHAR cmd) + { + outb_p (cmd, padapter->cmd); + if ( WaitReady (padapter) ) + return 0; + + if ( inw_p (padapter->mb0) ) + return 0; + + return inb_p (padapter->mb1); + } +/**************************************************************** + * Name: BuildSgList :LOCAL + * + * Description: Build the scatter gather list for controller. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * padapter - Pointer to adapter information structure. + * pdev - Pointer to adapter device structure. + * + * Returns: Non-zero in not scatter gather. + * + ****************************************************************/ +static int BuildSgList (Scsi_Cmnd *SCpnt, PADAPTER2000 padapter, PDEV2000 pdev) + { + int z; + + if ( SCpnt->use_sg ) + { + for ( z = 0; z < SCpnt->use_sg; z++ ) + { + pdev->scatGath[z].address = virt_to_bus (((struct scatterlist *)SCpnt->request_buffer)[z].address); + pdev->scatGath[z].length = ((struct scatterlist *)SCpnt->request_buffer)[z].length; + } + outl (virt_to_bus (pdev->scatGath), padapter->mb2); + outl ((SCpnt->use_sg << 24) | SCpnt->request_bufflen, padapter->mb3); + return FALSE; + } + outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2); + outl (SCpnt->request_bufflen, padapter->mb3); + return TRUE; + } +/**************************************************************** + * Name: Irq_Handler :LOCAL + * + * Description: Interrupt handler. + * + * Parameters: irq - Hardware IRQ number. + * dev_id - + * regs - + * + * Returns: TRUE if drive is not ready in time. + * + ****************************************************************/ +static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) + { + struct Scsi_Host *shost = NULL; // Pointer to host data block + PADAPTER2000 padapter; // Pointer to adapter control structure + PDEV2000 pdev; + Scsi_Cmnd *SCpnt; + UCHAR tag = 0; + UCHAR tag0; + ULONG error; + int pun; + int bus; + int z; + + DEB(printk ("\npci2000 recieved interrupt ")); + for ( z = 0; z < NumAdapters; z++ ) // scan for interrupt to process + { + if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) ) + { + tag = inb_p (HOSTDATA(PsiHost[z])->tag); + if ( tag ) + { + shost = PsiHost[z]; + break; + } + } + } + + if ( !shost ) + { + DEB (printk ("\npci2000: not my interrupt")); + return; + } + + padapter = HOSTDATA(shost); + + tag0 = tag & 0x7F; // mask off the error bit + for ( bus = 0; bus < MAX_BUS; bus++ ) // scan the busses + { + for ( pun = 0; pun < MAX_UNITS; pun++ ) // scan the targets + { + pdev = &padapter->dev[bus][pun]; + if ( !pdev->tag ) + continue; + if ( pdev->tag == tag0 ) // is this it? + { + pdev->tag = 0; + SCpnt = pdev->SCpnt; + goto irqProceed; + } + } + } + + outb_p (0xFF, padapter->tag); // clear the op interrupt + outb_p (CMD_DONE, padapter->cmd); // complete the op + return; // done, but, with what? + +irqProceed:; + if ( tag & ERR08_TAGGED ) // is there an error here? + { + if ( WaitReady (padapter) ) + { + OpDone (SCpnt, DID_TIME_OUT << 16); + return; + } + + outb_p (tag0, padapter->mb0); // get real error code + outb_p (CMD_ERROR, padapter->cmd); + if ( WaitReady (padapter) ) // wait for controller to suck up the op + { + OpDone (SCpnt, DID_TIME_OUT << 16); + return; + } + + error = inl (padapter->mb0); // get error data + outb_p (0xFF, padapter->tag); // clear the op interrupt + outb_p (CMD_DONE, padapter->cmd); // complete the op + + DEB (printk ("status: %lX ", error)); + if ( error == 0x00020002 ) // is this error a check condition? + { + if ( bus ) // are we doint SCSI commands? + { + OpDone (SCpnt, (DID_OK << 16) | 2); + return; + } + if ( *SCpnt->cmnd == SCSIOP_TEST_UNIT_READY ) + OpDone (SCpnt, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2); // test caller we have sense data too + else + OpDone (SCpnt, DID_ERROR << 16); + return; + } + OpDone (SCpnt, DID_ERROR << 16); + return; + } + + outb_p (0xFF, padapter->tag); // clear the op interrupt + outb_p (CMD_DONE, padapter->cmd); // complete the op + OpDone (SCpnt, DID_OK << 16); + } +/**************************************************************** + * Name: Pci2220i_QueueCommand + * + * Description: Process a queued command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * done - Pointer to done function to call. + * + * Returns: Status code. + * + ****************************************************************/ +int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) + { + UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB + PADAPTER2000 padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure + int rc = -1; // command return code + UCHAR bus = SCpnt->channel; + UCHAR pun = SCpnt->target; + UCHAR lun = SCpnt->lun; + UCHAR cmd; + PDEV2000 pdev = &padapter->dev[bus][pun]; + + if ( !done ) + { + printk("pci2000_queuecommand: %02X: done can't be NULL\n", *cdb); + return 0; + } + + SCpnt->scsi_done = done; + pdev->SCpnt = SCpnt; // Save this command data + + if ( WaitReady (padapter) ) + { + rc = DID_ERROR; + goto finished; + } + + outw_p (pun | (lun << 8), padapter->mb0); + + if ( bus ) + { + DEB (if(*cdb) printk ("\nCDB: %X- %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9])); + DEB (if(*cdb) printk ("\ntimeout_per_command: %d, timeout_total: %d, timeout: %d, internal_timout: %d", SCpnt->timeout_per_command, + SCpnt->timeout_total, SCpnt->timeout, SCpnt->internal_timeout)); + outl (SCpnt->timeout_per_command, padapter->mb1); + outb_p (CMD_SCSI_TIMEOUT, padapter->cmd); + if ( WaitReady (padapter) ) + { + rc = DID_ERROR; + goto finished; + } + + outw_p (pun | (lun << 8), padapter->mb0); + outw_p (SCpnt->cmd_len << 8, padapter->mb0 + 2); + outl (virt_to_bus (cdb), padapter->mb1); + if ( BuildSgList (SCpnt, padapter, pdev) ) + cmd = CMD_SCSI_THRU; + else + cmd = CMD_SCSI_THRU_SG; + if ( (pdev->tag = Command (padapter, cmd)) == 0 ) + rc = DID_TIME_OUT; + goto finished; + } + else + { + if ( lun ) + { + rc = DID_BAD_TARGET; + goto finished; + } + } + + switch ( *cdb ) + { + case SCSIOP_INQUIRY: // inquiry CDB + { + if ( SCpnt->use_sg ) + { + outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2); + } + else + { + outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2); + } + outl (SCpnt->request_bufflen, padapter->mb3); + cmd = CMD_DASD_SCSI_INQ; + break; + } + + case SCSIOP_TEST_UNIT_READY: // test unit ready CDB + outl (virt_to_bus (SCpnt->sense_buffer), padapter->mb2); + outl (sizeof (SCpnt->sense_buffer), padapter->mb3); + cmd = CMD_TEST_READY; + break; + + case SCSIOP_READ_CAPACITY: // read capctiy CDB + if ( SCpnt->use_sg ) + { + outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2); + } + else + { + outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2); + } + outl (8, padapter->mb3); + cmd = CMD_DASD_CAP; + break; + case SCSIOP_VERIFY: // verify CDB + outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2); + outl (XSCSI2LONG (&cdb[2]), padapter->mb1); + cmd = CMD_READ_SG; + break; + case SCSIOP_READ: // read10 CDB + outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2); + outl (XSCSI2LONG (&cdb[2]), padapter->mb1); + if ( BuildSgList (SCpnt, padapter, pdev) ) + cmd = CMD_READ; + else + cmd = CMD_READ_SG; + break; + case SCSIOP_READ6: // read6 CDB + outw_p (cdb[4], padapter->mb0 + 2); + outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1); + if ( BuildSgList (SCpnt, padapter, pdev) ) + cmd = CMD_READ; + else + cmd = CMD_READ_SG; + break; + case SCSIOP_WRITE: // write10 CDB + outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2); + outl (XSCSI2LONG (&cdb[2]), padapter->mb1); + if ( BuildSgList (SCpnt, padapter, pdev) ) + cmd = CMD_WRITE; + else + cmd = CMD_WRITE_SG; + break; + case SCSIOP_WRITE6: // write6 CDB + outw_p (cdb[4], padapter->mb0 + 2); + outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1); + if ( BuildSgList (SCpnt, padapter, pdev) ) + cmd = CMD_WRITE; + else + cmd = CMD_WRITE_SG; + break; + case SCSIOP_START_STOP_UNIT: + cmd = CMD_EJECT_MEDIA; + break; + case SCSIOP_MEDIUM_REMOVAL: + switch ( cdb[4] ) + { + case 0: + cmd = CMD_UNLOCK_DOOR; + break; + case 1: + cmd = CMD_LOCK_DOOR; + break; + default: + cmd = 0; + break; + } + if ( cmd ) + break; + default: + DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb)); + OpDone (SCpnt, DID_ERROR << 16); + return 0; + } + + if ( (pdev->tag = Command (padapter, cmd)) == 0 ) + rc = DID_TIME_OUT; +finished:; + if ( rc != -1 ) + OpDone (SCpnt, rc << 16); + return 0; + } +/**************************************************************** + * Name: internal_done :LOCAL + * + * Description: Done handler for non-queued commands + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Nothing. + * + ****************************************************************/ +static void internal_done (Scsi_Cmnd * SCpnt) + { + SCpnt->SCp.Status++; + } +/**************************************************************** + * Name: Pci2220i_Command + * + * Description: Process a command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Status code. + * + ****************************************************************/ +int Pci2000_Command (Scsi_Cmnd *SCpnt) + { + DEB(printk("pci2000_command: ..calling pci2000_queuecommand\n")); + + Pci2000_QueueCommand (SCpnt, internal_done); + + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier (); + return SCpnt->result; + } +/**************************************************************** + * Name: Pci2220i_Detect + * + * Description: Detect and initialize our boards. + * + * Parameters: tpnt - Pointer to SCSI host template structure. + * + * Returns: Number of adapters found. + * + ****************************************************************/ +int Pci2000_Detect (Scsi_Host_Template *tpnt) + { + int pci_index = 0; + struct Scsi_Host *pshost; + PADAPTER2000 padapter; + int z; + int setirq; + + if ( pcibios_present () ) + { + for ( pci_index = 0; pci_index <= MAXADAPTER; ++pci_index ) + { + UCHAR pci_bus, pci_device_fn; + + if ( pcibios_find_device (VENDOR_PSI, DEVICE_ROY_1, pci_index, &pci_bus, &pci_device_fn) != 0 ) + break; + + pshost = scsi_register (tpnt, sizeof(ADAPTER2000)); + padapter = HOSTDATA(pshost); + + pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &padapter->basePort); + padapter->basePort &= 0xFFFE; + DEB (printk ("\nBase Regs = %#04X", padapter->basePort)); // get the base I/O port address + padapter->mb0 = padapter->basePort + RTR_MAILBOX; // get the 32 bit mail boxes + padapter->mb1 = padapter->basePort + RTR_MAILBOX + 4; + padapter->mb2 = padapter->basePort + RTR_MAILBOX + 8; + padapter->mb3 = padapter->basePort + RTR_MAILBOX + 12; + padapter->mb4 = padapter->basePort + RTR_MAILBOX + 16; + padapter->cmd = padapter->basePort + RTR_LOCAL_DOORBELL; // command register + padapter->tag = padapter->basePort + RTR_PCI_DOORBELL; // tag/response register + + if ( WaitReady (padapter) ) + goto unregister; + outb_p (0x84, padapter->mb0); + outb_p (CMD_SPECIFY, padapter->cmd); + if ( WaitReady (padapter) ) + goto unregister; + + pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq); + setirq = 1; + for ( z = 0; z < pci_index; z++ ) // scan for shared interrupts + { + if ( PsiHost[z]->irq == pshost->irq ) // if shared then, don't posses + setirq = 0; + } + if ( setirq ) // if not shared, posses + { + if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2000", NULL) ) + { + printk ("Unable to allocate IRQ for PSI-2000 controller.\n"); + goto unregister; + } + } + PsiHost[pci_index] = pshost; // save SCSI_HOST pointer + + pshost->unique_id = padapter->basePort; + pshost->max_id = 16; + pshost->max_channel = 1; + + printk("\nPSI-2000 EIDE CONTROLLER: at I/O = %X IRQ = %d\n", padapter->basePort, pshost->irq); + printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n"); + continue; +unregister:; + scsi_unregister (pshost); + } + } + NumAdapters = pci_index; + return pci_index; + } +/**************************************************************** + * Name: Pci2220i_Abort + * + * Description: Process the Abort command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Allways snooze. + * + ****************************************************************/ +int Pci2000_Abort (Scsi_Cmnd *SCpnt) + { + DEB (printk ("pci2000_abort\n")); + return SCSI_ABORT_SNOOZE; + } +/**************************************************************** + * Name: Pci2220i_Reset + * + * Description: Process the Reset command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * flags - Flags about the reset command + * + * Returns: No active command at this time, so this means + * that each time we got some kind of response the + * last time through. Tell the mid-level code to + * request sense information in order to decide what + * to do next. + * + ****************************************************************/ +int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) + { + return SCSI_RESET_PUNT; + } + +#include "sd.h" + +/**************************************************************** + * Name: Pci2220i_BiosParam + * + * Description: Process the biosparam request from the SCSI manager to + * return C/H/S data. + * + * Parameters: disk - Pointer to SCSI disk structure. + * dev - Major/minor number from kernel. + * geom - Pointer to integer array to place geometry data. + * + * Returns: zero. + * + ****************************************************************/ +int Pci2000_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[]) + { + PADAPTER2000 padapter; + + padapter = HOSTDATA(disk->device->host); + + if ( WaitReady (padapter) ) + return 0; + outb_p (disk->device->id, padapter->mb0); + outb_p (CMD_GET_PARMS, padapter->cmd); + if ( WaitReady (padapter) ) + return 0; + + geom[0] = inb_p (padapter->mb2 + 3); + geom[1] = inb_p (padapter->mb2 + 2); + geom[2] = inw_p (padapter->mb2); + return 0; + } + + +#ifdef MODULE +/* Eventually this will go into an include file, but this will be later */ +Scsi_Host_Template driver_template = PCI2220I; + +#include "scsi_module.c" +#endif + diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/pci2000.h linux/drivers/scsi/pci2000.h --- v2.1.62/linux/drivers/scsi/pci2000.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/pci2000.h Wed Nov 5 13:00:23 1997 @@ -0,0 +1,226 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: pci2000.h + * + * Description: Header file for the SCSI driver for the PCI-2000 + * interface card. + * + *-M*************************************************************************/ +#ifndef _PCI2000_H +#define _PCI2000_H + +#include +#include + +#ifndef PSI_EIDE_SCSIOP +#define PSI_EIDE_SCSIOP 1 + +/************************************************/ +/* definition of standard data types */ +/************************************************/ +#define CHAR char +#define UCHAR unsigned char +#define SHORT short +#define USHORT unsigned short +#define BOOL long +#define LONG long +#define ULONG unsigned long +#define VOID void + +typedef CHAR *PCHAR; +typedef UCHAR *PUCHAR; +typedef SHORT *PSHORT; +typedef USHORT *PUSHORT; +typedef BOOL *PBOOL; +typedef LONG *PLONG; +typedef ULONG *PULONG; +typedef VOID *PVOID; + + +/************************************************/ +/* Misc. macros */ +/************************************************/ +#define ANY2SCSI(up, p) \ +((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \ +((UCHAR *)up)[1] = ((ULONG)(p)); + +#define SCSI2LONG(up) \ +( (((long)*(((UCHAR *)up))) << 16) \ ++ (((long)(((UCHAR *)up)[1])) << 8) \ ++ ((long)(((UCHAR *)up)[2])) ) + +#define XANY2SCSI(up, p) \ +((UCHAR *)up)[0] = ((long)(p)) >> 24; \ +((UCHAR *)up)[1] = ((long)(p)) >> 16; \ +((UCHAR *)up)[2] = ((long)(p)) >> 8; \ +((UCHAR *)up)[3] = ((long)(p)); + +#define XSCSI2LONG(up) \ +( (((long)(((UCHAR *)up)[0])) << 24) \ ++ (((long)(((UCHAR *)up)[1])) << 16) \ ++ (((long)(((UCHAR *)up)[2])) << 8) \ ++ ((long)(((UCHAR *)up)[3])) ) + +/************************************************/ +/* SCSI CDB operation codes */ +/************************************************/ +#define SCSIOP_TEST_UNIT_READY 0x00 +#define SCSIOP_REZERO_UNIT 0x01 +#define SCSIOP_REWIND 0x01 +#define SCSIOP_REQUEST_BLOCK_ADDR 0x02 +#define SCSIOP_REQUEST_SENSE 0x03 +#define SCSIOP_FORMAT_UNIT 0x04 +#define SCSIOP_READ_BLOCK_LIMITS 0x05 +#define SCSIOP_REASSIGN_BLOCKS 0x07 +#define SCSIOP_READ6 0x08 +#define SCSIOP_RECEIVE 0x08 +#define SCSIOP_WRITE6 0x0A +#define SCSIOP_PRINT 0x0A +#define SCSIOP_SEND 0x0A +#define SCSIOP_SEEK6 0x0B +#define SCSIOP_TRACK_SELECT 0x0B +#define SCSIOP_SLEW_PRINT 0x0B +#define SCSIOP_SEEK_BLOCK 0x0C +#define SCSIOP_PARTITION 0x0D +#define SCSIOP_READ_REVERSE 0x0F +#define SCSIOP_WRITE_FILEMARKS 0x10 +#define SCSIOP_FLUSH_BUFFER 0x10 +#define SCSIOP_SPACE 0x11 +#define SCSIOP_INQUIRY 0x12 +#define SCSIOP_VERIFY6 0x13 +#define SCSIOP_RECOVER_BUF_DATA 0x14 +#define SCSIOP_MODE_SELECT 0x15 +#define SCSIOP_RESERVE_UNIT 0x16 +#define SCSIOP_RELEASE_UNIT 0x17 +#define SCSIOP_COPY 0x18 +#define SCSIOP_ERASE 0x19 +#define SCSIOP_MODE_SENSE 0x1A +#define SCSIOP_START_STOP_UNIT 0x1B +#define SCSIOP_STOP_PRINT 0x1B +#define SCSIOP_LOAD_UNLOAD 0x1B +#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C +#define SCSIOP_SEND_DIAGNOSTIC 0x1D +#define SCSIOP_MEDIUM_REMOVAL 0x1E +#define SCSIOP_READ_CAPACITY 0x25 +#define SCSIOP_READ 0x28 +#define SCSIOP_WRITE 0x2A +#define SCSIOP_SEEK 0x2B +#define SCSIOP_LOCATE 0x2B +#define SCSIOP_WRITE_VERIFY 0x2E +#define SCSIOP_VERIFY 0x2F +#define SCSIOP_SEARCH_DATA_HIGH 0x30 +#define SCSIOP_SEARCH_DATA_EQUAL 0x31 +#define SCSIOP_SEARCH_DATA_LOW 0x32 +#define SCSIOP_SET_LIMITS 0x33 +#define SCSIOP_READ_POSITION 0x34 +#define SCSIOP_SYNCHRONIZE_CACHE 0x35 +#define SCSIOP_COMPARE 0x39 +#define SCSIOP_COPY_COMPARE 0x3A +#define SCSIOP_WRITE_DATA_BUFF 0x3B +#define SCSIOP_READ_DATA_BUFF 0x3C +#define SCSIOP_CHANGE_DEFINITION 0x40 +#define SCSIOP_READ_SUB_CHANNEL 0x42 +#define SCSIOP_READ_TOC 0x43 +#define SCSIOP_READ_HEADER 0x44 +#define SCSIOP_PLAY_AUDIO 0x45 +#define SCSIOP_PLAY_AUDIO_MSF 0x47 +#define SCSIOP_PLAY_TRACK_INDEX 0x48 +#define SCSIOP_PLAY_TRACK_RELATIVE 0x49 +#define SCSIOP_PAUSE_RESUME 0x4B +#define SCSIOP_LOG_SELECT 0x4C +#define SCSIOP_LOG_SENSE 0x4D +#define SCSIOP_MODE_SELECT10 0x55 +#define SCSIOP_MODE_SENSE10 0x5A +#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6 +#define SCSIOP_MECHANISM_STATUS 0xBD +#define SCSIOP_READ_CD 0xBE + +// SCSI read capacity structure +typedef struct _READ_CAPACITY_DATA + { + ULONG blks; /* total blocks (converted to little endian) */ + ULONG blksiz; /* size of each (converted to little endian) */ + } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; + +// SCSI inquiry data +typedef struct _INQUIRYDATA + { + UCHAR DeviceType :5; + UCHAR DeviceTypeQualifier :3; + UCHAR DeviceTypeModifier :7; + UCHAR RemovableMedia :1; + UCHAR Versions; + UCHAR ResponseDataFormat; + UCHAR AdditionalLength; + UCHAR Reserved[2]; + UCHAR SoftReset :1; + UCHAR CommandQueue :1; + UCHAR Reserved2 :1; + UCHAR LinkedCommands :1; + UCHAR Synchronous :1; + UCHAR Wide16Bit :1; + UCHAR Wide32Bit :1; + UCHAR RelativeAddressing :1; + UCHAR VendorId[8]; + UCHAR ProductId[16]; + UCHAR ProductRevisionLevel[4]; + UCHAR VendorSpecific[20]; + UCHAR Reserved3[40]; + } INQUIRYDATA, *PINQUIRYDATA; + +#endif + +// function prototypes +int Pci2000_Detect (Scsi_Host_Template *tpnt); +int Pci2000_Command (Scsi_Cmnd *SCpnt); +int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); +int Pci2000_Abort (Scsi_Cmnd *SCpnt); +int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int flags); +int Pci2000_BiosParam (Disk *disk, kdev_t dev, int geom[]); + +#ifndef NULL + #define NULL 0 +#endif + +extern struct proc_dir_entry Proc_Scsi_Pci2000; + +#define PCI2000 { NULL, NULL, \ + &Proc_Scsi_Pci2000,/* proc_dir_entry */ \ + NULL, \ + "PCI-2000 SCSI Intelligent Disk Controller",\ + Pci2000_Detect, \ + NULL, \ + NULL, \ + Pci2000_Command, \ + Pci2000_QueueCommand, \ + Pci2000_Abort, \ + Pci2000_Reset, \ + NULL, \ + Pci2000_BiosParam, \ + 16, \ + -1, \ + 16, \ + 1, \ + 0, \ + 0, \ + DISABLE_CLUSTERING } + +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/pci2220i.c linux/drivers/scsi/pci2220i.c --- v2.1.62/linux/drivers/scsi/pci2220i.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/pci2220i.c Wed Nov 5 13:18:17 1997 @@ -0,0 +1,817 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: pci2220i.c + * + * Description: SCSI driver for the PCI2220I EIDE interface card. + * + *-M*************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include "hosts.h" + +#include "pci2220i.h" +#include "psi_dale.h" + +#include + +struct proc_dir_entry Proc_Scsi_Pci2220i = + { PROC_SCSI_PCI2220I, 7, "pci2220i", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; + +//#define DEBUG 1 + +#ifdef DEBUG +#define DEB(x) x +#define STOP_HERE {int st;for(st=0;st<100;st++){st=1;}} +#else +#define DEB(x) +#define STOP_HERE +#endif + +#define MAXADAPTER 4 /* Increase this and the sizes of the arrays below, if you need more. */ + +#define MAX_BUS_MASTER_BLOCKS 1 // This is the maximum we can bus master for (1024 bytes) + +#define PORT_DATA 0 +#define PORT_ERROR 1 +#define PORT_SECTOR_COUNT 2 +#define PORT_LBA_0 3 +#define PORT_LBA_8 4 +#define PORT_LBA_16 5 +#define PORT_LBA_24 6 +#define PORT_STAT_CMD 7 +#define PORT_STAT_SEL 8 +#define PORT_FAIL 9 +#define PORT_ALT_STAT 10 + +typedef struct + { + UCHAR device; // device code + UCHAR byte6; // device select register image + UCHAR spigot; // spigot number + UCHAR sparebyte; // placeholder + USHORT sectors; // number of sectors per track + USHORT heads; // number of heads + USHORT cylinders; // number of cylinders for this device + USHORT spareword; // placeholder + ULONG blocks; // number of blocks on device + } OUR_DEVICE, *POUR_DEVICE; + +typedef struct + { + USHORT ports[12]; + USHORT regDmaDesc; // address of the DMA discriptor register for direction of transfer + USHORT regDmaCmdStat; // Byte #1 of DMA command status register + USHORT regDmaAddrPci; // 32 bit register for PCI address of DMA + USHORT regDmaAddrLoc; // 32 bit register for local bus address of DMA + USHORT regDmaCount; // 32 bit register for DMA transfer count + USHORT regDmaMode; // 32 bit register for DMA mode control + USHORT regRemap; // 32 bit local space remap + USHORT regDesc; // 32 bit local region descriptor + USHORT regRange; // 32 bit local range + USHORT regIrqControl; // 16 bit Interrupt enable/disable and status + USHORT regScratchPad; // scratch pad I/O base address + USHORT regBase; // Base I/O register for data space + USHORT basePort; // PLX base I/O port + USHORT timingMode; // timing mode currently set for adapter + ULONG timingAddress; // address to use on adapter for current timing mode + OUR_DEVICE device[4]; + IDE_STRUCT ide; + ULONG startSector; + USHORT sectorCount; + Scsi_Cmnd *SCpnt; + VOID *buffer; + USHORT expectingIRQ; + USHORT readPhase; + } ADAPTER2220I, *PADAPTER2220I; + +#define HOSTDATA(host) ((PADAPTER2220I)&host->hostdata) + + +static struct Scsi_Host *PsiHost[MAXADAPTER] = {NULL,}; // One for each adapter +static int NumAdapters = 0; +static IDENTIFY_DATA identifyData; +static SETUP DaleSetup; + +/**************************************************************** + * Name: WriteData :LOCAL + * + * Description: Write data to device. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: TRUE if drive does not assert DRQ in time. + * + ****************************************************************/ +static int WriteData (PADAPTER2220I padapter) + { + ULONG timer; + USHORT *pports = padapter->ports; + + timer = jiffies + TIMEOUT_DRQ; // calculate the timeout value + do { + if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ ) + { + outb_p (0, padapter->regDmaDesc); // write operation + outl (padapter->timingAddress, padapter->regDmaAddrLoc); + outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci); + outl ((ULONG)padapter->ide.ide.ide[2] * (ULONG)512, padapter->regDmaCount); + outb_p (1, padapter->regDmaMode); // interrupts off + outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear + return 0; + } + } while ( timer > jiffies ); // test for timeout + + padapter->ide.ide.ides.cmd = 0; // null out the command byte + return 1; + } +/**************************************************************** + * Name: IdeCmd :LOCAL + * + * Description: Process a queued command from the SCSI manager. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: Zero if no error or status register contents on error. + * + ****************************************************************/ +static UCHAR IdeCmd (PADAPTER2220I padapter) + { + ULONG timer; + USHORT *pports = padapter->ports; + UCHAR status; + + outb_p (padapter->ide.ide.ides.spigot, pports[PORT_STAT_SEL]); // select the spigot + outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]); // select the drive + timer = jiffies + TIMEOUT_READY; // calculate the timeout value + DEB(printk ("\npci2220i Issueing new command: 0x%X",padapter->ide.ide.ides.cmd)); + do { + status = inb_p (padapter->ports[PORT_STAT_CMD]); + if ( status & IDE_STATUS_DRDY ) + { + outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]); + outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]); + outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]); + outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]); + padapter->expectingIRQ = 1; + outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]); + + if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE ) + return (WriteData (padapter)); + return 0; + } + } while ( timer > jiffies ); // test for timeout + + padapter->ide.ide.ides.cmd = 0; // null out the command byte + return status; + } +/**************************************************************** + * Name: SetupTransfer :LOCAL + * + * Description: Setup a data transfer command. + * + * Parameters: padapter - Pointer adapter data structure. + * drive - Drive/head register upper nibble only. + * + * Returns: TRUE if no data to transfer. + * + ****************************************************************/ +static int SetupTransfer (PADAPTER2220I padapter, UCHAR drive) + { + if ( padapter->sectorCount ) + { + *(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector; + padapter->ide.ide.ide[6] |= drive; +// padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount; + padapter->ide.ide.ides.sectors = ( padapter->sectorCount > MAX_BUS_MASTER_BLOCKS ) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount; + padapter->sectorCount -= padapter->ide.ide.ides.sectors; // bump the start and count for next xfer + padapter->startSector += padapter->ide.ide.ides.sectors; + return 0; + } + else + { + padapter->ide.ide.ides.cmd = 0; // null out the command byte + padapter->SCpnt = NULL; + return 1; + } + } +/**************************************************************** + * Name: DecodeError :LOCAL + * + * Description: Decode and process device errors. + * + * Parameters: pshost - Pointer to host data block. + * status - Status register code. + * + * Returns: The driver status code. + * + ****************************************************************/ +static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status) + { + PADAPTER2220I padapter = HOSTDATA(pshost); + UCHAR error; + + padapter->expectingIRQ = 0; + padapter->SCpnt = NULL; + if ( status & IDE_STATUS_WRITE_FAULT ) + { + return DID_PARITY << 16; + } + if ( status & IDE_STATUS_BUSY ) + return DID_BUS_BUSY << 16; + + error = inb_p (padapter->ports[PORT_ERROR]); + DEB(printk ("\npci2220i error register: %x", error)); + switch ( error ) + { + case IDE_ERROR_AMNF: + case IDE_ERROR_TKONF: + case IDE_ERROR_ABRT: + case IDE_ERROR_IDFN: + case IDE_ERROR_UNC: + case IDE_ERROR_BBK: + default: + return DID_ERROR << 16; + } + return DID_ERROR << 16; + } +/**************************************************************** + * Name: Irq_Handler :LOCAL + * + * Description: Interrupt handler. + * + * Parameters: irq - Hardware IRQ number. + * dev_id - + * regs - + * + * Returns: TRUE if drive is not ready in time. + * + ****************************************************************/ +static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) + { + struct Scsi_Host *shost = NULL; // Pointer to host data block + PADAPTER2220I padapter; // Pointer to adapter control structure + USHORT *pports; // I/O port array + Scsi_Cmnd *SCpnt; + UCHAR status; + int z; + +// DEB(printk ("\npci2220i recieved interrupt\n")); + + for ( z = 0; z < NumAdapters; z++ ) // scan for interrupt to process + { + if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) ) + { + if ( inw_p (HOSTDATA(PsiHost[z])->regIrqControl) & 0x8000 ) + { + shost = PsiHost[z]; + break; + } + } + } + + if ( !shost ) + { + DEB (printk ("\npci2220i: not my interrupt")); + return; + } + + padapter = HOSTDATA(shost); + pports = padapter->ports; + SCpnt = padapter->SCpnt; + + if ( !padapter->expectingIRQ ) + { + DEB(printk ("\npci2220i Unsolicited interrupt\n")); + return; + } + padapter->expectingIRQ = 0; + + status = inb_p (padapter->ports[PORT_STAT_CMD]); // read the device status + if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) + goto irqerror; + + switch ( padapter->ide.ide.ides.cmd ) // decide how to handle the interrupt + { + case IDE_CMD_READ_MULTIPLE: + if ( padapter->readPhase == 1 ) // is this a bus master channel complete? + { + DEB(printk ("\npci2220i processing read interrupt cleanup")); + outb_p (0x08, padapter->regDmaCmdStat); // cancel interrupt from DMA engine + padapter->buffer += padapter->ide.ide.ides.sectors * 512; + if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) + { + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + padapter->readPhase = 0; + if ( !(status = IdeCmd (padapter)) ) + { + DEB (printk ("\npci2220i interrupt complete, waiting for another")); + return; + } + } + if ( status & IDE_STATUS_DRQ ) + { + DEB(printk ("\npci2220i processing read interrupt start bus master cycle")); + outb_p (8, padapter->regDmaDesc); // read operation + padapter->readPhase = 1; + padapter->expectingIRQ = 1; + outl (padapter->timingAddress, padapter->regDmaAddrLoc); + outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci); + outl ((ULONG)padapter->ide.ide.ides.sectors * (ULONG)512, padapter->regDmaCount); + outb_p (5, padapter->regDmaMode); // interrupt enable/disable + outb_p (0x03, padapter->regDmaCmdStat); // kick the DMA engine in gear + return; + } + break; + + case IDE_CMD_WRITE_MULTIPLE: + DEB(printk ("\npci2220i processing write interrupt cleanup")); + padapter->buffer += padapter->ide.ide.ides.sectors * 512; + if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) + { + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + if ( !(status = IdeCmd (padapter)) ) + { + DEB (printk ("\npci2220i interrupt complete, waiting for another")); + return; + } + break; + + case IDE_COMMAND_IDENTIFY: + { + PINQUIRYDATA pinquiryData = SCpnt->request_buffer; + + DEB(printk ("\npci2220i processing verify interrupt cleanup")); + if ( status & IDE_STATUS_DRQ ) + { + insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1); + + memset (pinquiryData, 0, SCpnt->request_bufflen); // Zero INQUIRY data structure. + pinquiryData->DeviceType = 0; + pinquiryData->Versions = 2; + pinquiryData->AdditionalLength = 35 - 4; + + // Fill in vendor identification fields. + for ( z = 0; z < 20; z += 2 ) + { + pinquiryData->VendorId[z] = ((UCHAR *)identifyData.ModelNumber)[z + 1]; + pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z]; + } + + // Initialize unused portion of product id. + for ( z = 0; z < 4; z++ ) + pinquiryData->ProductId[12 + z] = ' '; + + // Move firmware revision from IDENTIFY data to + // product revision in INQUIRY data. + for ( z = 0; z < 4; z += 2 ) + { + pinquiryData->ProductRevisionLevel[z] = ((UCHAR *)identifyData.FirmwareRevision)[z + 1]; + pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z]; + } + + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + break; + } + + default: + DEB(printk ("\npci2220i no real process here!")); + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + +irqerror:; + DEB(printk ("\npci2220i error Device Status: %X\n", status)); + SCpnt->result = DecodeError (shost, status); + SCpnt->scsi_done (SCpnt); + } +/**************************************************************** + * Name: Pci2220i_QueueCommand + * + * Description: Process a queued command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * done - Pointer to done function to call. + * + * Returns: Status code. + * + ****************************************************************/ +int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) + { + UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB + PADAPTER2220I padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure + POUR_DEVICE pdev = &padapter->device[SCpnt->target];// Pointer to device information + UCHAR rc; // command return code + + SCpnt->scsi_done = done; + padapter->ide.ide.ides.spigot = pdev->spigot; + padapter->buffer = SCpnt->request_buffer; + if (done) + { + if ( !pdev->device ) + { + SCpnt->result = DID_BAD_TARGET << 16; + done (SCpnt); + return 0; + } + } + else + { + printk("pci2220i_queuecommand: %02X: done can't be NULL\n", *cdb); + return 0; + } + + DEB (if(*cdb) printk ("\nCDB: %X- %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9])); + switch ( *cdb ) + { + case SCSIOP_INQUIRY: // inquiry CDB + { + padapter->ide.ide.ide[6] = pdev->byte6; + padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY; + break; + } + + case SCSIOP_TEST_UNIT_READY: // test unit ready CDB + SCpnt->result = DID_OK << 16; + done (SCpnt); + return 0; + + case SCSIOP_READ_CAPACITY: // read capctiy CDB + { + PREAD_CAPACITY_DATA pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer; + + pdata->blksiz = 0x20000; + XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks); + SCpnt->result = DID_OK << 16; + done (SCpnt); + return 0; + } + + case SCSIOP_VERIFY: // verify CDB + *(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]); + padapter->ide.ide.ide[6] |= pdev->byte6; + padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8)); + padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY; + break; + + case SCSIOP_READ: // read10 CDB + padapter->startSector = XSCSI2LONG (&cdb[2]); + padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8); + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE; + padapter->readPhase = 0; + break; + + case SCSIOP_READ6: // read6 CDB + padapter->startSector = SCSI2LONG (&cdb[1]); + padapter->sectorCount = cdb[4]; + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE; + padapter->readPhase = 0; + break; + + case SCSIOP_WRITE: // write10 CDB + padapter->startSector = XSCSI2LONG (&cdb[2]); + padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8); + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE; + break; + case SCSIOP_WRITE6: // write6 CDB + padapter->startSector = SCSI2LONG (&cdb[1]); + padapter->sectorCount = cdb[4]; + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE; + break; + + default: + DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb)); + SCpnt->result = DID_ERROR << 16; + done (SCpnt); + return 0; + } + + padapter->SCpnt = SCpnt; // Save this command data + + rc = IdeCmd (padapter); + if ( rc ) + { + padapter->expectingIRQ = 0; + DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd)); + SCpnt->result = DID_ERROR << 16; + done (SCpnt); + return 0; + } + if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE ) + { + if ( WriteData (padapter) ) + { + padapter->expectingIRQ = 0; + DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to accept data\n", *cdb, padapter->ide.ide.ides.cmd)); + SCpnt->result = DID_ERROR << 16; + done (SCpnt); + return 0; + } + } + DEB (printk(" now waiting for initial interrupt ")); + return 0; + } + +static void internal_done(Scsi_Cmnd * SCpnt) + { + SCpnt->SCp.Status++; + } +/**************************************************************** + * Name: Pci2220i_Command + * + * Description: Process a command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Status code. + * + ****************************************************************/ +int Pci2220i_Command (Scsi_Cmnd *SCpnt) + { + DEB(printk("pci2220i_command: ..calling pci2220i_queuecommand\n")); + + Pci2220i_QueueCommand (SCpnt, internal_done); + + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier (); + return SCpnt->result; + } +/**************************************************************** + * Name: ReadFlash + * + * Description: Read information from controller Flash memory. + * + * Parameters: hostdata - Pointer to host interface data structure. + * pdata - Pointer to data structures. + * base - base address in Flash. + * length - lenght of data space in bytes. + * + * Returns: Nothing. + * + ****************************************************************/ +VOID ReadFlash (PADAPTER2220I hostdata, VOID *pdata, ULONG base, ULONG length) + { + ULONG oldremap; + UCHAR olddesc; + ULONG z; + UCHAR *pd = (UCHAR *)pdata; + + oldremap = inl (hostdata->regRemap); // save values to restore later + olddesc = inb_p (hostdata->regDesc); + + outl (base | 1, hostdata->regRemap); // remap to Flash space as specified + outb_p (0x40, hostdata->regDesc); // describe remap region as 8 bit + for ( z = 0; z < length; z++) // get "length" data count + *pd++ = inb_p (hostdata->regBase + z); // read in the data + + outl (oldremap, hostdata->regRemap); // restore remap register values + outb_p (olddesc, hostdata->regDesc); + } + +/**************************************************************** + * Name: Pci2220i_Detect + * + * Description: Detect and initialize our boards. + * + * Parameters: tpnt - Pointer to SCSI host template structure. + * + * Returns: Number of adapters found. + * + ****************************************************************/ +int Pci2220i_Detect (Scsi_Host_Template *tpnt) + { + int pci_index = 0; + struct Scsi_Host *pshost; + PADAPTER2220I hostdata; + ULONG modearray[] = {DALE_DATA_MODE2, DALE_DATA_MODE3, DALE_DATA_MODE4, DALE_DATA_MODE4P}; + int unit; + int z; + int setirq; + + if ( pcibios_present () ) + { + for ( pci_index = 0; pci_index <= MAXADAPTER; ++pci_index ) + { + UCHAR pci_bus, pci_device_fn; + + if ( pcibios_find_device (VENDOR_PSI, DEVICE_DALE_1, pci_index, &pci_bus, &pci_device_fn) != 0 ) + break; + + pshost = scsi_register (tpnt, sizeof(ADAPTER2220I)); + hostdata = HOSTDATA(pshost); + + pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &hostdata->basePort); + hostdata->basePort &= 0xFFFE; + DEB (printk ("\nBase Regs = %#04X", hostdata->basePort)); + hostdata->regRemap = hostdata->basePort + RTR_LOCAL_REMAP; // 32 bit local space remap + DEB (printk (" %#04X", hostdata->regRemap)); + hostdata->regDesc = hostdata->basePort + RTR_REGIONS; // 32 bit local region descriptor + DEB (printk (" %#04X", hostdata->regDesc)); + hostdata->regRange = hostdata->basePort + RTR_LOCAL_RANGE; // 32 bit local range + DEB (printk (" %#04X", hostdata->regRange)); + hostdata->regIrqControl = hostdata->basePort + RTR_INT_CONTROL_STATUS; // 16 bit interupt control and status + DEB (printk (" %#04X", hostdata->regIrqControl)); + hostdata->regScratchPad = hostdata->basePort + RTR_MAILBOX; // 16 byte scratchpad I/O base address + DEB (printk (" %#04X", hostdata->regScratchPad)); + + pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &hostdata->regBase); + hostdata->regBase &= 0xFFFE; + for ( z = 0; z < 9; z++ ) // build regester address array + hostdata->ports[z] = hostdata->regBase + 0x80 + (z * 4); + hostdata->ports[PORT_FAIL] = hostdata->regBase + REG_FAIL; + hostdata->ports[PORT_ALT_STAT] = hostdata->regBase + REG_ALT_STAT; + DEB (printk ("\nPorts =")); + DEB (for (z=0;z<11;z++) printk(" %#04X", hostdata->ports[z]);); + + hostdata->regDmaDesc = hostdata->regBase + RTL_DMA1_DESC_PTR; // address of the DMA discriptor register for direction of transfer + DEB (printk ("\nDMA Regs = %#04X", hostdata->regDmaDesc)); + hostdata->regDmaCmdStat = hostdata->regBase + RTL_DMA_COMMAND_STATUS + 1; // Byte #1 of DMA command status register + DEB (printk (" %#04X", hostdata->regDmaCmdStat)); + hostdata->regDmaAddrPci = hostdata->regBase + RTL_DMA1_PCI_ADDR; // 32 bit register for PCI address of DMA + DEB (printk (" %#04X", hostdata->regDmaAddrPci)); + hostdata->regDmaAddrLoc = hostdata->regBase + RTL_DMA1_LOCAL_ADDR; // 32 bit register for local bus address of DMA + DEB (printk (" %#04X", hostdata->regDmaAddrLoc)); + hostdata->regDmaCount = hostdata->regBase + RTL_DMA1_COUNT; // 32 bit register for DMA transfer count + DEB (printk (" %#04X", hostdata->regDmaCount)); + hostdata->regDmaMode = hostdata->regBase + RTL_DMA1_MODE + 1; // 32 bit register for DMA mode control + DEB (printk (" %#04X", hostdata->regDmaMode)); + + if ( !inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES) ) // if no devices on this board + goto unregister; + + pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq); + setirq = 1; + for ( z = 0; z < pci_index; z++ ) // scan for shared interrupts + { + if ( PsiHost[z]->irq == pshost->irq ) // if shared then, don't posses + setirq = 0; + } + if ( setirq ) // if not shared, posses + { + if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2220i", NULL) ) + { + printk ("Unable to allocate IRQ for PSI-2220I controller.\n"); + goto unregister; + } + } + PsiHost[pci_index] = pshost; // save SCSI_HOST pointer + + pshost->unique_id = hostdata->regBase; + pshost->max_id = 4; + + outb_p (0x01, hostdata->regRange); // fix our range register because other drivers want to tromp on it + + hostdata->timingMode = inb_p (hostdata->regScratchPad + DALE_TIMING_MODE); + hostdata->timingAddress = modearray[hostdata->timingMode - 2]; + ReadFlash (hostdata, &DaleSetup, DALE_FLASH_SETUP, sizeof (SETUP)); + + for ( z = 0; z < inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES); ++z ) + { + unit = inb_p (hostdata->regScratchPad + DALE_CHANNEL_DEVICE_0 + z) & 0x0F; + hostdata->device[unit].device = inb_p (hostdata->regScratchPad + DALE_SCRATH_DEVICE_0 + unit); + hostdata->device[unit].byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0); + hostdata->device[unit].spigot = (UCHAR)(1 << (unit >> 1)); + hostdata->device[unit].sectors = DaleSetup.setupDevice[unit].sectors; + hostdata->device[unit].heads = DaleSetup.setupDevice[unit].heads; + hostdata->device[unit].cylinders = DaleSetup.setupDevice[unit].cylinders; + hostdata->device[unit].blocks = DaleSetup.setupDevice[unit].blocks; + DEB (printk ("\nHOSTDATA->device = %X", hostdata->device[unit].device)); + DEB (printk ("\n byte6 = %X", hostdata->device[unit].byte6)); + DEB (printk ("\n spigot = %X", hostdata->device[unit].spigot)); + DEB (printk ("\n sectors = %X", hostdata->device[unit].sectors)); + DEB (printk ("\n heads = %X", hostdata->device[unit].heads)); + DEB (printk ("\n cylinders = %X", hostdata->device[unit].cylinders)); + DEB (printk ("\n blocks = %lX", hostdata->device[unit].blocks)); + } + + printk("\nPSI-2220I EIDE CONTROLLER: at I/O = %X/%X IRQ = %d\n", hostdata->basePort, hostdata->regBase, pshost->irq); + printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n"); + continue; +unregister: + scsi_unregister (pshost); + NumAdapters++; + } + } + return NumAdapters; + } +/**************************************************************** + * Name: Pci2220i_Abort + * + * Description: Process the Abort command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Allways snooze. + * + ****************************************************************/ +int Pci2220i_Abort (Scsi_Cmnd *SCpnt) + { + DEB (printk ("pci2220i_abort\n")); + return SCSI_ABORT_SNOOZE; + } +/**************************************************************** + * Name: Pci2220i_Reset + * + * Description: Process the Reset command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * flags - Flags about the reset command + * + * Returns: No active command at this time, so this means + * that each time we got some kind of response the + * last time through. Tell the mid-level code to + * request sense information in order to decide what + * to do next. + * + ****************************************************************/ +int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) + { + return SCSI_RESET_PUNT; + } + +#include "sd.h" + +/**************************************************************** + * Name: Pci2220i_BiosParam + * + * Description: Process the biosparam request from the SCSI manager to + * return C/H/S data. + * + * Parameters: disk - Pointer to SCSI disk structure. + * dev - Major/minor number from kernel. + * geom - Pointer to integer array to place geometry data. + * + * Returns: zero. + * + ****************************************************************/ +int Pci2220i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[]) + { + POUR_DEVICE pdev; + + pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]); + + geom[0] = pdev->heads; + geom[1] = pdev->sectors; + geom[2] = pdev->cylinders; + return 0; + } + + +#ifdef MODULE +/* Eventually this will go into an include file, but this will be later */ +Scsi_Host_Template driver_template = PCI2220I; + +#include "scsi_module.c" +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/pci2220i.h linux/drivers/scsi/pci2220i.h --- v2.1.62/linux/drivers/scsi/pci2220i.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/pci2220i.h Wed Nov 5 13:06:34 1997 @@ -0,0 +1,345 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: pci2220i.h + * + * Description: Header file for the SCSI driver for the PCI2220I + * EIDE interface card. + * + *-M*************************************************************************/ + +#ifndef _PCI2220I_H +#define _PCI2220I_H + +#include +#include + +#ifndef PSI_EIDE_SCSIOP +#define PSI_EIDE_SCSIOP 1 + +/************************************************/ +/* Some defines that we like */ +/************************************************/ +#define CHAR char +#define UCHAR unsigned char +#define SHORT short +#define USHORT unsigned short +#define BOOL unsigned short +#define LONG long +#define ULONG unsigned long +#define VOID void + +/************************************************/ +/* Timeout konstants */ +/************************************************/ +#define TIMEOUT_READY 10 // 100 mSec +#define TIMEOUT_DRQ 40 // 400 mSec + +/************************************************/ +/* Misc. macros */ +/************************************************/ +#define ANY2SCSI(up, p) \ +((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \ +((UCHAR *)up)[1] = ((ULONG)(p)); + +#define SCSI2LONG(up) \ +( (((long)*(((UCHAR *)up))) << 16) \ ++ (((long)(((UCHAR *)up)[1])) << 8) \ ++ ((long)(((UCHAR *)up)[2])) ) + +#define XANY2SCSI(up, p) \ +((UCHAR *)up)[0] = ((long)(p)) >> 24; \ +((UCHAR *)up)[1] = ((long)(p)) >> 16; \ +((UCHAR *)up)[2] = ((long)(p)) >> 8; \ +((UCHAR *)up)[3] = ((long)(p)); + +#define XSCSI2LONG(up) \ +( (((long)(((UCHAR *)up)[0])) << 24) \ ++ (((long)(((UCHAR *)up)[1])) << 16) \ ++ (((long)(((UCHAR *)up)[2])) << 8) \ ++ ((long)(((UCHAR *)up)[3])) ) + +/************************************************/ +/* SCSI CDB operation codes */ +/************************************************/ +#define SCSIOP_TEST_UNIT_READY 0x00 +#define SCSIOP_REZERO_UNIT 0x01 +#define SCSIOP_REWIND 0x01 +#define SCSIOP_REQUEST_BLOCK_ADDR 0x02 +#define SCSIOP_REQUEST_SENSE 0x03 +#define SCSIOP_FORMAT_UNIT 0x04 +#define SCSIOP_READ_BLOCK_LIMITS 0x05 +#define SCSIOP_REASSIGN_BLOCKS 0x07 +#define SCSIOP_READ6 0x08 +#define SCSIOP_RECEIVE 0x08 +#define SCSIOP_WRITE6 0x0A +#define SCSIOP_PRINT 0x0A +#define SCSIOP_SEND 0x0A +#define SCSIOP_SEEK6 0x0B +#define SCSIOP_TRACK_SELECT 0x0B +#define SCSIOP_SLEW_PRINT 0x0B +#define SCSIOP_SEEK_BLOCK 0x0C +#define SCSIOP_PARTITION 0x0D +#define SCSIOP_READ_REVERSE 0x0F +#define SCSIOP_WRITE_FILEMARKS 0x10 +#define SCSIOP_FLUSH_BUFFER 0x10 +#define SCSIOP_SPACE 0x11 +#define SCSIOP_INQUIRY 0x12 +#define SCSIOP_VERIFY6 0x13 +#define SCSIOP_RECOVER_BUF_DATA 0x14 +#define SCSIOP_MODE_SELECT 0x15 +#define SCSIOP_RESERVE_UNIT 0x16 +#define SCSIOP_RELEASE_UNIT 0x17 +#define SCSIOP_COPY 0x18 +#define SCSIOP_ERASE 0x19 +#define SCSIOP_MODE_SENSE 0x1A +#define SCSIOP_START_STOP_UNIT 0x1B +#define SCSIOP_STOP_PRINT 0x1B +#define SCSIOP_LOAD_UNLOAD 0x1B +#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C +#define SCSIOP_SEND_DIAGNOSTIC 0x1D +#define SCSIOP_MEDIUM_REMOVAL 0x1E +#define SCSIOP_READ_CAPACITY 0x25 +#define SCSIOP_READ 0x28 +#define SCSIOP_WRITE 0x2A +#define SCSIOP_SEEK 0x2B +#define SCSIOP_LOCATE 0x2B +#define SCSIOP_WRITE_VERIFY 0x2E +#define SCSIOP_VERIFY 0x2F +#define SCSIOP_SEARCH_DATA_HIGH 0x30 +#define SCSIOP_SEARCH_DATA_EQUAL 0x31 +#define SCSIOP_SEARCH_DATA_LOW 0x32 +#define SCSIOP_SET_LIMITS 0x33 +#define SCSIOP_READ_POSITION 0x34 +#define SCSIOP_SYNCHRONIZE_CACHE 0x35 +#define SCSIOP_COMPARE 0x39 +#define SCSIOP_COPY_COMPARE 0x3A +#define SCSIOP_WRITE_DATA_BUFF 0x3B +#define SCSIOP_READ_DATA_BUFF 0x3C +#define SCSIOP_CHANGE_DEFINITION 0x40 +#define SCSIOP_READ_SUB_CHANNEL 0x42 +#define SCSIOP_READ_TOC 0x43 +#define SCSIOP_READ_HEADER 0x44 +#define SCSIOP_PLAY_AUDIO 0x45 +#define SCSIOP_PLAY_AUDIO_MSF 0x47 +#define SCSIOP_PLAY_TRACK_INDEX 0x48 +#define SCSIOP_PLAY_TRACK_RELATIVE 0x49 +#define SCSIOP_PAUSE_RESUME 0x4B +#define SCSIOP_LOG_SELECT 0x4C +#define SCSIOP_LOG_SENSE 0x4D +#define SCSIOP_MODE_SELECT10 0x55 +#define SCSIOP_MODE_SENSE10 0x5A +#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6 +#define SCSIOP_MECHANISM_STATUS 0xBD +#define SCSIOP_READ_CD 0xBE + +// IDE command definitions +#define IDE_COMMAND_ATAPI_RESET 0x08 +#define IDE_COMMAND_READ 0x20 +#define IDE_COMMAND_WRITE 0x30 +#define IDE_COMMAND_RECALIBRATE 0x10 +#define IDE_COMMAND_SEEK 0x70 +#define IDE_COMMAND_SET_PARAMETERS 0x91 +#define IDE_COMMAND_VERIFY 0x40 +#define IDE_COMMAND_ATAPI_PACKET 0xA0 +#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1 +#define IDE_CMD_READ_MULTIPLE 0xC4 +#define IDE_CMD_WRITE_MULTIPLE 0xC5 +#define IDE_CMD_SET_MULTIPLE 0xC6 +#define IDE_COMMAND_WRITE_DMA 0xCA +#define IDE_COMMAND_READ_DMA 0xC8 +#define IDE_COMMAND_IDENTIFY 0xEC + +// IDE status definitions +#define IDE_STATUS_ERROR 0x01 +#define IDE_STATUS_INDEX 0x02 +#define IDE_STATUS_CORRECTED_ERROR 0x04 +#define IDE_STATUS_DRQ 0x08 +#define IDE_STATUS_DSC 0x10 +#define IDE_STATUS_WRITE_FAULT 0x20 +#define IDE_STATUS_DRDY 0x40 +#define IDE_STATUS_BUSY 0x80 + +// IDE error definitions +#define IDE_ERROR_AMNF 0x01 +#define IDE_ERROR_TKONF 0x02 +#define IDE_ERROR_ABRT 0x04 +#define IDE_ERROR_MCR 0x08 +#define IDE_ERROR_IDFN 0x10 +#define IDE_ERROR_MC 0x20 +#define IDE_ERROR_UNC 0x40 +#define IDE_ERROR_BBK 0x80 + +// IDE interface structure +typedef struct _IDE_STRUCT + { + union + { + UCHAR ide[9]; + struct + { + USHORT data; + UCHAR sectors; + UCHAR lba[4]; + UCHAR cmd; + UCHAR spigot; + } ides; + } ide; + } IDE_STRUCT; + +// SCSI read capacity structure +typedef struct _READ_CAPACITY_DATA + { + ULONG blks; /* total blocks (converted to little endian) */ + ULONG blksiz; /* size of each (converted to little endian) */ + } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; + +// SCSI inquiry data +typedef struct _INQUIRYDATA + { + UCHAR DeviceType :5; + UCHAR DeviceTypeQualifier :3; + UCHAR DeviceTypeModifier :7; + UCHAR RemovableMedia :1; + UCHAR Versions; + UCHAR ResponseDataFormat; + UCHAR AdditionalLength; + UCHAR Reserved[2]; + UCHAR SoftReset :1; + UCHAR CommandQueue :1; + UCHAR Reserved2 :1; + UCHAR LinkedCommands :1; + UCHAR Synchronous :1; + UCHAR Wide16Bit :1; + UCHAR Wide32Bit :1; + UCHAR RelativeAddressing :1; + UCHAR VendorId[8]; + UCHAR ProductId[16]; + UCHAR ProductRevisionLevel[4]; + UCHAR VendorSpecific[20]; + UCHAR Reserved3[40]; + } INQUIRYDATA, *PINQUIRYDATA; + +// IDE IDENTIFY data +typedef struct _IDENTIFY_DATA + { + USHORT GeneralConfiguration; // 00 + USHORT NumberOfCylinders; // 02 + USHORT Reserved1; // 04 + USHORT NumberOfHeads; // 06 + USHORT UnformattedBytesPerTrack; // 08 + USHORT UnformattedBytesPerSector; // 0A + USHORT SectorsPerTrack; // 0C + USHORT VendorUnique1[3]; // 0E + USHORT SerialNumber[10]; // 14 + USHORT BufferType; // 28 + USHORT BufferSectorSize; // 2A + USHORT NumberOfEccBytes; // 2C + USHORT FirmwareRevision[4]; // 2E + USHORT ModelNumber[20]; // 36 + UCHAR MaximumBlockTransfer; // 5E + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 + USHORT Capabilities; // 62 + USHORT Reserved2; // 64 + UCHAR VendorUnique3; // 66 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C + USHORT NumberOfCurrentHeads; // 6E + USHORT CurrentSectorsPerTrack; // 70 + ULONG CurrentSectorCapacity; // 72 + USHORT Reserved4[197]; // 76 + } IDENTIFY_DATA, *PIDENTIFY_DATA; + +// Identify data without the Reserved4. +typedef struct _IDENTIFY_DATA2 { + USHORT GeneralConfiguration; // 00 + USHORT NumberOfCylinders; // 02 + USHORT Reserved1; // 04 + USHORT NumberOfHeads; // 06 + USHORT UnformattedBytesPerTrack; // 08 + USHORT UnformattedBytesPerSector; // 0A + USHORT SectorsPerTrack; // 0C + USHORT VendorUnique1[3]; // 0E + USHORT SerialNumber[10]; // 14 + USHORT BufferType; // 28 + USHORT BufferSectorSize; // 2A + USHORT NumberOfEccBytes; // 2C + USHORT FirmwareRevision[4]; // 2E + USHORT ModelNumber[20]; // 36 + UCHAR MaximumBlockTransfer; // 5E + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 + USHORT Capabilities; // 62 + USHORT Reserved2; // 64 + UCHAR VendorUnique3; // 66 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C + USHORT NumberOfCurrentHeads; // 6E + USHORT CurrentSectorsPerTrack; // 70 + ULONG CurrentSectorCapacity; // 72 + } IDENTIFY_DATA2, *PIDENTIFY_DATA2; + +#endif // PSI_EIDE_SCSIOP + +// function prototypes +int Pci2220i_Detect (Scsi_Host_Template *tpnt); +int Pci2220i_Command (Scsi_Cmnd *SCpnt); +int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); +int Pci2220i_Abort (Scsi_Cmnd *SCpnt); +int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags); +int Pci2220i_BiosParam (Disk *disk, kdev_t dev, int geom[]); + +#ifndef NULL + #define NULL 0 +#endif + +extern struct proc_dir_entry Proc_Scsi_Pci2220i; + +#define PCI2220I { NULL, NULL, \ + &Proc_Scsi_Pci2220i,/* proc_dir_entry */ \ + NULL, \ + "PCI-2220I EIDE Disk Controller", \ + Pci2220i_Detect, \ + NULL, \ + NULL, \ + Pci2220i_Command, \ + Pci2220i_QueueCommand, \ + Pci2220i_Abort, \ + Pci2220i_Reset, \ + NULL, \ + Pci2220i_BiosParam, \ + 1, \ + -1, \ + SG_NONE, \ + 1, \ + 0, \ + 0, \ + DISABLE_CLUSTERING } + +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/psi240i.c linux/drivers/scsi/psi240i.c --- v2.1.62/linux/drivers/scsi/psi240i.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/psi240i.c Wed Nov 5 13:09:56 1997 @@ -0,0 +1,717 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: psi240i.c + * + * Description: SCSI driver for the PSI240I EIDE interface card. + * + *-M*************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include "hosts.h" + +#include "psi240i.h" +#include "psi_chip.h" + +#include + +struct proc_dir_entry Proc_Scsi_Psi240i = + { PROC_SCSI_PSI240I, 7, "psi240i", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; + +//#define DEBUG 1 + +#ifdef DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + +#define MAXBOARDS 2 /* Increase this and the sizes of the arrays below, if you need more. */ + +#define PORT_DATA 0 +#define PORT_ERROR 1 +#define PORT_SECTOR_COUNT 2 +#define PORT_LBA_0 3 +#define PORT_LBA_8 4 +#define PORT_LBA_16 5 +#define PORT_LBA_24 6 +#define PORT_STAT_CMD 7 +#define PORT_SEL_FAIL 8 +#define PORT_IRQ_STATUS 9 +#define PORT_ADDRESS 10 +#define PORT_FAIL 11 +#define PORT_ALT_STAT 12 + +typedef struct + { + UCHAR device; // device code + UCHAR byte6; // device select register image + UCHAR spigot; // spigot number + UCHAR expectingIRQ; // flag for expecting and interrupt + USHORT sectors; // number of sectors per track + USHORT heads; // number of heads + USHORT cylinders; // number of cylinders for this device + USHORT spareword; // placeholder + ULONG blocks; // number of blocks on device + } OUR_DEVICE, *POUR_DEVICE; + +typedef struct + { + USHORT ports[13]; + OUR_DEVICE device[8]; + Scsi_Cmnd *pSCmnd; + IDE_STRUCT ide; + ULONG startSector; + USHORT sectorCount; + Scsi_Cmnd *SCpnt; + VOID *buffer; + USHORT expectingIRQ; + } ADAPTER240I, *PADAPTER240I; + +#define HOSTDATA(host) ((PADAPTER240I)&host->hostdata) + +static struct Scsi_Host *PsiHost[6] = {NULL,}; /* One for each IRQ level (10-15) */ +static IDENTIFY_DATA identifyData; +static SETUP ChipSetup; + +static USHORT portAddr[6] = {CHIP_ADRS_0, CHIP_ADRS_1, CHIP_ADRS_2, CHIP_ADRS_3, CHIP_ADRS_4, CHIP_ADRS_5}; + +/**************************************************************** + * Name: WriteData :LOCAL + * + * Description: Write data to device. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: TRUE if drive does not assert DRQ in time. + * + ****************************************************************/ +static int WriteData (PADAPTER240I padapter) + { + ULONG timer; + USHORT *pports = padapter->ports; + + timer = jiffies + TIMEOUT_DRQ; // calculate the timeout value + do { + if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ ) + { + outsw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ide[2] * 256); + return 0; + } + } while ( timer > jiffies ); // test for timeout + + padapter->ide.ide.ides.cmd = 0; // null out the command byte + return 1; + } +/**************************************************************** + * Name: IdeCmd :LOCAL + * + * Description: Process a queued command from the SCSI manager. + * + * Parameters: padapter - Pointer adapter data structure. + * + * Returns: Zero if no error or status register contents on error. + * + ****************************************************************/ +static UCHAR IdeCmd (PADAPTER240I padapter) + { + ULONG timer; + USHORT *pports = padapter->ports; + UCHAR status; + + outb_p (padapter->ide.ide.ides.spigot, pports[PORT_SEL_FAIL]); // select the spigot + outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]); // select the drive + timer = jiffies + TIMEOUT_READY; // calculate the timeout value + do { + status = inb_p (padapter->ports[PORT_STAT_CMD]); + if ( status & IDE_STATUS_DRDY ) + { + outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]); + outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]); + outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]); + outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]); + padapter->expectingIRQ = 1; + outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]); + + if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE ) + return (WriteData (padapter)); + + return 0; + } + } while ( timer > jiffies ); // test for timeout + + padapter->ide.ide.ides.cmd = 0; // null out the command byte + return status; + } +/**************************************************************** + * Name: SetupTransfer :LOCAL + * + * Description: Setup a data transfer command. + * + * Parameters: padapter - Pointer adapter data structure. + * drive - Drive/head register upper nibble only. + * + * Returns: TRUE if no data to transfer. + * + ****************************************************************/ +static int SetupTransfer (PADAPTER240I padapter, UCHAR drive) + { + if ( padapter->sectorCount ) + { + *(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector; + padapter->ide.ide.ide[6] |= drive; + padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount; + padapter->sectorCount -= padapter->ide.ide.ides.sectors; // bump the start and count for next xfer + padapter->startSector += padapter->ide.ide.ides.sectors; + return 0; + } + else + { + padapter->ide.ide.ides.cmd = 0; // null out the command byte + padapter->SCpnt = NULL; + return 1; + } + } +/**************************************************************** + * Name: DecodeError :LOCAL + * + * Description: Decode and process device errors. + * + * Parameters: pshost - Pointer to host data block. + * status - Status register code. + * + * Returns: The driver status code. + * + ****************************************************************/ +static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status) + { + PADAPTER240I padapter = HOSTDATA(pshost); + UCHAR error; + + padapter->expectingIRQ = 0; + padapter->SCpnt = NULL; + if ( status & IDE_STATUS_WRITE_FAULT ) + { + return DID_PARITY << 16; + } + if ( status & IDE_STATUS_BUSY ) + return DID_BUS_BUSY << 16; + + error = inb_p (padapter->ports[PORT_ERROR]); + DEB(printk ("\npsi240i error register: %x", error)); + switch ( error ) + { + case IDE_ERROR_AMNF: + case IDE_ERROR_TKONF: + case IDE_ERROR_ABRT: + case IDE_ERROR_IDFN: + case IDE_ERROR_UNC: + case IDE_ERROR_BBK: + default: + return DID_ERROR << 16; + } + return DID_ERROR << 16; + } +/**************************************************************** + * Name: Irq_Handler :LOCAL + * + * Description: Interrupt handler. + * + * Parameters: irq - Hardware IRQ number. + * dev_id - + * regs - + * + * Returns: TRUE if drive is not ready in time. + * + ****************************************************************/ +static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) + { + struct Scsi_Host *shost; // Pointer to host data block + PADAPTER240I padapter; // Pointer to adapter control structure + USHORT *pports; // I/O port array + Scsi_Cmnd *SCpnt; + UCHAR status; + int z; + + DEB(printk ("\npsi240i recieved interrupt\n")); + + shost = PsiHost[irq - 10]; + if ( !shost ) + panic ("Splunge!"); + + padapter = HOSTDATA(shost); + pports = padapter->ports; + SCpnt = padapter->SCpnt; + + if ( !padapter->expectingIRQ ) + { + DEB(printk ("\npsi240i Unsolicited interrupt\n")); + return; + } + padapter->expectingIRQ = 0; + + status = inb_p (padapter->ports[PORT_STAT_CMD]); // read the device status + if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) + goto irqerror; + + DEB(printk ("\npsi240i processing interrupt")); + switch ( padapter->ide.ide.ides.cmd ) // decide how to handle the interrupt + { + case IDE_CMD_READ_MULTIPLE: + if ( status & IDE_STATUS_DRQ ) + { + insw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ides.sectors * 256); + padapter->buffer += padapter->ide.ide.ides.sectors * 512; + if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) + { + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + if ( !(status = IdeCmd (padapter)) ) + return; + } + break; + + case IDE_CMD_WRITE_MULTIPLE: + padapter->buffer += padapter->ide.ide.ides.sectors * 512; + if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) + { + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + if ( !(status = IdeCmd (padapter)) ) + return; + break; + + case IDE_COMMAND_IDENTIFY: + { + PINQUIRYDATA pinquiryData = SCpnt->request_buffer; + + if ( status & IDE_STATUS_DRQ ) + { + insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1); + + memset (pinquiryData, 0, SCpnt->request_bufflen); // Zero INQUIRY data structure. + pinquiryData->DeviceType = 0; + pinquiryData->Versions = 2; + pinquiryData->AdditionalLength = 35 - 4; + + // Fill in vendor identification fields. + for ( z = 0; z < 20; z += 2 ) + { + pinquiryData->VendorId[z] = ((UCHAR *)identifyData.ModelNumber)[z + 1]; + pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z]; + } + + // Initialize unused portion of product id. + for ( z = 0; z < 4; z++ ) + pinquiryData->ProductId[12 + z] = ' '; + + // Move firmware revision from IDENTIFY data to + // product revision in INQUIRY data. + for ( z = 0; z < 4; z += 2 ) + { + pinquiryData->ProductRevisionLevel[z] = ((UCHAR *)identifyData.FirmwareRevision)[z + 1]; + pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z]; + } + + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + break; + } + + default: + SCpnt->result = DID_OK << 16; + padapter->SCpnt = NULL; + SCpnt->scsi_done (SCpnt); + return; + } + +irqerror:; + DEB(printk ("\npsi240i error Device Status: %X\n", status)); + SCpnt->result = DecodeError (shost, status); + SCpnt->scsi_done (SCpnt); + } +/**************************************************************** + * Name: Psi240i_QueueCommand + * + * Description: Process a queued command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * done - Pointer to done function to call. + * + * Returns: Status code. + * + ****************************************************************/ +int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) + { + UCHAR *cdb = (UCHAR *)SCpnt->cmnd; // Pointer to SCSI CDB + PADAPTER240I padapter = HOSTDATA(SCpnt->host); // Pointer to adapter control structure + POUR_DEVICE pdev = &padapter->device[SCpnt->target];// Pointer to device information + UCHAR rc; // command return code + + SCpnt->scsi_done = done; + padapter->ide.ide.ides.spigot = pdev->spigot; + padapter->buffer = SCpnt->request_buffer; + if (done) + { + if ( !pdev->device ) + { + SCpnt->result = DID_BAD_TARGET << 16; + done (SCpnt); + return 0; + } + } + else + { + printk("psi240i_queuecommand: %02X: done can't be NULL\n", *cdb); + return 0; + } + + switch ( *cdb ) + { + case SCSIOP_INQUIRY: // inquiry CDB + { + padapter->ide.ide.ide[6] = pdev->byte6; + padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY; + break; + } + + case SCSIOP_TEST_UNIT_READY: // test unit ready CDB + SCpnt->result = DID_OK << 16; + done (SCpnt); + return 0; + + case SCSIOP_READ_CAPACITY: // read capctiy CDB + { + PREAD_CAPACITY_DATA pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer; + + pdata->blksiz = 0x20000; + XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks); + SCpnt->result = DID_OK << 16; + done (SCpnt); + return 0; + } + + case SCSIOP_VERIFY: // verify CDB + *(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]); + padapter->ide.ide.ide[6] |= pdev->byte6; + padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8)); + padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY; + break; + + case SCSIOP_READ: // read10 CDB + padapter->startSector = XSCSI2LONG (&cdb[2]); + padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8); + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE; + break; + + case SCSIOP_READ6: // read6 CDB + padapter->startSector = SCSI2LONG (&cdb[1]); + padapter->sectorCount = cdb[4]; + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE; + break; + + case SCSIOP_WRITE: // write10 CDB + padapter->startSector = XSCSI2LONG (&cdb[2]); + padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8); + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE; + break; + case SCSIOP_WRITE6: // write6 CDB + padapter->startSector = SCSI2LONG (&cdb[1]); + padapter->sectorCount = cdb[4]; + SetupTransfer (padapter, pdev->byte6); + padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE; + break; + + default: + DEB (printk ("psi240i_queuecommand: Unsupported command %02X\n", *cdb)); + SCpnt->result = DID_ERROR << 16; + done (SCpnt); + return 0; + } + + padapter->SCpnt = SCpnt; // Save this command data + + rc = IdeCmd (padapter); + if ( rc ) + { + padapter->expectingIRQ = 0; + DEB (printk ("psi240i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd)); + SCpnt->result = DID_ERROR << 16; + done (SCpnt); + return 0; + } + DEB (printk("psi240i_queuecommand: %02X, %02X now waiting for interrupt ", *cdb, padapter->ide.ide.ides.cmd)); + return 0; + } + +static void internal_done(Scsi_Cmnd * SCpnt) + { + SCpnt->SCp.Status++; + } +/**************************************************************** + * Name: Psi240i_Command + * + * Description: Process a command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Status code. + * + ****************************************************************/ +int Psi240i_Command (Scsi_Cmnd *SCpnt) + { + DEB(printk("psi240i_command: ..calling psi240i_queuecommand\n")); + + Psi240i_QueueCommand (SCpnt, internal_done); + + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier (); + return SCpnt->result; + } +/*************************************************************************** + * Name: ReadChipMemory + * + * Description: Read information from controller memory. + * + * Parameters: psetup - Pointer to memory image of setup information. + * base - base address of memory. + * length - lenght of data space in bytes. + * port - I/O address of data port. + * + * Returns: Nothing. + * + **************************************************************************/ +void ReadChipMemory (void *pdata, USHORT base, USHORT length, USHORT port) + { + USHORT z, zz; + UCHAR *pd = (UCHAR *)pdata; + outb_p (SEL_NONE, port + REG_SEL_FAIL); // setup data port + zz = 0; + while ( zz < length ) + { + outw_p (base, port + REG_ADDRESS); // setup address + + for ( z = 0; z < 8; z++ ) + { + if ( (zz + z) < length ) + *pd++ = inb_p (port + z); // read data byte + } + zz += 8; + base += 8; + } + } +/**************************************************************** + * Name: Psi240i_Detect + * + * Description: Detect and initialize our boards. + * + * Parameters: tpnt - Pointer to SCSI host template structure. + * + * Returns: Number of adapters found. + * + ****************************************************************/ +int Psi240i_Detect (Scsi_Host_Template *tpnt) + { + int board; + int count = 0; + int unit; + int z; + USHORT port; + CHIP_CONFIG_N chipConfig; + CHIP_DEVICE_N chipDevice[8]; + struct Scsi_Host *pshost; + ULONG flags; + + for ( board = 0; board < 6; board++ ) // scan for I/O ports + { + port = portAddr[board]; // get base address to test + if ( check_region (port, 16) ) // test for I/O addresses available + continue; // nope + if ( inb_p (port + REG_FAIL) != CHIP_ID ) // do the first test for likley hood that it is us + continue; + outb_p (SEL_NONE, port + REG_SEL_FAIL); // setup EEPROM/RAM access + outw (0, port + REG_ADDRESS); // setup EEPROM address zero + if ( inb_p (port) != 0x55 ) // test 1st byte + continue; // nope + if ( inb_p (port + 1) != 0xAA ) // test 2nd byte + continue; // nope + + // at this point our board is found and can be accessed. Now we need to initialize + // our informatation and register with the kernel. + + + ReadChipMemory (&chipConfig, CHIP_CONFIG, sizeof (chipConfig), port); + ReadChipMemory (&chipDevice, CHIP_DEVICE, sizeof (chipDevice), port); + ReadChipMemory (&ChipSetup, CHIP_EEPROM_DATA, sizeof (ChipSetup), port); + + if ( !chipConfig.numDrives ) // if no devices on this board + continue; + + pshost = scsi_register (tpnt, sizeof(ADAPTER240I)); + + save_flags (flags); + cli (); + if ( request_irq (chipConfig.irq, Irq_Handler, 0, "psi240i", NULL) ) + { + printk ("Unable to allocate IRQ for PSI-240I controller.\n"); + restore_flags (flags); + goto unregister; + } + + PsiHost[chipConfig.irq - 10] = pshost; + pshost->unique_id = port; + pshost->io_port = port; + pshost->n_io_port = 16; /* Number of bytes of I/O space used */ + pshost->irq = chipConfig.irq; + + for ( z = 0; z < 11; z++ ) // build regester address array + HOSTDATA(pshost)->ports[z] = port + z; + HOSTDATA(pshost)->ports[11] = port + REG_FAIL; + HOSTDATA(pshost)->ports[12] = port + REG_ALT_STAT; + DEB (printk ("\nPorts =")); + DEB (for (z=0;z<13;z++) printk(" %#04X",HOSTDATA(pshost)->ports[z]);); + + for ( z = 0; z < chipConfig.numDrives; ++z ) + { + unit = chipDevice[z].channel & 0x0F; + HOSTDATA(pshost)->device[unit].device = ChipSetup.setupDevice[unit].device; + HOSTDATA(pshost)->device[unit].byte6 = (UCHAR)(((unit & 1) << 4) | 0xE0); + HOSTDATA(pshost)->device[unit].spigot = (UCHAR)(1 << (unit >> 1)); + HOSTDATA(pshost)->device[unit].sectors = ChipSetup.setupDevice[unit].sectors; + HOSTDATA(pshost)->device[unit].heads = ChipSetup.setupDevice[unit].heads; + HOSTDATA(pshost)->device[unit].cylinders = ChipSetup.setupDevice[unit].cylinders; + HOSTDATA(pshost)->device[unit].blocks = ChipSetup.setupDevice[unit].blocks; + DEB (printk ("\nHOSTDATA->device = %X", HOSTDATA(pshost)->device[unit].device)); + DEB (printk ("\n byte6 = %X", HOSTDATA(pshost)->device[unit].byte6)); + DEB (printk ("\n spigot = %X", HOSTDATA(pshost)->device[unit].spigot)); + DEB (printk ("\n sectors = %X", HOSTDATA(pshost)->device[unit].sectors)); + DEB (printk ("\n heads = %X", HOSTDATA(pshost)->device[unit].heads)); + DEB (printk ("\n cylinders = %X", HOSTDATA(pshost)->device[unit].cylinders)); + DEB (printk ("\n blocks = %lX", HOSTDATA(pshost)->device[unit].blocks)); + } + + restore_flags (flags); + printk("\nPSI-240I EIDE CONTROLLER: at I/O = %x IRQ = %d\n", port, chipConfig.irq); + printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n"); + count++; + continue; + +unregister:; + scsi_unregister (pshost); + } + return count; + } +/**************************************************************** + * Name: Psi240i_Abort + * + * Description: Process the Abort command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * + * Returns: Allways snooze. + * + ****************************************************************/ +int Psi240i_Abort (Scsi_Cmnd *SCpnt) + { + DEB (printk ("psi240i_abort\n")); + return SCSI_ABORT_SNOOZE; + } +/**************************************************************** + * Name: Psi240i_Reset + * + * Description: Process the Reset command from the SCSI manager. + * + * Parameters: SCpnt - Pointer to SCSI command structure. + * flags - Flags about the reset command + * + * Returns: No active command at this time, so this means + * that each time we got some kind of response the + * last time through. Tell the mid-level code to + * request sense information in order to decide what + * to do next. + * + ****************************************************************/ +int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags) + { + return SCSI_RESET_PUNT; + } + +#include "sd.h" + +/**************************************************************** + * Name: Psi240i_BiosParam + * + * Description: Process the biosparam request from the SCSI manager to + * return C/H/S data. + * + * Parameters: disk - Pointer to SCSI disk structure. + * dev - Major/minor number from kernel. + * geom - Pointer to integer array to place geometry data. + * + * Returns: zero. + * + ****************************************************************/ +int Psi240i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[]) + { + POUR_DEVICE pdev; + + pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]); + + geom[0] = pdev->heads; + geom[1] = pdev->sectors; + geom[2] = pdev->cylinders; + return 0; + } + + +#ifdef MODULE +/* Eventually this will go into an include file, but this will be later */ +Scsi_Host_Template driver_template = PSI240I; + +#include "scsi_module.c" +#endif + diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/psi240i.h linux/drivers/scsi/psi240i.h --- v2.1.62/linux/drivers/scsi/psi240i.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/psi240i.h Wed Nov 5 13:10:00 1997 @@ -0,0 +1,344 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: psi240i.h + * + * Description: Header file for the SCSI driver for the PSI240I + * EIDE interface card. + * + *-M*************************************************************************/ +#ifndef _PSI240I_H +#define _PSI240I_H + +#include +#include + +#ifndef PSI_EIDE_SCSIOP +#define PSI_EIDE_SCSIOP 1 + +/************************************************/ +/* Some defines that we like */ +/************************************************/ +#define CHAR char +#define UCHAR unsigned char +#define SHORT short +#define USHORT unsigned short +#define BOOL unsigned short +#define LONG long +#define ULONG unsigned long +#define VOID void + +/************************************************/ +/* Timeout konstants */ +/************************************************/ +#define TIMEOUT_READY 10 // 100 mSec +#define TIMEOUT_DRQ 40 // 400 mSec + +/************************************************/ +/* Misc. macros */ +/************************************************/ +#define ANY2SCSI(up, p) \ +((UCHAR *)up)[0] = (((ULONG)(p)) >> 8); \ +((UCHAR *)up)[1] = ((ULONG)(p)); + +#define SCSI2LONG(up) \ +( (((long)*(((UCHAR *)up))) << 16) \ ++ (((long)(((UCHAR *)up)[1])) << 8) \ ++ ((long)(((UCHAR *)up)[2])) ) + +#define XANY2SCSI(up, p) \ +((UCHAR *)up)[0] = ((long)(p)) >> 24; \ +((UCHAR *)up)[1] = ((long)(p)) >> 16; \ +((UCHAR *)up)[2] = ((long)(p)) >> 8; \ +((UCHAR *)up)[3] = ((long)(p)); + +#define XSCSI2LONG(up) \ +( (((long)(((UCHAR *)up)[0])) << 24) \ ++ (((long)(((UCHAR *)up)[1])) << 16) \ ++ (((long)(((UCHAR *)up)[2])) << 8) \ ++ ((long)(((UCHAR *)up)[3])) ) + +/************************************************/ +/* SCSI CDB operation codes */ +/************************************************/ +#define SCSIOP_TEST_UNIT_READY 0x00 +#define SCSIOP_REZERO_UNIT 0x01 +#define SCSIOP_REWIND 0x01 +#define SCSIOP_REQUEST_BLOCK_ADDR 0x02 +#define SCSIOP_REQUEST_SENSE 0x03 +#define SCSIOP_FORMAT_UNIT 0x04 +#define SCSIOP_READ_BLOCK_LIMITS 0x05 +#define SCSIOP_REASSIGN_BLOCKS 0x07 +#define SCSIOP_READ6 0x08 +#define SCSIOP_RECEIVE 0x08 +#define SCSIOP_WRITE6 0x0A +#define SCSIOP_PRINT 0x0A +#define SCSIOP_SEND 0x0A +#define SCSIOP_SEEK6 0x0B +#define SCSIOP_TRACK_SELECT 0x0B +#define SCSIOP_SLEW_PRINT 0x0B +#define SCSIOP_SEEK_BLOCK 0x0C +#define SCSIOP_PARTITION 0x0D +#define SCSIOP_READ_REVERSE 0x0F +#define SCSIOP_WRITE_FILEMARKS 0x10 +#define SCSIOP_FLUSH_BUFFER 0x10 +#define SCSIOP_SPACE 0x11 +#define SCSIOP_INQUIRY 0x12 +#define SCSIOP_VERIFY6 0x13 +#define SCSIOP_RECOVER_BUF_DATA 0x14 +#define SCSIOP_MODE_SELECT 0x15 +#define SCSIOP_RESERVE_UNIT 0x16 +#define SCSIOP_RELEASE_UNIT 0x17 +#define SCSIOP_COPY 0x18 +#define SCSIOP_ERASE 0x19 +#define SCSIOP_MODE_SENSE 0x1A +#define SCSIOP_START_STOP_UNIT 0x1B +#define SCSIOP_STOP_PRINT 0x1B +#define SCSIOP_LOAD_UNLOAD 0x1B +#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C +#define SCSIOP_SEND_DIAGNOSTIC 0x1D +#define SCSIOP_MEDIUM_REMOVAL 0x1E +#define SCSIOP_READ_CAPACITY 0x25 +#define SCSIOP_READ 0x28 +#define SCSIOP_WRITE 0x2A +#define SCSIOP_SEEK 0x2B +#define SCSIOP_LOCATE 0x2B +#define SCSIOP_WRITE_VERIFY 0x2E +#define SCSIOP_VERIFY 0x2F +#define SCSIOP_SEARCH_DATA_HIGH 0x30 +#define SCSIOP_SEARCH_DATA_EQUAL 0x31 +#define SCSIOP_SEARCH_DATA_LOW 0x32 +#define SCSIOP_SET_LIMITS 0x33 +#define SCSIOP_READ_POSITION 0x34 +#define SCSIOP_SYNCHRONIZE_CACHE 0x35 +#define SCSIOP_COMPARE 0x39 +#define SCSIOP_COPY_COMPARE 0x3A +#define SCSIOP_WRITE_DATA_BUFF 0x3B +#define SCSIOP_READ_DATA_BUFF 0x3C +#define SCSIOP_CHANGE_DEFINITION 0x40 +#define SCSIOP_READ_SUB_CHANNEL 0x42 +#define SCSIOP_READ_TOC 0x43 +#define SCSIOP_READ_HEADER 0x44 +#define SCSIOP_PLAY_AUDIO 0x45 +#define SCSIOP_PLAY_AUDIO_MSF 0x47 +#define SCSIOP_PLAY_TRACK_INDEX 0x48 +#define SCSIOP_PLAY_TRACK_RELATIVE 0x49 +#define SCSIOP_PAUSE_RESUME 0x4B +#define SCSIOP_LOG_SELECT 0x4C +#define SCSIOP_LOG_SENSE 0x4D +#define SCSIOP_MODE_SELECT10 0x55 +#define SCSIOP_MODE_SENSE10 0x5A +#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6 +#define SCSIOP_MECHANISM_STATUS 0xBD +#define SCSIOP_READ_CD 0xBE + +// IDE command definitions +#define IDE_COMMAND_ATAPI_RESET 0x08 +#define IDE_COMMAND_READ 0x20 +#define IDE_COMMAND_WRITE 0x30 +#define IDE_COMMAND_RECALIBRATE 0x10 +#define IDE_COMMAND_SEEK 0x70 +#define IDE_COMMAND_SET_PARAMETERS 0x91 +#define IDE_COMMAND_VERIFY 0x40 +#define IDE_COMMAND_ATAPI_PACKET 0xA0 +#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1 +#define IDE_CMD_READ_MULTIPLE 0xC4 +#define IDE_CMD_WRITE_MULTIPLE 0xC5 +#define IDE_CMD_SET_MULTIPLE 0xC6 +#define IDE_COMMAND_WRITE_DMA 0xCA +#define IDE_COMMAND_READ_DMA 0xC8 +#define IDE_COMMAND_IDENTIFY 0xEC + +// IDE status definitions +#define IDE_STATUS_ERROR 0x01 +#define IDE_STATUS_INDEX 0x02 +#define IDE_STATUS_CORRECTED_ERROR 0x04 +#define IDE_STATUS_DRQ 0x08 +#define IDE_STATUS_DSC 0x10 +#define IDE_STATUS_WRITE_FAULT 0x20 +#define IDE_STATUS_DRDY 0x40 +#define IDE_STATUS_BUSY 0x80 + +// IDE error definitions +#define IDE_ERROR_AMNF 0x01 +#define IDE_ERROR_TKONF 0x02 +#define IDE_ERROR_ABRT 0x04 +#define IDE_ERROR_MCR 0x08 +#define IDE_ERROR_IDFN 0x10 +#define IDE_ERROR_MC 0x20 +#define IDE_ERROR_UNC 0x40 +#define IDE_ERROR_BBK 0x80 + +// IDE interface structure +typedef struct _IDE_STRUCT + { + union + { + UCHAR ide[9]; + struct + { + USHORT data; + UCHAR sectors; + UCHAR lba[4]; + UCHAR cmd; + UCHAR spigot; + } ides; + } ide; + } IDE_STRUCT; + +// SCSI read capacity structure +typedef struct _READ_CAPACITY_DATA + { + ULONG blks; /* total blocks (converted to little endian) */ + ULONG blksiz; /* size of each (converted to little endian) */ + } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; + +// SCSI inquiry data +typedef struct _INQUIRYDATA + { + UCHAR DeviceType :5; + UCHAR DeviceTypeQualifier :3; + UCHAR DeviceTypeModifier :7; + UCHAR RemovableMedia :1; + UCHAR Versions; + UCHAR ResponseDataFormat; + UCHAR AdditionalLength; + UCHAR Reserved[2]; + UCHAR SoftReset :1; + UCHAR CommandQueue :1; + UCHAR Reserved2 :1; + UCHAR LinkedCommands :1; + UCHAR Synchronous :1; + UCHAR Wide16Bit :1; + UCHAR Wide32Bit :1; + UCHAR RelativeAddressing :1; + UCHAR VendorId[8]; + UCHAR ProductId[16]; + UCHAR ProductRevisionLevel[4]; + UCHAR VendorSpecific[20]; + UCHAR Reserved3[40]; + } INQUIRYDATA, *PINQUIRYDATA; + +// IDE IDENTIFY data +typedef struct _IDENTIFY_DATA + { + USHORT GeneralConfiguration; // 00 + USHORT NumberOfCylinders; // 02 + USHORT Reserved1; // 04 + USHORT NumberOfHeads; // 06 + USHORT UnformattedBytesPerTrack; // 08 + USHORT UnformattedBytesPerSector; // 0A + USHORT SectorsPerTrack; // 0C + USHORT VendorUnique1[3]; // 0E + USHORT SerialNumber[10]; // 14 + USHORT BufferType; // 28 + USHORT BufferSectorSize; // 2A + USHORT NumberOfEccBytes; // 2C + USHORT FirmwareRevision[4]; // 2E + USHORT ModelNumber[20]; // 36 + UCHAR MaximumBlockTransfer; // 5E + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 + USHORT Capabilities; // 62 + USHORT Reserved2; // 64 + UCHAR VendorUnique3; // 66 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C + USHORT NumberOfCurrentHeads; // 6E + USHORT CurrentSectorsPerTrack; // 70 + ULONG CurrentSectorCapacity; // 72 + USHORT Reserved4[197]; // 76 + } IDENTIFY_DATA, *PIDENTIFY_DATA; + +// Identify data without the Reserved4. +typedef struct _IDENTIFY_DATA2 { + USHORT GeneralConfiguration; // 00 + USHORT NumberOfCylinders; // 02 + USHORT Reserved1; // 04 + USHORT NumberOfHeads; // 06 + USHORT UnformattedBytesPerTrack; // 08 + USHORT UnformattedBytesPerSector; // 0A + USHORT SectorsPerTrack; // 0C + USHORT VendorUnique1[3]; // 0E + USHORT SerialNumber[10]; // 14 + USHORT BufferType; // 28 + USHORT BufferSectorSize; // 2A + USHORT NumberOfEccBytes; // 2C + USHORT FirmwareRevision[4]; // 2E + USHORT ModelNumber[20]; // 36 + UCHAR MaximumBlockTransfer; // 5E + UCHAR VendorUnique2; // 5F + USHORT DoubleWordIo; // 60 + USHORT Capabilities; // 62 + USHORT Reserved2; // 64 + UCHAR VendorUnique3; // 66 + UCHAR PioCycleTimingMode; // 67 + UCHAR VendorUnique4; // 68 + UCHAR DmaCycleTimingMode; // 69 + USHORT TranslationFieldsValid:1; // 6A + USHORT Reserved3:15; + USHORT NumberOfCurrentCylinders; // 6C + USHORT NumberOfCurrentHeads; // 6E + USHORT CurrentSectorsPerTrack; // 70 + ULONG CurrentSectorCapacity; // 72 + } IDENTIFY_DATA2, *PIDENTIFY_DATA2; + +#endif // PSI_EIDE_SCSIOP + +// function prototypes +int Psi240i_Detect (Scsi_Host_Template *tpnt); +int Psi240i_Command (Scsi_Cmnd *SCpnt); +int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)); +int Psi240i_Abort (Scsi_Cmnd *SCpnt); +int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int flags); +int Psi240i_BiosParam (Disk *disk, kdev_t dev, int geom[]); + +#ifndef NULL + #define NULL 0 +#endif + +extern struct proc_dir_entry Proc_Scsi_Psi240i; + +#define PSI240I { NULL, NULL, \ + &Proc_Scsi_Psi240i,/* proc_dir_entry */ \ + NULL, \ + "PSI-240I EIDE Disk Controller", \ + Psi240i_Detect, \ + NULL, \ + NULL, \ + Psi240i_Command, \ + Psi240i_QueueCommand, \ + Psi240i_Abort, \ + Psi240i_Reset, \ + NULL, \ + Psi240i_BiosParam, \ + 1, \ + -1, \ + SG_NONE, \ + 1, \ + 0, \ + 0, \ + DISABLE_CLUSTERING } + +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/psi_chip.h linux/drivers/scsi/psi_chip.h --- v2.1.62/linux/drivers/scsi/psi_chip.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/psi_chip.h Wed Nov 5 13:10:06 1997 @@ -0,0 +1,194 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: psi_chip.h + * + * Description: This file contains the interface defines and + * error codes. + * + *-M*************************************************************************/ +#ifndef PSI_CHIP +#define PSI_CHIP + +/************************************************/ +/* Misc konstants */ +/************************************************/ +#define CHIP_MAXDRIVES 8 + +/************************************************/ +/* Chip I/O addresses */ +/************************************************/ +#define CHIP_ADRS_0 0x0130 +#define CHIP_ADRS_1 0x0150 +#define CHIP_ADRS_2 0x0190 +#define CHIP_ADRS_3 0x0210 +#define CHIP_ADRS_4 0x0230 +#define CHIP_ADRS_5 0x0250 + +/************************************************/ +/* EEPROM locations */ +/************************************************/ +#define CHIP_EEPROM_BIOS 0x0000 // BIOS base address +#define CHIP_EEPROM_DATA 0x2000 // SETUP data base address +#define CHIP_EEPROM_FACTORY 0x2400 // FACTORY data base address +#define CHIP_EEPROM_SETUP 0x3000 // SETUP PROGRAM base address + +#define CHIP_EEPROM_SIZE 32768U // size of the entire EEPROM +#define CHIP_EEPROM_BIOS_SIZE 8192 // size of the BIOS in bytes +#define CHIP_EEPROM_DATA_SIZE 4096 // size of factory, setup, log data block in bytes +#define CHIP_EEPROM_SETUP_SIZE 20480U // size of the setup program in bytes + +/************************************************/ +/* Chip Interrupts */ +/************************************************/ +#define CHIP_IRQ_10 0x72 +#define CHIP_IRQ_11 0x73 +#define CHIP_IRQ_12 0x74 + +/************************************************/ +/* Chip Setup addresses */ +/************************************************/ +#define CHIP_SETUP_BASE 0x0000C000L + +/************************************************/ +/* Chip Register address offsets */ +/************************************************/ +#define REG_DATA 0x00 +#define REG_ERROR 0x01 +#define REG_SECTOR_COUNT 0x02 +#define REG_LBA_0 0x03 +#define REG_LBA_8 0x04 +#define REG_LBA_16 0x05 +#define REG_LBA_24 0x06 +#define REG_STAT_CMD 0x07 +#define REG_SEL_FAIL 0x08 +#define REG_IRQ_STATUS 0x09 +#define REG_ADDRESS 0x0A +#define REG_FAIL 0x0C +#define REG_ALT_STAT 0x0E +#define REG_DRIVE_ADRS 0x0F + +/************************************************/ +/* Chip RAM locations */ +/************************************************/ +#define CHIP_DEVICE 0x8000 +#define CHIP_DEVICE_0 0x8000 +#define CHIP_DEVICE_1 0x8008 +#define CHIP_DEVICE_2 0x8010 +#define CHIP_DEVICE_3 0x8018 +#define CHIP_DEVICE_4 0x8020 +#define CHIP_DEVICE_5 0x8028 +#define CHIP_DEVICE_6 0x8030 +#define CHIP_DEVICE_7 0x8038 +typedef struct + { + UCHAR channel; // channel of this device (0-8). + UCHAR spt; // Sectors Per Track. + ULONG spc; // Sectors Per Cylinder. + } CHIP_DEVICE_N; + +#define CHIP_CONFIG 0x8100 // address of boards configuration. +typedef struct + { + UCHAR irq; // interrupt request channel number + UCHAR numDrives; // Number of accessable drives + UCHAR fastFormat; // Boolean for fast format enable + } CHIP_CONFIG_N; + +#define CHIP_MAP 0x8108 // eight byte device type map. + + +#define CHIP_RAID 0x8120 // array of RAID signature structures and LBA +#define CHIP_RAID_1 0x8120 +#define CHIP_RAID_2 0x8130 +#define CHIP_RAID_3 0x8140 +#define CHIP_RAID_4 0x8150 + +/************************************************/ +/* Chip Register Masks */ +/************************************************/ +#define CHIP_ID 0x7B +#define SEL_RAM 0x8000 +#define MASK_FAIL 0x80 + +/************************************************/ +/* Chip cable select bits */ +/************************************************/ +#define SECTORSXFER 8 + +/************************************************/ +/* Chip cable select bits */ +/************************************************/ +#define SEL_NONE 0x00 +#define SEL_1 0x01 +#define SEL_2 0x02 +#define SEL_3 0x04 +#define SEL_4 0x08 + +/************************************************/ +/* Programmable Interrupt Controller*/ +/************************************************/ +#define PIC1 0x20 // first 8259 base port address +#define PIC2 0xA0 // second 8259 base port address +#define INT_OCW1 1 // Operation Control Word 1: IRQ mask +#define EOI 0x20 // non-specific end-of-interrupt + +/************************************************/ +/* Device/Geometry controls */ +/************************************************/ +#define GEOMETRY_NONE 0x0 // No device +#define GEOMETRY_AUTO 0x1 // Geometry set automatically +#define GEOMETRY_USER 0x2 // User supplied geometry + +#define DEVICE_NONE 0x0 // No device present +#define DEVICE_INACTIVE 0x1 // device present but not registered active +#define DEVICE_ATAPI 0x2 // ATAPI device (CD_ROM, Tape, Etc...) +#define DEVICE_DASD_NONLBA 0x3 // Non LBA incompatible device +#define DEVICE_DASD_LBA 0x4 // LBA compatible device + +/************************************************/ +/* Setup Structure Definitions */ +/************************************************/ +typedef struct // device setup parameters + { + UCHAR geometryControl; // geometry control flags + UCHAR device; // device code + USHORT sectors; // number of sectors per track + USHORT heads; // number of heads + USHORT cylinders; // number of cylinders for this device + ULONG blocks; // number of blocks on device + USHORT spare1; + USHORT spare2; + } SETUP_DEVICE, *PSETUP_DEVICE; + +typedef struct // master setup structure + { + USHORT startupDelay; + USHORT promptBIOS; + USHORT fastFormat; + USHORT spare2; + USHORT spare3; + USHORT spare4; + USHORT spare5; + USHORT spare6; + SETUP_DEVICE setupDevice[8]; + } SETUP, *PSETUP; + +#endif \ No newline at end of file diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/psi_dale.h linux/drivers/scsi/psi_dale.h --- v2.1.62/linux/drivers/scsi/psi_dale.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/psi_dale.h Wed Nov 5 13:06:38 1997 @@ -0,0 +1,187 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: psi_dale.h + * + * Description: This file contains the interface defines and + * error codes. + * + *-M*************************************************************************/ + +#ifndef PSI_DALE +#define PSI_DALE + +/************************************************/ +/* Dale PCI setup */ +/************************************************/ +#define VENDOR_PSI 0x1256 +#define DEVICE_DALE_1 0x4401 /* 'D1' */ + +/************************************************/ +/* Misc konstants */ +/************************************************/ +#define DALE_MAXDRIVES 4 +#define SECTORSXFER 8 +#define BYTES_PER_SECTOR 512 +#define DEFAULT_TIMING_MODE 5 + +/************************************************/ +/* EEPROM locations */ +/************************************************/ +#define DALE_FLASH_PAGE_SIZE 128 // number of bytes per page +#define DALE_FLASH_SIZE 65536L + +#define DALE_FLASH_BIOS 0x00080000L // BIOS base address +#define DALE_FLASH_SETUP 0x00088000L // SETUP PROGRAM base address offset from BIOS +#define DALE_FLASH_RAID 0x00088400L // RAID signature storage +#define DALE_FLASH_FACTORY 0x00089000L // FACTORY data base address offset from BIOS + +#define DALE_FLASH_BIOS_SIZE 32768U // size of FLASH BIOS REGION + +/************************************************/ +/* DALE Register address offsets */ +/************************************************/ +#define REG_DATA 0x80 +#define REG_ERROR 0x84 +#define REG_SECTOR_COUNT 0x88 +#define REG_LBA_0 0x8C +#define REG_LBA_8 0x90 +#define REG_LBA_16 0x94 +#define REG_LBA_24 0x98 +#define REG_STAT_CMD 0x9C +#define REG_STAT_SEL 0xA0 +#define REG_FAIL 0xB0 +#define REG_ALT_STAT 0xB8 +#define REG_DRIVE_ADRS 0xBC + +#define DALE_DATA_SLOW 0x00040000L +#define DALE_DATA_MODE2 0x00040000L +#define DALE_DATA_MODE3 0x00050000L +#define DALE_DATA_MODE4 0x00060000L +#define DALE_DATA_MODE4P 0x00070000L + +#define RTR_LOCAL_RANGE 0x000 +#define RTR_LOCAL_REMAP 0x004 +#define RTR_EXP_RANGE 0x010 +#define RTR_EXP_REMAP 0x014 +#define RTR_REGIONS 0x018 +#define RTR_DM_MASK 0x01C +#define RTR_DM_LOCAL_BASE 0x020 +#define RTR_DM_IO_BASE 0x024 +#define RTR_DM_PCI_REMAP 0x028 +#define RTR_DM_IO_CONFIG 0x02C +#define RTR_MAILBOX 0x040 +#define RTR_LOCAL_DOORBELL 0x060 +#define RTR_PCI_DOORBELL 0x064 +#define RTR_INT_CONTROL_STATUS 0x068 +#define RTR_EEPROM_CONTROL_STATUS 0x06C + +#define RTL_DMA0_MODE 0x00 +#define RTL_DMA0_PCI_ADDR 0x04 +#define RTL_DMA0_LOCAL_ADDR 0x08 +#define RTL_DMA0_COUNT 0x0C +#define RTL_DMA0_DESC_PTR 0x10 +#define RTL_DMA1_MODE 0x14 +#define RTL_DMA1_PCI_ADDR 0x18 +#define RTL_DMA1_LOCAL_ADDR 0x1C +#define RTL_DMA1_COUNT 0x20 +#define RTL_DMA1_DESC_PTR 0x24 +#define RTL_DMA_COMMAND_STATUS 0x28 +#define RTL_DMA_ARB0 0x2C +#define RTL_DMA_ARB1 0x30 + +/************************************************/ +/* Dale Scratchpad locations */ +/************************************************/ +#define DALE_CHANNEL_DEVICE_0 0 // device channel locations +#define DALE_CHANNEL_DEVICE_1 1 +#define DALE_CHANNEL_DEVICE_2 2 +#define DALE_CHANNEL_DEVICE_3 3 + +#define DALE_SCRATH_DEVICE_0 4 // device type codes +#define DALE_SCRATH_DEVICE_1 5 +#define DALE_SCRATH_DEVICE_2 6 +#define DALE_SCRATH_DEVICE_3 7 + +#define DALE_RAID_0_STATUS 8 +#define DALE_RAID_1_STATUS 9 + +#define DALE_TIMING_MODE 12 // bus master timing mode (2, 3, 4, 5) +#define DALE_NUM_DRIVES 13 // number of addressable drives on this board +#define DALE_RAID_ON 14 // RAID status On +#define DALE_LAST_ERROR 15 // Last error code from BIOS + +/************************************************/ +/* Dale cable select bits */ +/************************************************/ +#define SEL_NONE 0x00 +#define SEL_1 0x01 +#define SEL_2 0x02 + +/************************************************/ +/* Programmable Interrupt Controller */ +/************************************************/ +#define PIC1 0x20 // first 8259 base port address +#define PIC2 0xA0 // second 8259 base port address +#define INT_OCW1 1 // Operation Control Word 1: IRQ mask +#define EOI 0x20 // non-specific end-of-interrupt + +/************************************************/ +/* Device/Geometry controls */ +/************************************************/ +#define GEOMETRY_NONE 0x0 // No device +#define GEOMETRY_SET 0x1 // Geometry set +#define GEOMETRY_LBA 0x2 // Geometry set in default LBA mode +#define GEOMETRY_PHOENIX 0x3 // Geometry set in Pheonix BIOS compatibility mode + +#define DEVICE_NONE 0x0 // No device present +#define DEVICE_INACTIVE 0x1 // device present but not registered active +#define DEVICE_ATAPI 0x2 // ATAPI device (CD_ROM, Tape, Etc...) +#define DEVICE_DASD_NONLBA 0x3 // Non LBA incompatible device +#define DEVICE_DASD_LBA 0x4 // LBA compatible device + +/************************************************/ +/* Setup Structure Definitions */ +/************************************************/ +typedef struct // device setup parameters + { + UCHAR geometryControl; // geometry control flags + UCHAR device; // device code + USHORT sectors; // number of sectors per track + USHORT heads; // number of heads + USHORT cylinders; // number of cylinders for this device + ULONG blocks; // number of blocks on device + ULONG realCapacity; // number of real blocks on this device for drive changed testing + } SETUP_DEVICE, *PSETUP_DEVICE; + +typedef struct // master setup structure + { + USHORT startupDelay; + BOOL promptBIOS; + BOOL fastFormat; + BOOL shareInterrupt; + BOOL rebootRebuil; + USHORT timingMode; + USHORT spare5; + USHORT spare6; + SETUP_DEVICE setupDevice[4]; + } SETUP, *PSETUP; + +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/psi_roy.h linux/drivers/scsi/psi_roy.h --- v2.1.62/linux/drivers/scsi/psi_roy.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/psi_roy.h Wed Nov 5 13:00:30 1997 @@ -0,0 +1,314 @@ +/*+M************************************************************************* + * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux. + * + * Copyright (c) 1997 Perceptive Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * File Name: psi_roy.h + * + * Description: This file contains the host interface command and + * error codes. + * + *-M*************************************************************************/ + +#ifndef ROY_HOST +#define ROY_HOST + +/************************************************/ +/* PCI setup */ +/************************************************/ +#define VENDOR_PSI 0x1256 +#define DEVICE_ROY_1 0x5201 /* 'R1' */ + +/************************************************/ +/* controller constants */ +/************************************************/ +#define MAXADAPTER 4 // Increase this and the sizes of the arrays below, if you need more. +#define MAX_BUS 2 +#define MAX_UNITS 16 +#define TIMEOUT_COMMAND 30 // number of jiffies for command busy timeout + +/************************************************/ +/* I/O address offsets */ +/************************************************/ +#define RTR_MAILBOX 0x040 +#define RTR_LOCAL_DOORBELL 0x060 +#define RTR_PCI_DOORBELL 0x064 + +/************************************************/ +/* */ +/* Host command codes */ +/* */ +/************************************************/ +#define CMD_READ_CHS 0x01 /* read sectors as specified (CHS mode) */ +#define CMD_READ 0x02 /* read sectors as specified (RBA mode) */ +#define CMD_READ_SG 0x03 /* read sectors using scatter/gather list */ +#define CMD_WRITE_CHS 0x04 /* write sectors as specified (CHS mode) */ +#define CMD_WRITE 0x05 /* write sectors as specified (RBA mode) */ +#define CMD_WRITE_SG 0x06 /* write sectors using scatter/gather list (LBA mode) */ +#define CMD_READ_CHS_SG 0x07 /* read sectors using scatter/gather list (CHS mode) */ +#define CMD_WRITE_CHS_SG 0x08 /* write sectors using scatter/gather list (CHS mode) */ +#define CMD_VERIFY_CHS 0x09 /* verify data on sectors as specified (CHS mode) */ +#define CMD_VERIFY 0x0A /* verify data on sectors as specified (RBA mode) */ + +#define CMD_READ_ABS 0x10 /* read absolute disk */ +#define CMD_WRITE_ABS 0x11 /* write absolute disk */ +#define CMD_VERIFY_ABS 0x12 /* verify absolute disk */ +#define CMD_TEST_READY 0x13 /* test unit ready and return status code */ +#define CMD_LOCK_DOOR 0x14 /* lock device door */ +#define CMD_UNLOCK_DOOR 0x15 /* unlock device door */ +#define CMD_EJECT_MEDIA 0x16 /* eject the media */ +#define CMD_UPDATE_CAP 0x17 /* update capacity information */ +#define CMD_TEST_PRIV 0x18 /* test and setup private format media */ + + +#define CMD_SCSI_THRU 0x30 /* SCSI pass through CDB */ +#define CMD_SCSI_THRU_SG 0x31 /* SCSI pass through CDB with scatter/gather */ +#define CMD_SCSI_REQ_SENSE 0x32 /* SCSI pass through request sense after check condition */ + +#define CMD_DASD_SCSI_INQ 0x36 /* to DASD inquire for DASD info in SCSI inquiry format */ +#define CMD_DASD_CAP 0x37 /* read DASD capacity */ +#define CMD_DASD_INQ 0x38 /* do DASD inquire for type data */ +#define CMD_SCSI_INQ 0x39 /* do SCSI inquire */ +#define CMD_READ_SETUP 0x3A /* Get setup structures from controller */ +#define CMD_WRITE_SETUP 0x3B /* Put setup structures in controller and burn in flash */ +#define CMD_READ_CONFIG 0x3C /* Get the entire configuration and setup structures */ +#define CMD_WRITE_CONFIG 0x3D /* Put the entire configuration and setup structures in flash */ + +#define CMD_TEXT_DEVICE 0x3E /* obtain device text */ +#define CMD_TEXT_SIGNON 0x3F /* get sign on banner */ + +#define CMD_QUEUE 0x40 /* any command below this generates a queue tag interrupt to host*/ + +#define CMD_PREFETCH 0x40 /* prefetch sectors as specified */ +#define CMD_TEST_WRITE 0x41 /* Test a device for write protect */ +#define CMD_LAST_STATUS 0x42 /* get last command status and error data*/ +#define CMD_ABORT 0x43 /* abort command as specified */ +#define CMD_ERROR 0x44 /* fetch error code from a tagged op */ +#define CMD_DONE 0x45 /* done with operation */ +#define CMD_DIAGNOSTICS 0x46 /* execute controller diagnostics and wait for results */ +#define CMD_FEATURE_MODE 0x47 /* feature mode control word */ +#define CMD_DASD_INQUIRE 0x48 /* inquire as to DASD SCSI device (32 possible) */ +#define CMD_FEATURE_QUERY 0x49 /* query the feature control word */ +#define CMD_DASD_EJECT 0x4A /* Eject removable media for DASD type */ +#define CMD_DASD_LOCK 0x4B /* Lock removable media for DASD type */ +#define CMD_DASD_TYPE 0x4C /* obtain DASD device type */ +#define CMD_NUM_DEV 0x4D /* obtain the number of devices connected to the controller */ +#define CMD_GET_PARMS 0x4E /* obtain device parameters */ +#define CMD_SPECIFY 0x4F /* specify operating system for scatter/gather operations */ + +#define CMD_RAID_GET_DEV 0x50 /* read RAID device geometry */ +#define CMD_RAID_READ 0x51 /* read RAID 1 parameter block */ +#define CMD_RAID_WRITE 0x52 /* write RAID 1 parameter block */ +#define CMD_RAID_LITEUP 0x53 /* Light up the drive light for identification */ +#define CMD_RAID_REBUILD 0x54 /* issue a RAID 1 pair rebuild */ +#define CMD_RAID_MUTE 0x55 /* mute RAID failure alarm */ +#define CMD_RAID_FAIL 0x56 /* induce a RAID failure */ +#define CMD_RAID_STATUS 0x57 /* get status of RAID pair */ +#define CMD_RAID_STOP 0x58 /* stop any reconstruct in progress */ +#define CMD_RAID_START 0x59 /* start reconstruct */ + +#define CMD_SCSI_GET 0x60 /* get SCSI pass through devices */ +#define CMD_SCSI_TIMEOUT 0x61 /* set SCSI pass through timeout */ +#define CMD_SCSI_ERROR 0x62 /* get SCSI pass through request sense length and residual data count */ +#define CMD_GET_SPARMS 0x63 /* get SCSI bus and user parms */ +#define CMD_SCSI_ABORT 0x64 /* abort by setting time-out to zero */ + +#define CMD_CHIRP_CHIRP 0x77 /* make a chirp chirp sound */ +#define CMD_GET_LAST_DONE 0x78 /* get tag of last done in progress */ +#define CMD_GET_FEATURES 0x79 /* get feature code and ESN */ +#define CMD_CLEAR_CACHE 0x7A /* Clear cache on specified device */ +#define CMD_BIOS_TEST 0x7B /* Test whether or not to load BIOS */ +#define CMD_WAIT_FLUSH 0x7C /* wait for cache flushed and invalidate read cache */ +#define CMD_RESET_BUS 0x7D /* reset the SCSI bus */ +#define CMD_STARTUP_QRY 0x7E /* startup in progress query */ +#define CMD_RESET 0x7F /* reset the controller */ + +#define CMD_RESTART_RESET 0x80 /* reload and restart the controller at any reset issued */ +#define CMD_SOFT_RESET 0x81 /* do a soft reset NOW! */ + +/************************************************/ +/* */ +/* Host return errors */ +/* */ +/************************************************/ +#define ERR08_TAGGED 0x80 /* doorbell error ored with tag */ + +#define ERR16_NONE 0x0000 /* no errors */ +#define ERR16_SC_COND_MET 0x0004 /* SCSI status - Condition Met */ +#define ERR16_CMD 0x0101 /* command error */ +#define ERR16_SC_CHECK_COND 0x0002 /* SCSI status - Check Condition */ +#define ERR16_CMD_NOT 0x0201 /* command not supported */ +#define ERR16_NO_DEVICE 0x0301 /* invalid device selection */ +#define ERR16_SECTOR 0x0202 /* bad sector */ +#define ERR16_PROTECT 0x0303 /* write protected */ +#define ERR16_NOSECTOR 0x0404 /* sector not found */ +#define ERR16_MEDIA 0x0C0C /* invalid media */ +#define ERR16_CONTROL 0x2020 /* controller error */ +#define ERR16_CONTROL_DMA 0x2120 /* controller DMA engine error */ +#define ERR16_NO_ALARM 0x2220 /* alarm is not active */ +#define ERR16_OP_BUSY 0x2320 /* operation busy */ +#define ERR16_SEEK 0x4040 /* seek failure */ +#define ERR16_DEVICE_FAIL 0x4140 /* device has failed */ +#define ERR16_TIMEOUT 0x8080 /* timeout error */ +#define ERR16_DEV_NOT_READY 0xAAAA /* drive not ready */ +#define ERR16_UNDEFINED 0xBBBB /* undefined error */ +#define ERR16_WRITE_FAULT 0xCCCC /* write fault */ +#define ERR16_INVALID_DEV 0x4001 /* invalid device access */ +#define ERR16_DEVICE_BUSY 0x4002 /* device is busy */ +#define ERR16_MEMORY 0x4003 /* device pass thru requires too much memory */ +#define ERR16_NO_FEATURE 0x40FA /* feature no implemented */ +#define ERR16_NOTAG 0x40FD /* no tag space available */ +#define ERR16_NOT_READY 0x40FE /* controller not ready error */ +#define ERR16_SETUP_FLASH 0x5050 /* error when writing setup to flash memory */ +#define ERR16_SETUP_SIZE 0x5051 /* setup block size error */ +#define ERR16_SENSE 0xFFFF /* sense opereration failed */ +#define ERR16_SC_BUSY 0x0008 /* SCSI status - Busy */ +#define ERR16_SC_RES_CONFL 0x0018 /* SCSI status - Reservation Conflict */ +#define ERR16_SC_CMD_TERM 0x0022 /* SCSI status - Command Terminated */ +#define ERR16_SC_OTHER 0x00FF /* SCSI status - not recognized (any value masked) */ +#define ERR16_MEDIA_CHANGED 0x8001 /* devices media has been changed */ + +#define ERR32_NONE 0x00000000 /* no errors */ +#define ERR32_SC_COND_MET 0x00000004 /* SCSI status - Condition Met */ +#define ERR32_CMD 0x00010101 /* command error */ +#define ERR32_SC_CHECK_COND 0x00020002 /* SCSI status - Check Condition */ +#define ERR32_CMD_NOT 0x00030201 /* command not supported */ +#define ERR32_NO_DEVICE 0x00040301 /* invalid device selection */ +#define ERR32_SECTOR 0x00050202 /* bad sector */ +#define ERR32_PROTECT 0x00060303 /* write protected */ +#define ERR32_NOSECTOR 0x00070404 /* sector not found */ +#define ERR32_MEDIA 0x00080C0C /* invalid media */ +#define ERR32_CONTROL 0x00092020 /* controller error */ +#define ERR32_CONTROL_DMA 0x000A2120 /* Controller DMA error */ +#define ERR32_NO_ALARM 0x000B2220 /* alarm is not active */ +#define ERR32_OP_BUSY 0x000C2320 /* operation busy */ +#define ERR32_SEEK 0x000D4040 /* seek failure */ +#define ERR32_DEVICE_FAIL 0x000E4140 /* device has failed */ +#define ERR32_TIMEOUT 0x000F8080 /* timeout error */ +#define ERR32_DEV_NOT_READY 0x0010AAAA /* drive not ready */ +#define ERR32_UNDEFINED 0x0011BBBB /* undefined error */ +#define ERR32_WRITE_FAULT 0x0012CCCC /* write fault */ +#define ERR32_INVALID_DEV 0x00134001 /* invalid device access */ +#define ERR32_DEVICE_BUSY 0x00144002 /* device is busy */ +#define ERR32_MEMORY 0x00154003 /* device pass thru requires too much memory */ +#define ERR32_NO_FEATURE 0x001640FA /* feature no implemented */ +#define ERR32_NOTAG 0x001740FD /* no tag space available */ +#define ERR32_NOT_READY 0x001840FE /* controller not ready error */ +#define ERR32_SETUP_FLASH 0x00195050 /* error when writing setup to flash memory */ +#define ERR32_SETUP_SIZE 0x001A5051 /* setup block size error */ +#define ERR32_SENSE 0x001BFFFF /* sense opereration failed */ +#define ERR32_SC_BUSY 0x001C0008 /* SCSI status - Busy */ +#define ERR32_SC_RES_CONFL 0x001D0018 /* SCSI status - Reservation Conflict */ +#define ERR32_SC_CMD_TERM 0x001E0022 /* SCSI status - Command Terminated */ +#define ERR32_SC_OTHER 0x001F00FF /* SCSI status - not recognized (any value masked) */ +#define ERR32_MEDIA_CHANGED 0x00208001 /* devices media has been changed */ + +/************************************************/ +/* */ +/* Host Operating System specification codes */ +/* */ +/************************************************/ + +#define SPEC_INTERRUPT 0x80 /* specification requires host interrupt */ +#define SPEC_BACKWARD_SG 0x40 /* specification requires scatter/gather items reversed */ +#define SPEC_DOS_BLOCK 0x01 /* DOS DASD blocking on pass through */ +#define SPEC_OS2_V3 0x02 /* OS/2 Warp */ +#define SPCE_SCO_3242 0x04 /* SCO 3.4.2.2 */ + + +/************************************************/ +/* */ +/* Inquire structures */ +/* */ +/************************************************/ +typedef struct _CNT_SCSI_INQ + { + UCHAR devt; /* 00: device type */ + UCHAR devtm; /* 01: device type modifier */ + UCHAR svers; /* 02: SCSI version */ + UCHAR rfmt; /* 03: response data format */ + UCHAR adlen; /* 04: additional length of data */ + UCHAR res1; /* 05: */ + UCHAR res2; /* 06: */ + UCHAR fncs; /* 07: functional capabilities */ + UCHAR vid[8]; /* 08: vendor ID */ + UCHAR pid[16]; /* 10: product ID */ + UCHAR rev[4]; /* 20: product revision */ + } CNT_SCSI_INQ; + +typedef struct _CNT_IDE_INQ + { + USHORT GeneralConfiguration; /* 00 */ + USHORT NumberOfCylinders; /* 02 */ + USHORT Reserved1; /* 04 */ + USHORT NumberOfHeads; /* 06 */ + USHORT UnformattedBytesPerTrack; /* 08 */ + USHORT UnformattedBytesPerSector; /* 0A */ + USHORT SectorsPerTrack; /* 0C */ + USHORT VendorUnique1[3]; /* 0E */ + USHORT SerialNumber[10]; /* 14 */ + USHORT BufferType; /* 28 */ + USHORT BufferSectorSize; /* 2A */ + USHORT NumberOfEccBytes; /* 2C */ + USHORT FirmwareRevision[4]; /* 2E */ + USHORT ModelNumber[20]; /* 36 */ + UCHAR MaximumBlockTransfer; /* 5E */ + UCHAR VendorUnique2; /* 5F */ + USHORT DoubleWordIo; /* 60 */ + USHORT Capabilities; /* 62 */ + USHORT Reserved2; /* 64 */ + UCHAR VendorUnique3; /* 66 */ + UCHAR PioCycleTimingMode; /* 67 */ + UCHAR VendorUnique4; /* 68 */ + UCHAR DmaCycleTimingMode; /* 69 */ + USHORT TranslationFieldsValid; /* 6A */ + USHORT NumberOfCurrentCylinders; /* 6C */ + USHORT NumberOfCurrentHeads; /* 6E */ + USHORT CurrentSectorsPerTrack; /* 70 */ + ULONG CurrentSectorCapacity; /* 72 */ + } CNT_IDE_INQ; + +typedef struct _DASD_INQUIRE + { + ULONG type; /* 0 = SCSI, 1 = IDE */ + union + { + CNT_SCSI_INQ scsi; /* SCSI inquire data */ + CNT_IDE_INQ ide; /* IDE inquire data */ + } inq; + } DASD_INQUIRE; + +/************************************************/ +/* */ +/* Device Codes */ +/* */ +/************************************************/ +#define DEVC_DASD 0x00 /* Direct-access Storage Device */ +#define DEVC_SEQACESS 0x01 /* Sequential-access device */ +#define DEVC_PRINTER 0x02 /* Printer device */ +#define DEVC_PROCESSOR 0x03 /* Processor device */ +#define DEVC_WRITEONCE 0x04 /* Write-once device */ +#define DEVC_CDROM 0x05 /* CD-ROM device */ +#define DEVC_SCANNER 0x06 /* Scanner device */ +#define DEVC_OPTICAL 0x07 /* Optical memory device */ +#define DEVC_MEDCHGR 0x08 /* Medium changer device */ +#define DEVC_DASD_REMOVABLE 0x80 /* Direct-access storage device, Removable */ +#define DEVC_NONE 0xFF /* no device */ + +#endif + diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- v2.1.62/linux/drivers/scsi/scsi.h Sun Sep 7 13:10:43 1997 +++ linux/drivers/scsi/scsi.h Sat Nov 8 11:39:12 1997 @@ -498,8 +498,7 @@ req->nr_sectors -= bh->b_size >> 9; req->sector += bh->b_size >> 9; bh->b_reqnext = NULL; - mark_buffer_uptodate(bh, uptodate); - unlock_buffer(bh); + bh->b_end_io(bh, uptodate); sectors -= bh->b_size >> 9; if ((bh = req->bh) != NULL) { req->current_nr_sectors = bh->b_size >> 9; diff -u --recursive --new-file v2.1.62/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c --- v2.1.62/linux/drivers/scsi/scsi_ioctl.c Wed Sep 3 20:52:43 1997 +++ linux/drivers/scsi/scsi_ioctl.c Fri Nov 7 10:14:49 1997 @@ -341,6 +341,11 @@ &((Scsi_Idlun *) arg)->dev_id); put_user(dev->host->unique_id, &((Scsi_Idlun *) arg)->host_unique_id); return 0; + case SCSI_IOCTL_GET_BUS_NUMBER: + result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); + if (result) return result; + put_user( dev->host->host_no, (int *) arg); + return 0; case SCSI_IOCTL_TAGGED_ENABLE: if(!suser()) return -EACCES; if(!dev->tagged_supported) return -EINVAL; diff -u --recursive --new-file v2.1.62/linux/drivers/sound/.objects linux/drivers/sound/.objects --- v2.1.62/linux/drivers/sound/.objects Tue Mar 4 10:25:24 1997 +++ linux/drivers/sound/.objects Tue Sep 30 08:46:46 1997 @@ -89,3 +89,11 @@ OBJS := $(OBJS) uart401.o endif +ifdef CONFIG_OPL3SA1 + OBJS := $(OBJS) opl3sa.o +endif + +ifdef CONFIG_SOFTOSS + OBJS := $(OBJS) softoss.o softoss_rs.o +endif + diff -u --recursive --new-file v2.1.62/linux/drivers/sound/.version linux/drivers/sound/.version --- v2.1.62/linux/drivers/sound/.version Wed May 28 10:51:32 1997 +++ linux/drivers/sound/.version Sun Nov 9 23:01:54 1997 @@ -1,2 +1,2 @@ -3.8a +3.8s 0x030804 diff -u --recursive --new-file v2.1.62/linux/drivers/sound/CHANGELOG linux/drivers/sound/CHANGELOG --- v2.1.62/linux/drivers/sound/CHANGELOG Wed May 28 10:51:32 1997 +++ linux/drivers/sound/CHANGELOG Tue Sep 30 08:46:46 1997 @@ -1,5 +1,12 @@ -Changelog for version 3.8 +Changelog for version 3.8o -------------------------- + +Since 3.8h +- Included support for OPL3-SA1 and SoftOSS + +Since 3.8 +- Fixed SNDCTL_DSP_GETOSPACE +- Compatibility fixes for Linux 2.1.47 Since 3.8-beta21 - Fixed all known bugs (I think). diff -u --recursive --new-file v2.1.62/linux/drivers/sound/Config.in linux/drivers/sound/Config.in --- v2.1.62/linux/drivers/sound/Config.in Thu Jul 17 10:06:06 1997 +++ linux/drivers/sound/Config.in Sun Nov 9 22:59:31 1997 @@ -1,277 +1,15 @@ -bool 'ProAudioSpectrum 16 support' CONFIG_PAS -bool '100%% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB -bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB -bool 'Gravis Ultrasound support' CONFIG_GUS -bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401 -bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS -bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 -bool 'GUS MAX support' CONFIG_GUSMAX -bool 'Microsoft Sound System support' CONFIG_MSS -bool 'Ensoniq SoundScape support' CONFIG_SSCAPE -bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX -bool 'Support for MAD16 and/or Mozart based cards' CONFIG_MAD16 -bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232 -bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI -bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812 - -if [ "$CONFIG_AEDSP16" = "y" ]; then -hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220 -fi - -if [ "$CONFIG_SB" = "y" ]; then -hex 'I/O base for SB Check from manual of the card' SBC_BASE 220 -fi - -if [ "$CONFIG_SB" = "y" ]; then -int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7 -fi - -if [ "$CONFIG_SB" = "y" ]; then -int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1 -fi - -if [ "$CONFIG_SB" = "y" ]; then -int 'Sound Blaster 16 bit DMA (SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5 -fi - -if [ "$CONFIG_SB" = "y" ]; then -hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 330 -fi - - -if [ "$CONFIG_SB" = "y" ]; then -comment 'MPU401 IRQ is only required with Jazz16, SM Wave and ESS1688.' -fi - - -if [ "$CONFIG_SB" = "y" ]; then -comment 'Enter -1 to the following question if you have something else such as SB16/32.' -fi - -if [ "$CONFIG_SB" = "y" ]; then -int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' SB_MPU_IRQ -1 -fi - -if [ "$CONFIG_PAS" = "y" ]; then -int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10 -fi - -if [ "$CONFIG_PAS" = "y" ]; then -int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3 -fi - -if [ "$CONFIG_GUS" = "y" ]; then -hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220 -fi - -if [ "$CONFIG_GUS" = "y" ]; then -int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15 -fi - -if [ "$CONFIG_GUS" = "y" ]; then -int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6 -fi - -if [ "$CONFIG_GUS" = "y" ]; then -int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1 -fi - -if [ "$CONFIG_GUS16" = "y" ]; then -hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530 -fi - -if [ "$CONFIG_GUS16" = "y" ]; then -int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7 -fi - -if [ "$CONFIG_GUS16" = "y" ]; then -int 'GUS DMA 0, 1 or 3' GUS16_DMA 3 -fi - -if [ "$CONFIG_MPU401" = "y" ]; then -hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330 -fi - -if [ "$CONFIG_MPU401" = "y" ]; then -int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9 -fi - - -if [ "$CONFIG_MAUI" = "y" ]; then -comment 'ERROR! You have to use old sound configuration method with Maui.' -fi - -if [ "$CONFIG_MAUI" = "y" ]; then -hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330 -fi - -if [ "$CONFIG_MAUI" = "y" ]; then -int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9 -fi - -if [ "$CONFIG_UART6850" = "y" ]; then -hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0 -fi - -if [ "$CONFIG_UART6850" = "y" ]; then -int 'UART6850 IRQ (Unknown)' U6850_IRQ -1 -fi - - -if [ "$CONFIG_PSS" = "y" ]; then -comment 'ERROR! You have to use old sound configuration method with PSS cards.' -fi - -if [ "$CONFIG_PSS" = "y" ]; then -hex 'PSS I/O base 220 or 240' PSS_BASE 220 -fi - -if [ "$CONFIG_PSS" = "y" ]; then -hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530 -fi - -if [ "$CONFIG_PSS" = "y" ]; then -int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11 -fi - -if [ "$CONFIG_PSS" = "y" ]; then -int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3 -fi - -if [ "$CONFIG_PSS" = "y" ]; then -hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330 -fi - -if [ "$CONFIG_PSS" = "y" ]; then -int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9 -fi - -if [ "$CONFIG_MSS" = "y" ]; then -hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530 -fi - -if [ "$CONFIG_MSS" = "y" ]; then -int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11 -fi - -if [ "$CONFIG_MSS" = "y" ]; then -int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3 -fi - -if [ "$CONFIG_MSS" = "y" ]; then -int 'MSS/WSS second DMA (if possible) 0, 1 or 3' MSS_DMA2 -1 -fi - -if [ "$CONFIG_SSCAPE" = "y" ]; then -hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330 -fi - -if [ "$CONFIG_SSCAPE" = "y" ]; then -int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9 -fi - -if [ "$CONFIG_SSCAPE" = "y" ]; then -int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3 -fi - -if [ "$CONFIG_SSCAPE" = "y" ]; then -hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534 -fi - -if [ "$CONFIG_SSCAPE" = "y" ]; then -int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11 -fi - - -if [ "$CONFIG_TRIX" = "y" ]; then -comment 'ERROR! You have to use old sound configuration method with AudioTrix.' -fi - -if [ "$CONFIG_TRIX" = "y" ]; then -hex 'AudioTrix audio I/O base 530, 604, E80 or F40' TRIX_BASE 530 -fi - -if [ "$CONFIG_TRIX" = "y" ]; then -int 'AudioTrix audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11 -fi - -if [ "$CONFIG_TRIX" = "y" ]; then -int 'AudioTrix audio DMA 0, 1 or 3' TRIX_DMA 0 -fi - -if [ "$CONFIG_TRIX" = "y" ]; then -int 'AudioTrix second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3 -fi - -if [ "$CONFIG_TRIX" = "y" ]; then -hex 'AudioTrix MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330 -fi - -if [ "$CONFIG_TRIX" = "y" ]; then -int 'AudioTrix MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9 -fi - -if [ "$CONFIG_TRIX" = "y" ]; then -hex 'AudioTrix SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220 -fi - -if [ "$CONFIG_TRIX" = "y" ]; then -int 'AudioTrix SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7 -fi - -if [ "$CONFIG_TRIX" = "y" ]; then -int 'AudioTrix SB DMA 1 or 3' TRIX_SB_DMA 1 -fi - -if [ "$CONFIG_CS4232" = "y" ]; then -hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530 -fi - -if [ "$CONFIG_CS4232" = "y" ]; then -int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11 -fi - -if [ "$CONFIG_CS4232" = "y" ]; then -int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0 -fi - -if [ "$CONFIG_CS4232" = "y" ]; then -int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3 -fi - -if [ "$CONFIG_CS4232" = "y" ]; then -hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330 -fi - -if [ "$CONFIG_CS4232" = "y" ]; then -int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9 -fi - -if [ "$CONFIG_MAD16" = "y" ]; then -hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530 -fi - -if [ "$CONFIG_MAD16" = "y" ]; then -int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11 -fi - -if [ "$CONFIG_MAD16" = "y" ]; then -int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3 -fi - -if [ "$CONFIG_MAD16" = "y" ]; then -int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0 -fi - -if [ "$CONFIG_MAD16" = "y" ]; then -hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330 -fi - -if [ "$CONFIG_MAD16" = "y" ]; then -int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9 -fi # -$MAKE -C drivers/sound kernelconfig || exit 1 +# Sound driver configuration +# +#-------- +# There is another confic script which is compatible with rest of +# the kernel. It can be activated by running 'make mkscript' in this +# directory. Please note that this is an _experimental_ feature which +# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui). +#-------- +# +$MAKE -C drivers/sound config || exit 1 + bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then diff -u --recursive --new-file v2.1.62/linux/drivers/sound/Makefile linux/drivers/sound/Makefile --- v2.1.62/linux/drivers/sound/Makefile Wed May 28 10:51:32 1997 +++ linux/drivers/sound/Makefile Sun Nov 9 22:59:28 1997 @@ -1,3 +1,4 @@ +BUILDCODE=o # Makefile for the Linux sound card driver # # Note 2! The CFLAGS definitions are now inherited from the @@ -155,6 +156,7 @@ clrconf: rm -f local.h .depend synth-ld.h trix_boot.h smw-midi0001.h maui_boot.h .defines + rm Config.in;cp Config.std Config.in configure: configure.c $(HOSTCC) -o configure configure.c @@ -162,6 +164,7 @@ dep: $(CPP) -M $(CFLAGS) -I. *.c > .depend + cd lowlevel;make dep setup: @echo Compiling Sound Driver v $(VERSION) for Linux diff -u --recursive --new-file v2.1.62/linux/drivers/sound/Readme linux/drivers/sound/Readme --- v2.1.62/linux/drivers/sound/Readme Wed May 28 10:51:32 1997 +++ linux/drivers/sound/Readme Tue Sep 30 08:46:46 1997 @@ -1,22 +1,21 @@ -OSS Lite version 3.8 release notes +OSS/Free version 3.8 release notes ---------------------------------- Most up to date information about this driver is available from -http://www.4front-tech.com/ossfree or http://personal.eunet.fi/pp/voxware -(European mirror). +http://www.4front-tech.com/ossfree. Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP sites). It gives instructions about using sound with Linux. It's bit out of date but still very useful. Information about bug fixes and such things -is available from the web page (see below). +is available from the web page (see above). Please check http://www.4front-tech.com/pguide for more info about programming -with OSS. +with OSS API. ==================================================== -- THIS VERSION ____REQUIRES____ Linux 2.1.36 OR LATER. +- THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER. ==================================================== Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z" @@ -78,6 +77,18 @@ There are probably many other names missing. If you have sent me some patches and your name is not in the above list, please inform me. +Sending your contributions or patches +------------------------------------- + +First of all it's highly recommended to contact me before sending anything +or before even starting to do any work. Tell me what you suggest to be +changed or what you have planned to do. Also ensure you are using the +very latest (development) version of OSS/Free since the change may already be +implemented there. In general it's major waste of time to try to improve +several months old version. Information about the latest version can be found +from http://www.4front-tech.com/ossfree. In general there is no point in +sending me patches relative to production kernels. + Sponsors etc. ------------- @@ -166,7 +177,7 @@ Hannu Hannu Savolainen -hannu@voxware.pp.fi, hannu@4front-tech.com +hannu@4front-tech.com (Please check http://www.4front-tech.com/ossfree before mailing me). Snail mail: Hannu Savolainen diff -u --recursive --new-file v2.1.62/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards --- v2.1.62/linux/drivers/sound/Readme.cards Wed May 28 10:51:32 1997 +++ linux/drivers/sound/Readme.cards Tue Sep 30 08:46:46 1997 @@ -5,7 +5,13 @@ Open Sound Systems (OSS/Free). Information about the commercial version (OSS/Linux) and it's configuration is available from http://www.4front-tech.com/linux.html. Information presented here is -not valid for OSS/Linux. +not valid for OSS/Linux. + +If you are unsure about how to configure OSS/Free +you can download the free evaluation version of OSS/Linux from the above +address. There is a chance that it can autodetect your soundcard. In this case +you can use the information included in soundon.log when configuring OSS/Free. + IMPORTANT! This document covers only cards that were "known" when this driver version was released. Please look at @@ -25,7 +31,7 @@ method to use. After you have used the "new" method once it will always be used when you use any of the config programs. To return back to the "old" method you should - reinstall the kernel sources. + execute "cp Config.std Config.in" in linux/drivers/sound. The /etc/soundconf file (forget it if you don't know what this file does) contains settings that are used only by @@ -33,8 +39,8 @@ are stored there (they really are _NOT_ stored there). Don't try to edit /etc/soundconf or any other kernel or sound driver config files manually. The _only_ - proper ways to change the settings are make config, - make menuconfig or make xconfig. + proper ways to change the settings are make config or + make menuconfig (the "old" method). When using make xconfig and/or make menuconfig, you should carefully check each sound configuration option (particularly @@ -42,7 +48,6 @@ offered by these programs are not necessarily valid. - THE BIGGEST MISTAKES YOU CAN DO =============================== @@ -211,7 +216,8 @@ is compatible just with SB Pro but there is also a non-SB- compatible 16 bit mode. Usually it's MSS/WSS but it could also be a proprietary one like MV Jazz16 or ESS ES688. OPTi - MAD16 chips are very common in so called "SB 16 bit cards". + MAD16 chips are very common in so called "SB 16 bit cards" + (try with the MAD16 driver). ====================================================================== "Supposed to be SB compatible" cards. @@ -292,10 +298,67 @@ ---------------------------------------------------------------- NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition to the FM synth this chip has also digital audio (WSS) and - MIDI (MPU401) capabilities. OPL3-SA is not supported by OSS/Free. - Support for it is included in OSS/Linux v3.8 and later. + MIDI (MPU401) capabilities. Support for OPL3-SA is described below. ---------------------------------------------------------------- +Yamaha OPL3-SA1 + + Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some + (Intel) motherboards and on cheap soundcards. It should not be + confused with the original OPL3 chip (YMF278) which is entirely + different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro + (not used in OSS/Free) in addition to the OPL3 FM synth. + + There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They + are PnP chips and will not work with the OPL3-SA1 driver. You should + use the standard MSS, MPU401 and OPL3 options with thses chips and to + activate the card using isapnptools. + +4Front Technologies SoftOSS + + SoftOSS is a software based wave table emulation which works with + any 16 bit stereo soundcard. Due to it's nature a fast CPU is + required (P133 is minumum). Althoug SoftOSS doesn _not_ use MMX + instructions it has proven out that recent processors (which appear + to have MMX) perform significantly better with SoftOSS than earlier + ones. For example a P166MMX beats a PPro200. SoftOSS should not be used + on 486 or 386 machines. + + The amount of CPU load caused by SoftOSS can be controlled by + selecting the SOFTOSS_RATE and SOFTOSS_VOICES parameters properly + (they will be prompted by make config). It's recommended to set + SOFTOSS_VOICES to 32. If you have a P166MMX or faster (PPro200 is + not faster) you can set SOFTOSS_RATE to 44100 (kHz). However with + slower systems it recommended to use sampling rates around 22050 + or even 16000 kHz. Selecting too high values for these parameters + may hang your system when playing MIDI files with hight degree of + polyphony (number of concurrently playing notes). It's also possible to + decrease SOFTOSS_VOICES. This makes it possible to use higher sampling + rates. However using fewer voices decreases playback quality more than + decreasing the sampling rate. + + SoftOSS keeps the samples loaded on system's RAM so large RAM is + required. SoftOSS should never be used on machines with less than 16M + of RAM since this is potentially dangerous (you may accidently run out + of memory which probably crashes the machine). + + SoftOSS implements the wave table API originally designed for GUS. For + this reason all applications designed for GUS should work (at least + after minor modifications). For example gmod/xgmod and playmidi -g are + known to work. + + To work SoftOSS will require GUS compatible + patch files to be installed on the system (in /dos/ultrasnd/midi). You + can use the public domain MIDIA patchset available from several ftp + sites. + + ********************************************************************* + IMPORTANT NOTICE! The original patch set distributed with Gravis + Ultrasound card is not in public domain (even it's available from + some ftp sites). You should contact Voice Crystal (www.voicecrystal.com) + if you like to use these patches with SoftOSS included in OSS/Free. + ********************************************************************* + PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC) Analog Devices and Echo Speech have together defined a soundcard architecture based on the above chips. The DSP chip is used @@ -327,7 +390,7 @@ VIVO90 cards are not compatible with Soundscapes so the Soundscape driver will not work with them. You may want to use OSS/Linux with these cards. -MAD16 and Mozart based cards +OPTi MAD16 and Mozart based cards The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929), OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface chips are used in many different soundcards, including some @@ -644,8 +707,12 @@ Sound Blasters (the original ones by Creative) --------------------------------------------- +NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995 + are almost certainly PnP ones). With PnP cards you should use isapnptools + to activate them (see above). + It's possible to configure these cards to use different I/O, IRQ and -DMA settings. Since the available settings have changed between various +DMA settings. Since the possible/default settings have changed between various models, you have to consult manual of your card for the proper ones. It's a good idea to use the same values than with DOS/Windows. With SB and SB Pro it's the only choice. SB16 has software selectable IRQ and DMA channels but @@ -662,6 +729,15 @@ one when the configuration program asks for the 16 bit one. This may work in some systems but is likely to cause terrible noise on some other systems. +It's possible to use two SB16/32/64 at the same time. To do this you should +first configure OSS/Free for one card. Then edit local.h manually and define +SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get +the OPL3, MIDI and EMU8000 devices of the second card to work. If you are +going to use two PnP Sound Blasters, ensure that they are of different model +and have different PnP ID's. There is no way to get two cards with the same +card ID and serial number to work. The easiest way to check this is trying +if isapnptools can see both cards or just one. + NOTE! Don't enable the SM Games option (asked by the configuration program) if you are not 101% sure that your card is a Logitech Soundman Games (not a SM Wave or SM16). @@ -1185,12 +1261,10 @@ If you have any corrections and/or comments, please contact me. Hannu Savolainen -hannu@voxware.pp.fi +hannu@4front-tech.com Personal home page: http://personal.eunet.fi/pp/voxware/hannu.html www home page of OSS/Free: http://www.4front-tech.com/ossfree - European/Finnish mirror: http://personal.eunet.fi/pp/voxware www home page of commercial OSS (Open Sound System) drivers: http://www.4front-tech.com/oss.html - diff -u --recursive --new-file v2.1.62/linux/drivers/sound/Readme.linux linux/drivers/sound/Readme.linux --- v2.1.62/linux/drivers/sound/Readme.linux Wed May 28 10:51:32 1997 +++ linux/drivers/sound/Readme.linux Tue Sep 30 08:46:46 1997 @@ -67,10 +67,10 @@ check for possible boot (insmod) time error messages in /var/adm/messages. - Other messages or problems -Please check http://www.4front-tech.com/osslite for more info. +Please check http://www.4front-tech.com/ossfree for more info. Hannu Savolainen -hannu@voxware.pp.fi +hannu@4front-tech.com ----------------- cut here ------------------------------ SURPRISE SURPRISE!!! diff -u --recursive --new-file v2.1.62/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c --- v2.1.62/linux/drivers/sound/ad1848.c Wed May 28 10:51:32 1997 +++ linux/drivers/sound/ad1848.c Tue Sep 30 08:46:49 1997 @@ -28,7 +28,7 @@ #define DEB1(x) #include "sound_config.h" -#if defined(CONFIG_AD1848) +#ifdef CONFIG_AD1848 #include "ad1848_mixer.h" @@ -49,7 +49,7 @@ int audio_mode; int open_mode; int intr_active; - char *chip_name; + char *chip_name, *name; int model; #define MD_1848 1 #define MD_4231 2 @@ -1423,7 +1423,7 @@ devc->MCE_bit = 0x40; devc->irq = 0; devc->open_mode = 0; - devc->chip_name = "AD1848"; + devc->chip_name = devc->name = "AD1848"; devc->model = MD_1848; /* AD1848 or CS4248 */ devc->levels = NULL; devc->c930_password_port = 0; @@ -1439,6 +1439,11 @@ * If the I/O address is unused, it typically returns 0xff. */ + if (inb (devc->base) == 0xff) + { + DDB (printk ("ad1848_detect: The base I/O address appears to be dead\n")); + } + /* * Wait for the device to stop initialization */ @@ -1459,8 +1464,7 @@ if ((inb (devc->base) & 0x80) != 0x00) /* Not a AD1848 */ { - DDB (printk ("ad1848 detect error - step A (%02x)\n", - (int) inb (devc->base))); + DDB (printk ("ad1848 detect error - step A (%02x)\n", (int) inb (devc->base))); return 0; } @@ -1480,7 +1484,7 @@ else { DDB (printk ("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2)); - /* return 0; */ + return 0; } DDB (printk ("ad1848_detect() - step C\n")); @@ -1493,7 +1497,7 @@ else { DDB (printk ("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2)); - /* return 0; */ + return 0; } /* @@ -1618,8 +1622,7 @@ id = ad_read (devc, 25) & 0xe7; if (id == 0x80) /* Device still busy??? */ id = ad_read (devc, 25) & 0xe7; - DDB (printk ("ad1848_detect() - step J (%02x/%02x)\n", id, - ad_read (devc, 25))); + DDB (printk ("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read (devc, 25))); switch (id) { @@ -1687,9 +1690,7 @@ break; default: /* Assume CS4231 or OPTi 82C930 */ - DDB (printk ("ad1848: I25 = %02x/%02x\n", - ad_read (devc, 25), - ad_read (devc, 25) & 0xe7)); + DDB (printk ("ad1848: I25 = %02x/%02x\n", ad_read (devc, 25), ad_read (devc, 25) & 0xe7)); if (optiC930) { devc->chip_name = "82C930"; @@ -1741,8 +1742,6 @@ ad1848_port_info *portc = NULL; - request_region (devc->base, 4, devc->chip_name); - devc->irq = (irq > 0) ? irq : 0; devc->open_mode = 0; devc->timer_ticks = 0; @@ -1750,6 +1749,8 @@ devc->dma2 = dma_capture; devc->audio_flags = DMA_AUTOMODE; devc->playback_dev = devc->record_dev = 0; + if (name != NULL) + devc->name = name; if (name != NULL && name[0] != 0) sprintf (dev_name, @@ -1758,6 +1759,8 @@ sprintf (dev_name, "Generic audio codec (%s)", devc->chip_name); + request_region (devc->base, 4, devc->name); + conf_printf2 (dev_name, devc->base, devc->irq, dma_playback, dma_capture); @@ -1801,7 +1804,7 @@ { irq2dev[irq] = devc->dev_no = my_dev; if (snd_set_irq_handler (devc->irq, adintr, - "SoundPort", + devc->name, NULL) < 0) { printk ("ad1848: IRQ in use\n"); @@ -1843,11 +1846,11 @@ if (!share_dma) { - if (sound_alloc_dma (dma_playback, "Sound System")) + if (sound_alloc_dma (dma_playback, devc->name)) printk ("ad1848.c: Can't allocate DMA%d\n", dma_playback); if (dma_capture != dma_playback) - if (sound_alloc_dma (dma_capture, "Sound System (capture)")) + if (sound_alloc_dma (dma_capture, devc->name)) printk ("ad1848.c: Can't allocate DMA%d\n", dma_capture); } @@ -1944,8 +1947,7 @@ } } else - printk ("ad1848: Can't find device to be unloaded. Base=%x\n", - io_base); + printk ("ad1848: Can't find device to be unloaded. Base=%x\n", io_base); } void @@ -1973,7 +1975,7 @@ if (irq > 15) { - /* printk ("ad1848.c: Bogus interrupt %d\n", irq); */ + /* printk("ad1848.c: Bogus interrupt %d\n", irq); */ return; } @@ -2149,8 +2151,7 @@ tmp |= 0x03; break; default: - DDB (printk ("init_deskpro: Invalid MSS port %x\n", - hw_config->io_base)); + DDB (printk ("init_deskpro: Invalid MSS port %x\n", hw_config->io_base)); return 0; } outb ((tmp & ~0x04), 0xc44); /* Write to bank=0 */ @@ -2325,8 +2326,7 @@ { int ret; - DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n", - hw_config->io_base, (int) inb (hw_config->io_base + 3))); + DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb (hw_config->io_base + 3))); DDB (printk ("Trying to detect codec anyway but IRQ/DMA may not work\n")); if (!(ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp))) return 0; diff -u --recursive --new-file v2.1.62/linux/drivers/sound/adlib_card.c linux/drivers/sound/adlib_card.c --- v2.1.62/linux/drivers/sound/adlib_card.c Tue Mar 4 10:25:24 1997 +++ linux/drivers/sound/adlib_card.c Tue Sep 30 08:46:50 1997 @@ -16,7 +16,7 @@ #include "sound_config.h" -#if defined(CONFIG_YM3812) +#ifdef CONFIG_YM3812 void attach_adlib_card (struct address_info *hw_config) @@ -32,8 +32,7 @@ if (check_region (hw_config->io_base, 4)) { - DDB (printk ("opl3.c: I/O port %x already in use\n", - hw_config->io_base)); + DDB (printk ("opl3.c: I/O port %x already in use\n", hw_config->io_base)); return 0; } diff -u --recursive --new-file v2.1.62/linux/drivers/sound/audio.c linux/drivers/sound/audio.c --- v2.1.62/linux/drivers/sound/audio.c Wed May 28 10:51:32 1997 +++ linux/drivers/sound/audio.c Sun Nov 9 22:26:32 1997 @@ -30,7 +30,7 @@ #define AM_NONE 0 #define AM_WRITE OPEN_WRITE #define AM_READ OPEN_READ -static int dma_ioctl (int dev, unsigned int cmd, caddr_t arg); +int dma_ioctl (int dev, unsigned int cmd, caddr_t arg); static int local_format[MAX_AUDIO_DEV], audio_format[MAX_AUDIO_DEV]; @@ -59,7 +59,7 @@ else return local_format[dev]; - return audio_format[dev]; + return local_format[dev]; } int @@ -247,12 +247,16 @@ { if ((dma_buf + l) > (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize)) - printk ("audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", - (long) dma_buf, l, - (long) audio_devs[dev]->dmap_out->raw_buf, - (int) audio_devs[dev]->dmap_out->buffsize); + { + printk ("audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize); + return -EDOM; + } + if (dma_buf < audio_devs[dev]->dmap_out->raw_buf) - printk ("audio: Buffer error 13\n"); + { + printk ("audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf); + return -EDOM; + } copy_from_user (dma_buf, &(buf)[p], l); } else @@ -352,7 +356,7 @@ { int val; - /* printk("audio_ioctl(%x, %x)\n", (int)cmd, (int)arg); */ + /* printk( "audio_ioctl(%x, %x)\n", (int)cmd, (int)arg); */ dev = dev >> 4; @@ -484,8 +488,7 @@ n = *(int *) arg; if (n > 1) { - printk ("sound: SNDCTL_DSP_STEREO called with invalid argument %d\n", - n); + printk ("sound: SNDCTL_DSP_STEREO called with invalid argument %d\n", n); return -EINVAL; } @@ -550,13 +553,9 @@ unsigned i, n; unsigned sr, nc, sz, bsz; - if (!dmap->needs_reorg) - return; - sr = dsp_dev->d->set_speed (dev, 0); nc = dsp_dev->d->set_channels (dev, 0); sz = dsp_dev->d->set_bits (dev, 0); - dmap->needs_reorg = 0; if (sz == 8) dmap->neutral_byte = NEUTRAL8; @@ -565,8 +564,7 @@ if (sr < 1 || nc < 1 || sz < 1) { - printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", - dev, sr, nc, sz); + printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz); sr = DSP_DEFAULT_SPEED; nc = 1; sz = 8; @@ -577,6 +575,10 @@ sz /= 8; /* #bits -> #bytes */ dmap->data_rate = sz; + if (!dmap->needs_reorg) + return; + dmap->needs_reorg = 0; + if (dmap->fragment_size == 0) { /* Compute the fragment size using the default algorithm */ @@ -752,7 +754,7 @@ return 0; } -static int +int dma_ioctl (int dev, unsigned int cmd, caddr_t arg) { @@ -841,6 +843,11 @@ if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen) info->bytes -= dmap->counts[dmap->qhead]; + else + { + info->fragments = info->bytes / dmap->fragment_size; + info->bytes -= dmap->user_counter % dmap->fragment_size; + } } return 0; diff -u --recursive --new-file v2.1.62/linux/drivers/sound/configure.c linux/drivers/sound/configure.c --- v2.1.62/linux/drivers/sound/configure.c Thu May 29 21:53:08 1997 +++ linux/drivers/sound/configure.c Tue Sep 30 08:46:50 1997 @@ -45,18 +45,20 @@ #define OPT_MAD16 12 #define OPT_CS4232 13 #define OPT_MAUI 14 -#define OPT_SPNP 15 - -#define OPT_HIGHLEVEL 16 /* This must be same than the next one */ -#define OPT_UNUSED1 16 -#define OPT_UNUSED2 17 -#define OPT_AEDSP16 18 -#define OPT_UNUSED3 19 -#define OPT_UNUSED4 20 -#define OPT_UNUSED5 21 -#define OPT_YM3812_AUTO 22 -#define OPT_YM3812 23 -#define OPT_LAST 23 /* Last defined OPT number */ +#define OPT_SPNP 15 +#define OPT_OPL3SA1 16 +#define OPT_SOFTOSS 17 + +#define OPT_HIGHLEVEL 18 /* This must be same than the next one */ +#define OPT_UNUSED1 18 +#define OPT_UNUSED2 19 +#define OPT_AEDSP16 20 +#define OPT_UNUSED3 21 +#define OPT_UNUSED4 22 +#define OPT_UNUSED5 23 +#define OPT_YM3812_AUTO 24 +#define OPT_YM3812 25 +#define OPT_LAST 25 /* Last defined OPT number */ #define DUMMY_OPTS (B(OPT_YM3812_AUTO)) @@ -64,16 +66,17 @@ B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)| \ B(OPT_MSS)|B(OPT_SSCAPE)|B(OPT_UART6850)|B(OPT_TRIX)| \ B(OPT_MAD16)|B(OPT_CS4232)|B(OPT_MAUI)|B(OPT_ADLIB)| \ - B(OPT_SPNP)) + B(OPT_SPNP)|B(OPT_OPL3SA1)|B(OPT_SOFTOSS)) #define MPU_DEVS (B(OPT_PSS)|\ B(OPT_CS4232)|B(OPT_SPNP)|B(OPT_MAUI)|B(OPT_SSCAPE)) -#define UART401_DEVS (SBDSP_DEVS|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_SPNP)) +#define UART401_DEVS (SBDSP_DEVS|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_SPNP)|\ + B(OPT_OPL3SA1)) #define NON_AUDIO_CARDS (B(OPT_ADLIB)|B(OPT_MPU401)|B(OPT_UART6850)|B(OPT_MAUI)) #define AUDIO_CARDS (ANY_DEVS & ~NON_AUDIO_CARDS) #define MIDI_CARDS (ANY_DEVS & ~(B(OPT_ADLIB)|B(OPT_MSS))) #define AD1848_DEVS (B(OPT_GUS16)|B(OPT_MSS)|B(OPT_PSS)|B(OPT_GUSMAX)|\ B(OPT_SSCAPE)|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_CS4232)|\ - B(OPT_SPNP)) + B(OPT_SPNP)|B(OPT_OPL3SA1)) #define SBDSP_DEVS (B(OPT_SB)|B(OPT_SPNP)|B(OPT_MAD16)|B(OPT_TRIX)) #define SEQUENCER_DEVS 0x7fffffff /* @@ -131,6 +134,8 @@ {0, 0, "CS4232", 1, 0, 0}, {0, 0, "MAUI", 1, 0, 0}, {0, 0, "SPNP", 1, 0, 0}, + {0, 0, "OPL3SA1", 1, 0, 0}, + {0, 0, "SOFTOSS", 1, 0, 0}, {B (OPT_SB), B (OPT_PAS), "UNUSED1", 1, 0, 1}, {B (OPT_SB) | B (OPT_UNUSED1), B (OPT_PAS), "UNUSED2", 1, 0, 1}, @@ -156,10 +161,12 @@ "Microsoft Sound System support", "Ensoniq SoundScape support", "MediaTrix AudioTrix Pro support", - "Support for MAD16 and/or Mozart based cards", + "Support for OPTi MAD16 and/or Mozart based cards", "Support for Crystal CS4232 based (PnP) cards", "Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers", "Support for PnP sound cards (_EXPERIMENTAL_)", + "Yamaha OPL3-SA1 audio controller", + "SoftOSS software wave table engine", "*** Unused option 1 ***", "*** Unused option 2 ***", @@ -237,6 +244,11 @@ "Use this option to enable experimental support for cards that\n" "use the Plug and Play protocol.\n", + "Use this option with Yamaha OPL3-SA1 (YMF701) chip.\n", + + "SoftOSS is a virtual wave table engine by 4Front Technologies. It can\n" + "be used together with any 16 bit stereo soundcard.\n" + "Enable this option if your card is a Sound Blaster Pro or\n" "Sound Blaster 16. It also works with many Sound Blaster Pro clones.\n", @@ -1044,62 +1056,99 @@ if (dump_only) show_comment (B (OPT_TRIX), - "ERROR! You have to use old sound configuration method with AudioTrix."); + "ERROR! You have to use old sound configuration method with OPL3-SA1."); ask_int_choice (B (OPT_TRIX), "TRIX_BASE", - "AudioTrix audio I/O base", + "OPL3-SA1 audio I/O base", FMT_HEX, 0x530, "530, 604, E80 or F40"); ask_int_choice (B (OPT_TRIX), "TRIX_IRQ", - "AudioTrix audio IRQ", + "OPL3-SA1 audio IRQ", FMT_INT, 11, "7, 9, 10 or 11"); ask_int_choice (B (OPT_TRIX), "TRIX_DMA", - "AudioTrix audio DMA", + "OPL3-SA1 audio DMA", FMT_INT, 0, "0, 1 or 3"); ask_int_choice (B (OPT_TRIX), "TRIX_DMA2", - "AudioTrix second (duplex) DMA", + "OPL3-SA1 second (duplex) DMA", FMT_INT, 3, "0, 1 or 3"); ask_int_choice (B (OPT_TRIX), "TRIX_MPU_BASE", - "AudioTrix MIDI I/O base", + "OPL3-SA1 MIDI I/O base", FMT_HEX, 0x330, "330, 370, 3B0 or 3F0"); ask_int_choice (B (OPT_TRIX), "TRIX_MPU_IRQ", - "AudioTrix MIDI IRQ", + "OPL3-SA1 MIDI IRQ", FMT_INT, 9, "3, 4, 5, 7 or 9"); ask_int_choice (B (OPT_TRIX), "TRIX_SB_BASE", - "AudioTrix SB I/O base", + "OPL3-SA1 SB I/O base", FMT_HEX, 0x220, "220, 210, 230, 240, 250, 260 or 270"); ask_int_choice (B (OPT_TRIX), "TRIX_SB_IRQ", - "AudioTrix SB IRQ", + "OPL3-SA1 SB IRQ", FMT_INT, 7, "3, 4, 5 or 7"); ask_int_choice (B (OPT_TRIX), "TRIX_SB_DMA", - "AudioTrix SB DMA", + "OPL3-SA1 SB DMA", FMT_INT, 1, "1 or 3"); + + ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_BASE", + "OPL3-SA1 audio I/O base", + FMT_HEX, + 0x530, + "530, 604, E80 or F40"); + + ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_IRQ", + "OPL3-SA1 audio IRQ", + FMT_INT, + 11, + "7, 9, 10 or 11"); + + ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_DMA", + "OPL3-SA1 audio DMA", + FMT_INT, + 0, + "0, 1 or 3"); + + ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_DMA2", + "OPL3-SA1 second (duplex) DMA", + FMT_INT, + 3, + "0, 1 or 3"); + + ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_MPU_BASE", + "OPL3-SA1 MIDI I/O base", + FMT_HEX, + 0x330, + "330, 370, 3B0 or 3F0"); + + ask_int_choice (B (OPT_OPL3SA1), "OPL3SA1_MPU_IRQ", + "OPL3-SA1 MIDI IRQ", + FMT_INT, + 9, + "3, 4, 5, 7 or 9"); + ask_int_choice (B (OPT_CS4232), "CS4232_BASE", "CS4232 audio I/O base", FMT_HEX, @@ -1171,6 +1220,16 @@ FMT_INT, 9, "5, 7, 9 or 10"); + ask_int_choice (B (OPT_SOFTOSS), "SOFTOSS_RATE", + "Sampling rate for SoftOSS", + FMT_INT, + 22050, + "8000 to 48000"); + ask_int_choice (B (OPT_SOFTOSS), "SOFTOSS_VOICES", + "Max # of concurrent voices for SoftOSS", + FMT_INT, + 32, + "4 to 32"); } void diff -u --recursive --new-file v2.1.62/linux/drivers/sound/cs4232.c linux/drivers/sound/cs4232.c --- v2.1.62/linux/drivers/sound/cs4232.c Tue Mar 4 10:25:24 1997 +++ linux/drivers/sound/cs4232.c Tue Sep 30 08:46:50 1997 @@ -25,7 +25,7 @@ #include "sound_config.h" -#if defined(CONFIG_CS4232) +#ifdef CONFIG_CS4232 #define KEY_PORT 0x279 /* Same as LPT1 status port */ #define CSN_NUM 0x99 /* Just a random number */ diff -u --recursive --new-file v2.1.62/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c --- v2.1.62/linux/drivers/sound/dev_table.c Wed May 28 10:51:32 1997 +++ linux/drivers/sound/dev_table.c Mon Oct 20 06:26:29 1997 @@ -16,6 +16,7 @@ #define _DEV_TABLE_C_ #include "sound_config.h" int sb_be_quiet = 0; +int softoss_dev = 0; int sound_started = 0; @@ -154,6 +155,9 @@ } } + for (i=0;i /* * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h) @@ -23,6 +22,23 @@ */ #define SNDCARD_DESKPROXL 27 /* Compaq Deskpro XL */ #define SNDCARD_SBPNP 29 +#define SNDCARD_OPL3SA1 38 +#define SNDCARD_OPL3SA1_SB 39 +#define SNDCARD_OPL3SA1_MPU 40 +#define SNDCARD_SOFTOSS 36 + +void attach_opl3sa_wss (struct address_info *hw_config); +int probe_opl3sa_wss (struct address_info *hw_config); +void attach_opl3sa_sb (struct address_info *hw_config); +int probe_opl3sa_sb (struct address_info *hw_config); +void attach_opl3sa_mpu (struct address_info *hw_config); +int probe_opl3sa_mpu (struct address_info *hw_config); +void unload_opl3sa_wss(struct address_info *hw_info); +void unload_opl3sa_sb(struct address_info *hw_info); +void unload_opl3sa_mpu(struct address_info *hw_info); +void attach_softsyn_card (struct address_info *hw_config); +int probe_softsyn (struct address_info *hw_config); +void unload_softsyn (struct address_info *hw_config); /* * NOTE! NOTE! NOTE! NOTE! @@ -125,6 +141,10 @@ OS_DMA_PARMS #endif int applic_profile; /* Application profile (APF_*) */ + /* Interrupt callback stuff */ + void (*audio_callback) (int dev, int parm); + int callback_parm; + int buf_flags[MAX_SUB_BUFFERS]; #define BUFF_EOF 0x00000001 /* Increment eof count */ #define BUFF_DIRTY 0x00000002 /* Buffer written */ @@ -381,12 +401,23 @@ {"SSCAPE", 0, SNDCARD_SSCAPE, "Ensoniq SoundScape", attach_sscape, probe_sscape, unload_sscape}, {"SSCAPEMSS", 0, SNDCARD_SSCAPE_MSS, "MS Sound System (SoundScape)", attach_ss_ms_sound, probe_ss_ms_sound, unload_ss_ms_sound}, #endif + +#ifdef CONFIG_OPL3SA1 + {"OPL3SA", 0, SNDCARD_OPL3SA1, "Yamaha OPL3-SA", attach_opl3sa_wss, probe_opl3sa_wss, unload_opl3sa_wss}, +/* {"OPL3SASB", 0, SNDCARD_OPL3SA1_SB, "OPL3-SA (SB mode)", attach_opl3sa_sb, probe_opl3sa_sb, unload_opl3sa_sb}, */ + {"OPL3SAMPU", 0, SNDCARD_OPL3SA1_MPU, "OPL3-SA MIDI", attach_opl3sa_mpu, probe_opl3sa_mpu, unload_opl3sa_mpu}, +#endif + #ifdef CONFIG_TRIX {"TRXPRO", 0, SNDCARD_TRXPRO, "MediaTrix AudioTrix Pro", attach_trix_wss, probe_trix_wss, unload_trix_wss}, {"TRXPROSB", 0, SNDCARD_TRXPRO_SB, "AudioTrix (SB mode)", attach_trix_sb, probe_trix_sb, unload_trix_sb}, {"TRXPROMPU", 0, SNDCARD_TRXPRO_MPU, "AudioTrix MIDI", attach_trix_mpu, probe_trix_mpu, unload_trix_mpu}, #endif +#ifdef CONFIG_SOFTOSS + {"SOFTSYN", 0, SNDCARD_SOFTOSS, "SoftOSS Virtual Wave Table", + attach_softsyn_card, probe_softsyn, unload_softsyn}, +#endif @@ -427,6 +458,18 @@ {SNDCARD_TRXPRO_MPU, {TRIX_MPU_BASE, TRIX_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, # endif #endif + +#ifdef CONFIG_OPL3SA1 + {SNDCARD_OPL3SA1, {OPL3SA1_BASE, OPL3SA1_IRQ, OPL3SA1_DMA, OPL3SA1_DMA2}, SND_DEFAULT_ENABLE}, +# ifdef OPL3SA1_MPU_BASE + {SNDCARD_OPL3SA1_MPU, {OPL3SA1_MPU_BASE, OPL3SA1_MPU_IRQ, 0, -1}, SND_DEFAULT_ENABLE}, +# endif +#endif + +#ifdef CONFIG_SOFTOSS + {SNDCARD_SOFTOSS, {0, 0, -1, -1}, SND_DEFAULT_ENABLE}, +#endif + #ifdef CONFIG_SSCAPE {SNDCARD_SSCAPE, {SSCAPE_BASE, SSCAPE_IRQ, SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE}, {SNDCARD_SSCAPE_MSS, {SSCAPE_MSS_BASE, SSCAPE_MSS_IRQ, SSCAPE_DMA, -1}, SND_DEFAULT_ENABLE}, diff -u --recursive --new-file v2.1.62/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c --- v2.1.62/linux/drivers/sound/dmabuf.c Wed Sep 24 20:05:47 1997 +++ linux/drivers/sound/dmabuf.c Mon Oct 20 06:25:00 1997 @@ -94,6 +94,8 @@ dmap->max_byte_counter = 8000 * 60 * 60; dmap->applic_profile = APF_NORMAL; dmap->needs_reorg = 1; + dmap->audio_callback = NULL; + dmap->callback_parm = 0; if (dmap->dma_mode & DMODE_OUTPUT) @@ -267,7 +269,7 @@ tmout = (dmap->fragment_size * HZ) / dmap->data_rate; - tmout += HZ / 10; /* Some safety distance */ + tmout += HZ / 5; /* Some safety distance */ if (tmout < (HZ / 2)) tmout = HZ / 2; @@ -277,7 +279,7 @@ audio_devs[dev]->dmap_out->flags |= DMA_SYNCING; audio_devs[dev]->dmap_out->underrun_count = 0; - if (!signal_pending(current) + if (!(current->signal & ~current->blocked) && audio_devs[dev]->dmap_out->qlen && audio_devs[dev]->dmap_out->underrun_count == 0) { @@ -400,7 +402,7 @@ tmout = (dmap->fragment_size * HZ) / dmap->data_rate; - tmout += HZ / 10; /* Some safety distance */ + tmout += HZ / 5; /* Some safety distance */ if (tmout < (HZ / 2)) tmout = HZ / 2; @@ -416,7 +418,7 @@ audio_devs[dev]->dmap_out->flags |= DMA_SYNCING; audio_devs[dev]->dmap_out->underrun_count = 0; - while (!signal_pending(current) + while (!(current->signal & ~current->blocked) && n++ <= audio_devs[dev]->dmap_out->nbufs && audio_devs[dev]->dmap_out->qlen && audio_devs[dev]->dmap_out->underrun_count == 0) @@ -456,7 +458,7 @@ cli (); if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ { - while (!signal_pending(current) + while (!((current->signal & ~current->blocked)) && audio_devs[dev]->d->local_qlen (dev)) { @@ -496,7 +498,7 @@ if (audio_devs[dev]->open_mode & OPEN_WRITE) if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)) - if (!signal_pending(current) + if (!((current->signal & ~current->blocked)) && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)) { DMAbuf_sync (dev); @@ -592,6 +594,7 @@ if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) { printk ("Sound: Can't read from mmapped device (1)\n"); + restore_flags (flags); return -EINVAL; } else @@ -627,7 +630,7 @@ tmout = (dmap->fragment_size * HZ) / dmap->data_rate; - tmout += HZ / 10; /* Some safety distance */ + tmout += HZ / 5; /* Some safety distance */ if (tmout < (HZ / 2)) tmout = HZ / 2; @@ -747,7 +750,7 @@ enable_dma (dmap->dma); } restore_flags (flags); - /* printk ("%04x ", pos); */ + /* printk( "%04x ", pos); */ return pos; } @@ -802,8 +805,8 @@ */ max = dmap->max_fragments; - if (max > dmap->nbufs) - max = dmap->nbufs; + if (max > lim) + max = lim; len = dmap->qlen; if (audio_devs[dev]->d->local_qlen) @@ -815,6 +818,8 @@ */ len += tmp; } + if (dmap->byte_counter % dmap->fragment_size) /* There is a partial fragment */ + len = len + 1; if (len >= max) return 0; @@ -848,7 +853,7 @@ tmout = (dmap->fragment_size * HZ) / dmap->data_rate; - tmout += HZ / 10; /* Some safety distance */ + tmout += HZ / 5; /* Some safety distance */ if (tmout < (HZ / 2)) tmout = HZ / 2; @@ -856,7 +861,7 @@ tmout = 20 * HZ; } - if (signal_pending(current)) + if ((current->signal & ~current->blocked)) return -EIO; @@ -882,7 +887,7 @@ ; dma_reset_output (dev); } - else if (signal_pending(current)) + else if ((current->signal & ~current->blocked)) { err = -EINTR; } @@ -895,11 +900,14 @@ { struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; unsigned long flags; - unsigned long offs, active_offs; - long len; + unsigned long active_offs; + long len, offs; int maxfrags; + int occupied_bytes = (dmap->user_counter % dmap->fragment_size); + + *buf = dmap->raw_buf; - if (!(maxfrags = DMAbuf_space_in_queue (dev))) + if (!(maxfrags = DMAbuf_space_in_queue (dev)) && !occupied_bytes) { return 0; } @@ -918,12 +926,20 @@ #endif offs = (dmap->user_counter % dmap->bytes_in_use) & ~3; + if (offs < 0 || offs >= dmap->bytes_in_use) + { + printk ("OSS: Got unexpected offs %ld. Giving up.\n", offs); + printk ("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use); + return 0; + } *buf = dmap->raw_buf + offs; len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */ if ((offs + len) > dmap->bytes_in_use) - len = dmap->bytes_in_use - offs; + { + len = dmap->bytes_in_use - offs; + } if (len < 0) { @@ -931,8 +947,10 @@ return 0; } - if (len > maxfrags * dmap->fragment_size) - len = maxfrags * dmap->fragment_size; + if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes)) + { + len = (maxfrags * dmap->fragment_size) - occupied_bytes; + } *size = len & ~3; @@ -967,7 +985,7 @@ save_flags (flags); cli (); - while (!find_output_space (dev, buf, size)) + while (find_output_space (dev, buf, size) <= 0) { if ((err = output_sleep (dev, dontblock)) < 0) { @@ -1063,8 +1081,7 @@ if (dmap->raw_buf == NULL) { printk ("sound: DMA buffer(1) == NULL\n"); - printk ("Device %d, chn=%s\n", dev, - (dmap == audio_devs[dev]->dmap_out) ? "out" : "in"); + printk ("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in"); return 0; } @@ -1096,8 +1113,7 @@ if (dmap->raw_buf == NULL) { printk ("sound: DMA buffer(2) == NULL\n"); - printk ("Device %d, chn=%s\n", dev, - (dmap == audio_devs[dev]->dmap_out) ? "out" : "in"); + printk ("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in"); return 0; } @@ -1120,6 +1136,9 @@ { unsigned long flags; + if (dmap->audio_callback != NULL) + dmap->audio_callback (dev, dmap->callback_parm); + save_flags (flags); cli (); if ((out_sleep_flag[dev].opts & WK_SLEEP)) @@ -1308,7 +1327,7 @@ } else if (dmap->qlen >= (dmap->nbufs - 1)) { - /* printk ("Sound: Recording overrun\n"); */ + printk ("Sound: Recording overrun\n"); dmap->underrun_count++; /* Just throw away the oldest fragment but keep the engine running */ @@ -1576,5 +1595,16 @@ return 0; } +void +DMAbuf_deinit(int dev) +{ +/* This routine is called when driver is being unloaded */ +#ifdef RUNTIME_DMA_ALLOC + sound_free_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma); + + if (audio_devs[dev]->flags & DMA_DUPLEX) + sound_free_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma); +#endif +} #endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/dmabuf.c.old linux/drivers/sound/dmabuf.c.old --- v2.1.62/linux/drivers/sound/dmabuf.c.old Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/dmabuf.c.old Mon Oct 20 06:24:40 1997 @@ -0,0 +1,1599 @@ +/* + * sound/dmabuf.c + * + * The DMA buffer manager for digitized voice applications + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +#include + +#define BE_CONSERVATIVE + +#include "sound_config.h" + +#if defined(CONFIG_AUDIO) || defined(CONFIG_GUSHW) + +static struct wait_queue *in_sleeper[MAX_AUDIO_DEV] = +{NULL}; +static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] = +{ + {0}}; +static struct wait_queue *out_sleeper[MAX_AUDIO_DEV] = +{NULL}; +static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] = +{ + {0}}; + +static int ndmaps = 0; + +#define MAX_DMAP (MAX_AUDIO_DEV*2) + +static struct dma_buffparms dmaps[MAX_DMAP] = +{ + {0}}; + +static void dma_reset_output (int dev); +static void dma_reset_input (int dev); +static int local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); + +static void +dma_init_buffers (int dev, struct dma_buffparms *dmap) +{ + + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; + dmap->byte_counter = 0; + dmap->max_byte_counter = 8000 * 60 * 60; + dmap->bytes_in_use = dmap->buffsize; + + dmap->dma_mode = DMODE_NONE; + dmap->mapping_flags = 0; + dmap->neutral_byte = 0x80; + dmap->data_rate = 8000; + dmap->cfrag = -1; + dmap->closing = 0; + dmap->nbufs = 1; + dmap->flags = DMA_BUSY; /* Other flags off */ +} + +static int +open_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan) +{ + if (dmap->flags & DMA_BUSY) + return -EBUSY; + + { + int err; + + if ((err = sound_alloc_dmap (dev, dmap, chan)) < 0) + return err; + } + + if (dmap->raw_buf == NULL) + { + printk ("Sound: DMA buffers not available\n"); + return -ENOSPC; /* Memory allocation failed during boot */ + } + + if (sound_open_dma (chan, audio_devs[dev]->name)) + { + printk ("Unable to grab(2) DMA%d for the audio driver\n", chan); + return -EBUSY; + } + + dma_init_buffers (dev, dmap); + dmap->open_mode = mode; + dmap->subdivision = dmap->underrun_count = 0; + dmap->fragment_size = 0; + dmap->max_fragments = 65536; /* Just a large value */ + dmap->byte_counter = 0; + dmap->max_byte_counter = 8000 * 60 * 60; + dmap->applic_profile = APF_NORMAL; + dmap->needs_reorg = 1; + dmap->audio_callback = NULL; + dmap->callback_parm = 0; + + + if (dmap->dma_mode & DMODE_OUTPUT) + { + out_sleep_flag[dev].opts = WK_NONE; + } + else + { + in_sleep_flag[dev].opts = WK_NONE; + } + + return 0; +} + +static void +close_dmap (int dev, struct dma_buffparms *dmap, int chan) +{ + sound_close_dma (chan); + + if (dmap->flags & DMA_BUSY) + dmap->dma_mode = DMODE_NONE; + dmap->flags &= ~DMA_BUSY; + + disable_dma (dmap->dma); +} + + +static unsigned int +default_set_bits (int dev, unsigned int bits) +{ + return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) & bits); +} + +static int +default_set_speed (int dev, int speed) +{ + return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) & speed); +} + +static short +default_set_channels (int dev, short channels) +{ + int c = channels; + + return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) & c); +} + +static void +check_driver (struct audio_driver *d) +{ + if (d->set_speed == NULL) + d->set_speed = default_set_speed; + if (d->set_bits == NULL) + d->set_bits = default_set_bits; + if (d->set_channels == NULL) + d->set_channels = default_set_channels; +} + +int +DMAbuf_open (int dev, int mode) +{ + int retval; + struct dma_buffparms *dmap_in = NULL; + struct dma_buffparms *dmap_out = NULL; + + if (dev >= num_audiodevs) + { + return -ENXIO; + } + + if (!audio_devs[dev]) + { + return -ENXIO; + } + + if (!(audio_devs[dev]->flags & DMA_DUPLEX)) + { + audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out; + audio_devs[dev]->dmap_in->dma = audio_devs[dev]->dmap_out->dma; + } + + check_driver (audio_devs[dev]->d); + + if ((retval = audio_devs[dev]->d->open (dev, mode)) < 0) + return retval; + + dmap_out = audio_devs[dev]->dmap_out; + dmap_in = audio_devs[dev]->dmap_in; + + if (dmap_in == dmap_out) + audio_devs[dev]->flags &= ~DMA_DUPLEX; + + if (mode & OPEN_WRITE) + { + if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmap_out->dma)) < 0) + { + audio_devs[dev]->d->close (dev); + return retval; + } + } + + audio_devs[dev]->enable_bits = mode; + + if (mode == OPEN_READ || (mode != OPEN_WRITE && + audio_devs[dev]->flags & DMA_DUPLEX)) + { + if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmap_in->dma)) < 0) + { + audio_devs[dev]->d->close (dev); + + if (mode & OPEN_WRITE) + { + close_dmap (dev, dmap_out, audio_devs[dev]->dmap_out->dma); + } + + return retval; + } + } + + audio_devs[dev]->open_mode = mode; + audio_devs[dev]->go = 1; + + if (mode & OPEN_READ) + in_sleep_flag[dev].opts = WK_NONE; + + if (mode & OPEN_WRITE) + out_sleep_flag[dev].opts = WK_NONE; + + audio_devs[dev]->d->set_bits (dev, 8); + audio_devs[dev]->d->set_channels (dev, 1); + audio_devs[dev]->d->set_speed (dev, DSP_DEFAULT_SPEED); + + if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT) + { + memset (audio_devs[dev]->dmap_out->raw_buf, + audio_devs[dev]->dmap_out->neutral_byte, + audio_devs[dev]->dmap_out->bytes_in_use); + } + + return 0; +} + +void +DMAbuf_reset (int dev) +{ + if (audio_devs[dev]->open_mode & OPEN_WRITE) + dma_reset_output (dev); + + if (audio_devs[dev]->open_mode & OPEN_READ) + dma_reset_input (dev); +} + +static void +dma_reset_output (int dev) +{ + unsigned long flags; + int tmout; + + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + + if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */ + return; + +/* + * First wait until the current fragment has been played completely + */ + save_flags (flags); + cli (); + + tmout = + (dmap->fragment_size * HZ) / dmap->data_rate; + + tmout += HZ / 5; /* Some safety distance */ + + if (tmout < (HZ / 2)) + tmout = HZ / 2; + if (tmout > 20 * HZ) + tmout = 20 * HZ; + + audio_devs[dev]->dmap_out->flags |= DMA_SYNCING; + + audio_devs[dev]->dmap_out->underrun_count = 0; + if (!(current->signal & ~current->blocked) + && audio_devs[dev]->dmap_out->qlen + && audio_devs[dev]->dmap_out->underrun_count == 0) + { + + { + unsigned long tlimit; + + if (tmout) + current->timeout = tlimit = jiffies + (tmout); + else + tlimit = (unsigned long) -1; + out_sleep_flag[dev].opts = WK_SLEEP; + interruptible_sleep_on (&out_sleeper[dev]); + if (!(out_sleep_flag[dev].opts & WK_WAKEUP)) + { + if (jiffies >= tlimit) + out_sleep_flag[dev].opts |= WK_TIMEOUT; + } + out_sleep_flag[dev].opts &= ~WK_SLEEP; + }; + } + audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); + +/* + * Finally shut the device off + */ + + if (!(audio_devs[dev]->flags & DMA_DUPLEX) || + !audio_devs[dev]->d->halt_output) + audio_devs[dev]->d->halt_io (dev); + else + audio_devs[dev]->d->halt_output (dev); + audio_devs[dev]->dmap_out->flags &= ~DMA_STARTED; + restore_flags (flags); + + clear_dma_ff (dmap->dma); + disable_dma (dmap->dma); + dmap->byte_counter = 0; + reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0); + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; +} + +static void +dma_reset_input (int dev) +{ + unsigned long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + + save_flags (flags); + cli (); + if (!(audio_devs[dev]->flags & DMA_DUPLEX) || + !audio_devs[dev]->d->halt_input) + audio_devs[dev]->d->halt_io (dev); + else + audio_devs[dev]->d->halt_input (dev); + audio_devs[dev]->dmap_in->flags &= ~DMA_STARTED; + restore_flags (flags); + + dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; + dmap->byte_counter = 0; + reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1); +} + +void +DMAbuf_launch_output (int dev, struct dma_buffparms *dmap) +{ + if (!((audio_devs[dev]->enable_bits * audio_devs[dev]->go) & PCM_ENABLE_OUTPUT)) + return; /* Don't start DMA yet */ + + dmap->dma_mode = DMODE_OUTPUT; + + if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA) + { + if (!(dmap->flags & DMA_STARTED)) + { + reorganize_buffers (dev, dmap, 0); + + if (audio_devs[dev]->d->prepare_for_output (dev, + dmap->fragment_size, dmap->nbufs)) + return; + + if (!(dmap->flags & DMA_NODMA)) + { + local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use, + DMA_MODE_WRITE); + } + dmap->flags |= DMA_STARTED; + } + if (dmap->counts[dmap->qhead] == 0) + dmap->counts[dmap->qhead] = dmap->fragment_size; + + dmap->dma_mode = DMODE_OUTPUT; + audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys + + dmap->qhead * dmap->fragment_size, + dmap->counts[dmap->qhead], 1); + if (audio_devs[dev]->d->trigger) + audio_devs[dev]->d->trigger (dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } + dmap->flags |= DMA_ACTIVE; +} + +int +DMAbuf_sync (int dev) +{ + unsigned long flags; + int tmout, n = 0; + + if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT)) + return 0; + + if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT) + { + + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + save_flags (flags); + cli (); + + tmout = + (dmap->fragment_size * HZ) / dmap->data_rate; + + tmout += HZ / 5; /* Some safety distance */ + + if (tmout < (HZ / 2)) + tmout = HZ / 2; + if (tmout > 20 * HZ) + tmout = 20 * HZ; + + ; + if (dmap->qlen > 0) + if (!(dmap->flags & DMA_ACTIVE)) + DMAbuf_launch_output (dev, dmap); + ; + + audio_devs[dev]->dmap_out->flags |= DMA_SYNCING; + + audio_devs[dev]->dmap_out->underrun_count = 0; + while (!(current->signal & ~current->blocked) + && n++ <= audio_devs[dev]->dmap_out->nbufs + && audio_devs[dev]->dmap_out->qlen + && audio_devs[dev]->dmap_out->underrun_count == 0) + { + + { + unsigned long tlimit; + + if (tmout) + current->timeout = tlimit = jiffies + (tmout); + else + tlimit = (unsigned long) -1; + out_sleep_flag[dev].opts = WK_SLEEP; + interruptible_sleep_on (&out_sleeper[dev]); + if (!(out_sleep_flag[dev].opts & WK_WAKEUP)) + { + if (jiffies >= tlimit) + out_sleep_flag[dev].opts |= WK_TIMEOUT; + } + out_sleep_flag[dev].opts &= ~WK_SLEEP; + }; + if ((out_sleep_flag[dev].opts & WK_TIMEOUT)) + { + audio_devs[dev]->dmap_out->flags &= ~DMA_SYNCING; + restore_flags (flags); + return audio_devs[dev]->dmap_out->qlen; + } + } + audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); + restore_flags (flags); + /* + * Some devices such as GUS have huge amount of on board RAM for the + * audio data. We have to wait until the device has finished playing. + */ + + save_flags (flags); + cli (); + if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ + { + while (!((current->signal & ~current->blocked)) + && audio_devs[dev]->d->local_qlen (dev)) + { + + { + unsigned long tlimit; + + if (tmout) + current->timeout = tlimit = jiffies + (tmout); + else + tlimit = (unsigned long) -1; + out_sleep_flag[dev].opts = WK_SLEEP; + interruptible_sleep_on (&out_sleeper[dev]); + if (!(out_sleep_flag[dev].opts & WK_WAKEUP)) + { + if (jiffies >= tlimit) + out_sleep_flag[dev].opts |= WK_TIMEOUT; + } + out_sleep_flag[dev].opts &= ~WK_SLEEP; + }; + } + } + restore_flags (flags); + } + audio_devs[dev]->dmap_out->dma_mode = DMODE_NONE; + return audio_devs[dev]->dmap_out->qlen; +} + +int +DMAbuf_release (int dev, int mode) +{ + unsigned long flags; + + if (audio_devs[dev]->open_mode & OPEN_WRITE) + audio_devs[dev]->dmap_out->closing = 1; + if (audio_devs[dev]->open_mode & OPEN_READ) + audio_devs[dev]->dmap_in->closing = 1; + + if (audio_devs[dev]->open_mode & OPEN_WRITE) + if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)) + if (!((current->signal & ~current->blocked)) + && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)) + { + DMAbuf_sync (dev); + } + + if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT) + { + memset (audio_devs[dev]->dmap_out->raw_buf, + audio_devs[dev]->dmap_out->neutral_byte, + audio_devs[dev]->dmap_out->bytes_in_use); + } + + save_flags (flags); + cli (); + + DMAbuf_reset (dev); + audio_devs[dev]->d->close (dev); + + if (audio_devs[dev]->open_mode & OPEN_WRITE) + close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma); + + if (audio_devs[dev]->open_mode == OPEN_READ || + (audio_devs[dev]->open_mode != OPEN_WRITE && + audio_devs[dev]->flags & DMA_DUPLEX)) + close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma); + audio_devs[dev]->open_mode = 0; + + restore_flags (flags); + + return 0; +} + +int +DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap) +{ + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return 0; + + if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT)) + return 0; + + if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */ + { + DMAbuf_sync (dev); + DMAbuf_reset (dev); + dmap->dma_mode = DMODE_NONE; + } + + if (!dmap->dma_mode) + { + int err; + + reorganize_buffers (dev, dmap, 1); + if ((err = audio_devs[dev]->d->prepare_for_input (dev, + dmap->fragment_size, dmap->nbufs)) < 0) + { + return err; + } + dmap->dma_mode = DMODE_INPUT; + } + + if (!(dmap->flags & DMA_ACTIVE)) + { + if (dmap->needs_reorg) + reorganize_buffers (dev, dmap, 0); + local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use, + DMA_MODE_READ); + audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys + + dmap->qtail * dmap->fragment_size, + dmap->fragment_size, 0); + dmap->flags |= DMA_ACTIVE; + if (audio_devs[dev]->d->trigger) + audio_devs[dev]->d->trigger (dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } + return 0; +} + +int +DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) +{ + unsigned long flags; + int err = 0, n = 0; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return -EIO; + if (dmap->needs_reorg) + reorganize_buffers (dev, dmap, 0); + + save_flags (flags); + cli (); + if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) + { + printk ("Sound: Can't read from mmapped device (1)\n"); + restore_flags (flags); + return -EINVAL; + } + else + while (dmap->qlen <= 0 && n++ < 10) + { + int tmout; + + if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) || + !audio_devs[dev]->go) + { + restore_flags (flags); + return -EAGAIN; + } + + if ((err = DMAbuf_activate_recording (dev, dmap)) < 0) + { + restore_flags (flags); + return err; + } + + /* Wait for the next block */ + + if (dontblock) + { + restore_flags (flags); + return -EAGAIN; + } + + if (!audio_devs[dev]->go) + tmout = 0; + else + { + tmout = + (dmap->fragment_size * HZ) / dmap->data_rate; + + tmout += HZ / 5; /* Some safety distance */ + + if (tmout < (HZ / 2)) + tmout = HZ / 2; + if (tmout > 20 * HZ) + tmout = 20 * HZ; + } + + + { + unsigned long tlimit; + + if (tmout) + current->timeout = tlimit = jiffies + (tmout); + else + tlimit = (unsigned long) -1; + in_sleep_flag[dev].opts = WK_SLEEP; + interruptible_sleep_on (&in_sleeper[dev]); + if (!(in_sleep_flag[dev].opts & WK_WAKEUP)) + { + if (jiffies >= tlimit) + in_sleep_flag[dev].opts |= WK_TIMEOUT; + } + in_sleep_flag[dev].opts &= ~WK_SLEEP; + }; + if ((in_sleep_flag[dev].opts & WK_TIMEOUT)) + { + err = -EIO; + printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); + dma_reset_input (dev); + ; + } + else + err = -EINTR; + } + restore_flags (flags); + + if (dmap->qlen <= 0) + { + if (err == 0) + err = -EINTR; + return err; + } + + *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]]; + *len = dmap->fragment_size - dmap->counts[dmap->qhead]; + + return dmap->qhead; +} + +int +DMAbuf_rmchars (int dev, int buff_no, int c) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + + int p = dmap->counts[dmap->qhead] + c; + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + { + printk ("Sound: Can't read from mmapped device (2)\n"); + return -EINVAL; + } + else if (dmap->qlen <= 0) + return -EIO; + else if (p >= dmap->fragment_size) + { /* This buffer is completely empty */ + dmap->counts[dmap->qhead] = 0; + dmap->qlen--; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + } + else + dmap->counts[dmap->qhead] = p; + + return 0; +} + +int +DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction) +{ +/* + * Try to approximate the active byte position of the DMA pointer within the + * buffer area as well as possible. + */ + int pos; + unsigned long flags; + + save_flags (flags); + cli (); + if (!(dmap->flags & DMA_ACTIVE)) + pos = 0; + else + { + int chan = dmap->dma; + + clear_dma_ff (chan); + disable_dma (dmap->dma); + pos = get_dma_residue (chan); + pos = dmap->bytes_in_use - pos; + + if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) + if (direction == DMODE_OUTPUT) + { + if (dmap->qhead == 0) + if (pos > dmap->fragment_size) + pos = 0; + } + else + { + if (dmap->qtail == 0) + if (pos > dmap->fragment_size) + pos = 0; + } + + if (pos < 0) + pos = 0; + if (pos >= dmap->bytes_in_use) + pos = 0; + enable_dma (dmap->dma); + } + restore_flags (flags); + /* printk( "%04x ", pos); */ + + return pos; +} + +/* + * DMAbuf_start_devices() is called by the /dev/music driver to start + * one or more audio devices at desired moment. + */ +static void +DMAbuf_start_device (int dev) +{ + if (audio_devs[dev]->open_mode != 0) + if (!audio_devs[dev]->go) + { + /* OK to start the device */ + audio_devs[dev]->go = 1; + + if (audio_devs[dev]->d->trigger) + audio_devs[dev]->d->trigger (dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } +} + +void +DMAbuf_start_devices (unsigned int devmask) +{ + int dev; + + for (dev = 0; dev < num_audiodevs; dev++) + if (devmask & (1 << dev)) + DMAbuf_start_device (dev); +} + +int +DMAbuf_space_in_queue (int dev) +{ + int len, max, tmp; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + int lim = dmap->nbufs; + + + if (lim < 2) + lim = 2; + + if (dmap->qlen >= lim) /* No space at all */ + return 0; + + /* + * Verify that there are no more pending buffers than the limit + * defined by the process. + */ + + max = dmap->max_fragments; + if (max > lim) + max = lim; + len = dmap->qlen; + + if (audio_devs[dev]->d->local_qlen) + { + tmp = audio_devs[dev]->d->local_qlen (dev); + if (tmp && len) + tmp--; /* + * This buffer has been counted twice + */ + len += tmp; + } + if (dmap->byte_counter % dmap->fragment_size) /* There is a partial fragment */ + len = len + 1; + + if (len >= max) + return 0; + return max - len; +} + +static int +output_sleep (int dev, int dontblock) +{ + int tmout; + int err = 0; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + if (dontblock) + { + return -EAGAIN; + } + + if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT)) + { + return -EAGAIN; + } + + /* + * Wait for free space + */ + if (!audio_devs[dev]->go || dmap->flags & DMA_NOTIMEOUT) + tmout = 0; + else + { + tmout = + (dmap->fragment_size * HZ) / dmap->data_rate; + + tmout += HZ / 5; /* Some safety distance */ + + if (tmout < (HZ / 2)) + tmout = HZ / 2; + if (tmout > 20 * HZ) + tmout = 20 * HZ; + } + + if ((current->signal & ~current->blocked)) + return -EIO; + + + { + unsigned long tlimit; + + if (tmout) + current->timeout = tlimit = jiffies + (tmout); + else + tlimit = (unsigned long) -1; + out_sleep_flag[dev].opts = WK_SLEEP; + interruptible_sleep_on (&out_sleeper[dev]); + if (!(out_sleep_flag[dev].opts & WK_WAKEUP)) + { + if (jiffies >= tlimit) + out_sleep_flag[dev].opts |= WK_TIMEOUT; + } + out_sleep_flag[dev].opts &= ~WK_SLEEP; + }; + if ((out_sleep_flag[dev].opts & WK_TIMEOUT)) + { + printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); + ; + dma_reset_output (dev); + } + else if ((current->signal & ~current->blocked)) + { + err = -EINTR; + } + + return err; +} + +static int +find_output_space (int dev, char **buf, int *size) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + unsigned long flags; + unsigned long active_offs; + long len, offs; + int maxfrags; + int occupied_bytes = (dmap->user_counter % dmap->fragment_size); + + *buf = dmap->raw_buf; + + if (!(maxfrags = DMAbuf_space_in_queue (dev)) && !occupied_bytes) + { + return 0; + } + + save_flags (flags); + cli (); + +#ifdef BE_CONSERVATIVE + active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size; +#else + active_offs = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT); + /* Check for pointer wrapping situation */ + if (active_offs < 0 || active_offs >= dmap->bytes_in_use) + active_offs = 0; + active_offs += dmap->byte_counter; +#endif + + offs = (dmap->user_counter % dmap->bytes_in_use) & ~3; + if (offs < 0 || offs >= dmap->bytes_in_use) + { + printk ("OSS: Got unexpected offs %ld. Giving up.\n", offs); + printk ("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use); + return 0; + } + *buf = dmap->raw_buf + offs; + + len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */ + + if ((offs + len) > dmap->bytes_in_use) + { + len = dmap->bytes_in_use - offs; + } + + if (len < 0) + { + restore_flags (flags); + return 0; + } + + if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes)) + { + len = (maxfrags * dmap->fragment_size) - occupied_bytes; + } + + *size = len & ~3; + + restore_flags (flags); + return (len > 0); +} + +int +DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock) +{ + unsigned long flags; + int err = -EIO; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + if (dmap->needs_reorg) + reorganize_buffers (dev, dmap, 0); + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + { + printk ("Sound: Can't write to mmapped device (3)\n"); + return -EINVAL; + } + + if (dmap->dma_mode == DMODE_INPUT) /* Direction change */ + { + DMAbuf_reset (dev); + dmap->dma_mode = DMODE_NONE; + } + + dmap->dma_mode = DMODE_OUTPUT; + + save_flags (flags); + cli (); + + while (find_output_space (dev, buf, size) <= 0) + { + if ((err = output_sleep (dev, dontblock)) < 0) + { + restore_flags (flags); + return err; + } + } + + restore_flags (flags); + + return 0; +} + +int +DMAbuf_move_wrpointer (int dev, int l) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + unsigned long ptr = (dmap->user_counter / dmap->fragment_size) + * dmap->fragment_size; + + unsigned long end_ptr, p; + int post = (dmap->flags & DMA_POST); + + ; + + dmap->flags &= ~DMA_POST; + + dmap->cfrag = -1; + + dmap->user_counter += l; + dmap->flags |= DMA_DIRTY; + + if (dmap->user_counter >= dmap->max_byte_counter) + { /* Wrap the byte counters */ + long decr = dmap->user_counter; + + dmap->user_counter = (dmap->user_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->user_counter; + dmap->byte_counter -= decr; + } + + end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; + + p = (dmap->user_counter - 1) % dmap->bytes_in_use; + dmap->neutral_byte = dmap->raw_buf[p]; + + /* Update the fragment based bookkeeping too */ + while (ptr < end_ptr) + { + dmap->counts[dmap->qtail] = dmap->fragment_size; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + dmap->qlen++; + ptr += dmap->fragment_size; + } + + dmap->counts[dmap->qtail] = dmap->user_counter - ptr; + +/* + * Let the low level driver to perform some postprocessing to + * the written data. + */ + if (audio_devs[dev]->d->postprocess_write) + audio_devs[dev]->d->postprocess_write (dev); + + if (!(dmap->flags & DMA_ACTIVE)) + if (dmap->qlen > 1 || + (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1))) + { + DMAbuf_launch_output (dev, dmap); + } + + ; + return 0; +} + +int +DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) +{ + int chan; + struct dma_buffparms *dmap; + + if (dma_mode == DMA_MODE_WRITE) + { + chan = audio_devs[dev]->dmap_out->dma; + dmap = audio_devs[dev]->dmap_out; + } + else + { + chan = audio_devs[dev]->dmap_in->dma; + dmap = audio_devs[dev]->dmap_in; + } + + if (dmap->raw_buf == NULL) + { + printk ("sound: DMA buffer(1) == NULL\n"); + printk ("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in"); + return 0; + } + + if (chan < 0) + return 0; + + sound_start_dma (dev, dmap, chan, physaddr, count, dma_mode, 0); + + return count; +} + +static int +local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) +{ + int chan; + struct dma_buffparms *dmap; + + if (dma_mode == DMA_MODE_WRITE) + { + chan = audio_devs[dev]->dmap_out->dma; + dmap = audio_devs[dev]->dmap_out; + } + else + { + chan = audio_devs[dev]->dmap_in->dma; + dmap = audio_devs[dev]->dmap_in; + } + + if (dmap->raw_buf == NULL) + { + printk ("sound: DMA buffer(2) == NULL\n"); + printk ("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in"); + return 0; + } + + if (dmap->flags & DMA_NODMA) + { + return 1; + } + + if (chan < 0) + return 0; + + sound_start_dma (dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1); + dmap->flags |= DMA_STARTED; + + return count; +} + +static void +finish_output_interrupt (int dev, struct dma_buffparms *dmap) +{ + unsigned long flags; + + if (dmap->audio_callback != NULL) + dmap->audio_callback (dev, dmap->callback_parm); + + save_flags (flags); + cli (); + if ((out_sleep_flag[dev].opts & WK_SLEEP)) + { + { + out_sleep_flag[dev].opts = WK_WAKEUP; + wake_up (&out_sleeper[dev]); + }; + } + restore_flags (flags); +} + +static void +do_outputintr (int dev, int dummy) +{ + unsigned long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + int this_fragment; + +#ifdef OS_DMA_INTR + if (audio_devs[dev]->dmap_out->dma >= 0) + sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma); +#endif + + if (dmap->raw_buf == NULL) + { + printk ("Sound: Fatal error. Audio interrupt (%d) after freeing buffers.\n", dev); + return; + } + + if (dmap->mapping_flags & DMA_MAP_MAPPED) /* Virtual memory mapped access */ + { + /* mmapped access */ + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + if (dmap->qhead == 0) /* Wrapped */ + { + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */ + { + long decr = dmap->byte_counter; + + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + + dmap->qlen++; /* Yes increment it (don't decrement) */ + if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) + dmap->flags &= ~DMA_ACTIVE; + dmap->counts[dmap->qhead] = dmap->fragment_size; + + DMAbuf_launch_output (dev, dmap); + finish_output_interrupt (dev, dmap); + return; + } + + save_flags (flags); + cli (); + + dmap->qlen--; + this_fragment = dmap->qhead; + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + + if (dmap->qhead == 0) /* Wrapped */ + { + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */ + { + long decr = dmap->byte_counter; + + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + + if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) + dmap->flags &= ~DMA_ACTIVE; + + while (dmap->qlen < 0) + { + dmap->underrun_count++; + + dmap->qlen++; + if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS) + { + dmap->flags &= ~DMA_DIRTY; + memset (audio_devs[dev]->dmap_out->raw_buf, + audio_devs[dev]->dmap_out->neutral_byte, + audio_devs[dev]->dmap_out->buffsize); + } + dmap->user_counter += dmap->fragment_size; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + } + + if (dmap->qlen > 0) + DMAbuf_launch_output (dev, dmap); + + restore_flags (flags); + finish_output_interrupt (dev, dmap); +} + +void +DMAbuf_outputintr (int dev, int notify_only) +{ + unsigned long flags; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + save_flags (flags); + cli (); + + if (!(dmap->flags & DMA_NODMA)) + { + int chan = dmap->dma, pos, n; + + clear_dma_ff (chan); + disable_dma (dmap->dma); + pos = dmap->bytes_in_use - get_dma_residue (chan); + enable_dma (dmap->dma); + + pos = pos / dmap->fragment_size; /* Actual qhead */ + if (pos < 0 || pos >= dmap->nbufs) + pos = 0; + + n = 0; + while (dmap->qhead != pos && n++ < dmap->nbufs) + { + do_outputintr (dev, notify_only); + } + } + else + do_outputintr (dev, notify_only); + restore_flags (flags); +} + +static void +do_inputintr (int dev) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + unsigned long flags; + +#ifdef OS_DMA_INTR + if (audio_devs[dev]->dmap_in->dma >= 0) + sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma); +#endif + + if (dmap->raw_buf == NULL) + { + printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n"); + return; + } + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + { + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + if (dmap->qtail == 0) /* Wrapped */ + { + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */ + { + long decr = dmap->byte_counter; + + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + dmap->qlen++; + + if (!(audio_devs[dev]->flags & DMA_AUTOMODE)) + { + if (dmap->needs_reorg) + reorganize_buffers (dev, dmap, 0); + local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use, + DMA_MODE_READ); + audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys + + dmap->qtail * dmap->fragment_size, + dmap->fragment_size, 1); + if (audio_devs[dev]->d->trigger) + audio_devs[dev]->d->trigger (dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } + + dmap->flags |= DMA_ACTIVE; + } + else if (dmap->qlen >= (dmap->nbufs - 1)) + { + printk ("Sound: Recording overrun\n"); + dmap->underrun_count++; + + /* Just throw away the oldest fragment but keep the engine running */ + dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + } + else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) + { + dmap->qlen++; + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + if (dmap->qtail == 0) /* Wrapped */ + { + dmap->byte_counter += dmap->bytes_in_use; + if (dmap->byte_counter >= dmap->max_byte_counter) /* Overflow */ + { + long decr = dmap->byte_counter; + + dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; + decr -= dmap->byte_counter; + dmap->user_counter -= decr; + } + } + } + + if (!(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA) + { + local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use, + DMA_MODE_READ); + audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys + + dmap->qtail * dmap->fragment_size, + dmap->fragment_size, 1); + if (audio_devs[dev]->d->trigger) + audio_devs[dev]->d->trigger (dev, + audio_devs[dev]->enable_bits * audio_devs[dev]->go); + } + + dmap->flags |= DMA_ACTIVE; + + save_flags (flags); + cli (); + if (dmap->qlen > 0) + if ((in_sleep_flag[dev].opts & WK_SLEEP)) + { + { + in_sleep_flag[dev].opts = WK_WAKEUP; + wake_up (&in_sleeper[dev]); + }; + } + restore_flags (flags); +} + +void +DMAbuf_inputintr (int dev) +{ + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + unsigned long flags; + + save_flags (flags); + cli (); + + if (!(dmap->flags & DMA_NODMA)) + { + int chan = dmap->dma, pos, n; + + clear_dma_ff (chan); + disable_dma (dmap->dma); + pos = dmap->bytes_in_use - get_dma_residue (chan); + enable_dma (dmap->dma); + + pos = pos / dmap->fragment_size; /* Actual qhead */ + if (pos < 0 || pos >= dmap->nbufs) + pos = 0; + + n = 0; + while (dmap->qtail != pos && ++n < dmap->nbufs) + { + do_inputintr (dev); + } + } + else + do_inputintr (dev); + restore_flags (flags); +} + +int +DMAbuf_open_dma (int dev) +{ +/* + * NOTE! This routine opens only the primary DMA channel (output). + */ + + int chan = audio_devs[dev]->dmap_out->dma; + int err; + + if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0) + { + return -EBUSY; + } + dma_init_buffers (dev, audio_devs[dev]->dmap_out); + out_sleep_flag[dev].opts = WK_NONE; + audio_devs[dev]->dmap_out->flags |= DMA_ALLOC_DONE; + audio_devs[dev]->dmap_out->fragment_size = audio_devs[dev]->dmap_out->buffsize; + + if (chan >= 0) + { + unsigned long flags; + + save_flags (flags); + cli (); + disable_dma (audio_devs[dev]->dmap_out->dma); + clear_dma_ff (chan); + restore_flags (flags); + } + + return 0; +} + +void +DMAbuf_close_dma (int dev) +{ + close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma); +} + +void +DMAbuf_init (int dev, int dma1, int dma2) +{ + /* + * NOTE! This routine could be called several times. + */ + + if (audio_devs[dev]->dmap_out == NULL) + { + if (audio_devs[dev]->d == NULL) + panic ("OSS: audio_devs[%d]->d == NULL\n", dev); + + if (audio_devs[dev]->parent_dev) + { /* Use DMA map of the parent dev */ + int parent = audio_devs[dev]->parent_dev - 1; + + audio_devs[dev]->dmap_out = audio_devs[parent]->dmap_out; + audio_devs[dev]->dmap_in = audio_devs[parent]->dmap_in; + } + else + { + audio_devs[dev]->dmap_out = + audio_devs[dev]->dmap_in = + &dmaps[ndmaps++]; + audio_devs[dev]->dmap_out->dma = dma1; + + if (audio_devs[dev]->flags & DMA_DUPLEX) + { + audio_devs[dev]->dmap_in = + &dmaps[ndmaps++]; + audio_devs[dev]->dmap_in->dma = dma2; + } + } + } +} + +int +DMAbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait) +{ + struct dma_buffparms *dmap; + unsigned long flags; + + switch (sel_type) + { + case SEL_IN: + if (!(audio_devs[dev]->open_mode & OPEN_READ)) + return 0; + + dmap = audio_devs[dev]->dmap_in; + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + { + if (dmap->qlen) + return 1; + + save_flags (flags); + cli (); + + in_sleep_flag[dev].opts = WK_SLEEP; + poll_wait (&in_sleeper[dev], wait); + restore_flags (flags); + return 0; + } + + if (dmap->dma_mode != DMODE_INPUT) + { + if (dmap->dma_mode == DMODE_NONE && + audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT && + !dmap->qlen && + audio_devs[dev]->go) + { + unsigned long flags; + + save_flags (flags); + cli (); + DMAbuf_activate_recording (dev, dmap); + restore_flags (flags); + } + return 0; + } + + if (!dmap->qlen) + { + save_flags (flags); + cli (); + + in_sleep_flag[dev].opts = WK_SLEEP; + poll_wait (&in_sleeper[dev], wait); + restore_flags (flags); + return 0; + } + return 1; + break; + + case SEL_OUT: + dmap = audio_devs[dev]->dmap_out; + + if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) + return 0; + + if (dmap->mapping_flags & DMA_MAP_MAPPED) + { + if (dmap->qlen) + return 1; + + save_flags (flags); + cli (); + + out_sleep_flag[dev].opts = WK_SLEEP; + poll_wait (&out_sleeper[dev], wait); + restore_flags (flags); + return 0; + } + + if (dmap->dma_mode == DMODE_INPUT) + { + return 0; + } + + if (dmap->dma_mode == DMODE_NONE) + { + return 1; + } + + if (!DMAbuf_space_in_queue (dev)) + { + save_flags (flags); + cli (); + + out_sleep_flag[dev].opts = WK_SLEEP; + poll_wait (&out_sleeper[dev], wait); + restore_flags (flags); + return 0; + } + return 1; + break; + + case SEL_EX: + return 0; + } + + return 0; +} + + +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/dmasound.c linux/drivers/sound/dmasound.c --- v2.1.62/linux/drivers/sound/dmasound.c Wed Sep 24 20:05:47 1997 +++ linux/drivers/sound/dmasound.c Tue Sep 30 08:46:59 1997 @@ -628,7 +628,7 @@ static struct sound_queue sq; #define sq_block_address(i) (sq.buffers[i]) -#define SIGNAL_RECEIVED (signal_pending(current)) +#define SIGNAL_RECEIVED (current->signal & ~current->blocked) #define NON_BLOCKING(open_mode) (open_mode & O_NONBLOCK) #define ONE_SECOND HZ /* in jiffies (100ths of a second) */ #define NO_TIME_LIMIT 0xffffffff @@ -668,9 +668,10 @@ static int sound_open(struct inode *inode, struct file *file); -static int sound_fsync(struct file *filp, struct dentry *dentry); +static int sound_fsync(struct inode *inode, struct file *filp); static void sound_release(struct inode *inode, struct file *file); -static long long sound_lseek(struct file *file, long long offset, int orig); +static long long sound_lseek(struct inode *inode, struct file *file, + long long offset, int orig); static long sound_read(struct inode *inode, struct file *file, char *buf, unsigned long count); static long sound_write(struct inode *inode, struct file *file, @@ -3070,9 +3071,9 @@ } -static int sound_fsync(struct file *filp, struct dentry *dentry) +static int sound_fsync(struct inode *inode, struct file *filp) { - int dev = MINOR(dentry->d_inode->i_rdev) & 0x0f; + int dev = MINOR(inode->i_rdev) & 0x0f; switch (dev) { case SND_DEV_STATUS: @@ -3115,7 +3116,8 @@ } -static long long sound_lseek(struct file *file, long long offset, int orig) +static long long sound_lseek(struct inode *inode, struct file *file, + long long offset, int orig) { return -ESPIPE; } @@ -3184,25 +3186,25 @@ return(0); case SNDCTL_DSP_POST: case SNDCTL_DSP_SYNC: - return(sound_fsync(file, file->f_dentry)); + return(sound_fsync(inode, file)); /* ++TeSche: before changing any of these it's probably wise to * wait until sound playing has settled down */ case SNDCTL_DSP_SPEED: - sound_fsync(file, file->f_dentry); + sound_fsync(inode, file); IOCTL_IN(arg, data); return(IOCTL_OUT(arg, sound_set_speed(data))); case SNDCTL_DSP_STEREO: - sound_fsync(file, file->f_dentry); + sound_fsync(inode, file); IOCTL_IN(arg, data); return(IOCTL_OUT(arg, sound_set_stereo(data))); case SOUND_PCM_WRITE_CHANNELS: - sound_fsync(file, file->f_dentry); + sound_fsync(inode, file); IOCTL_IN(arg, data); return(IOCTL_OUT(arg, sound_set_stereo(data-1)+1)); case SNDCTL_DSP_SETFMT: - sound_fsync(file, file->f_dentry); + sound_fsync(inode, file); IOCTL_IN(arg, data); return(IOCTL_OUT(arg, sound_set_format(data))); case SNDCTL_DSP_GETFMTS: diff -u --recursive --new-file v2.1.62/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c --- v2.1.62/linux/drivers/sound/gus_card.c Wed May 28 10:51:32 1997 +++ linux/drivers/sound/gus_card.c Tue Sep 30 08:46:51 1997 @@ -15,13 +15,13 @@ #include "sound_config.h" -#if defined(CONFIG_GUSHW) +#ifdef CONFIG_GUSHW #include "gus_hw.h" void gusintr (int irq, void *dev_id, struct pt_regs *dummy); -int gus_base, gus_irq, gus_dma; +int gus_base = 0, gus_irq = 0, gus_dma = 0; extern int gus_wave_volume; extern int gus_pcm_volume; extern int have_gus_max; @@ -171,7 +171,7 @@ /* * Some extra code for the 16 bit sampling option */ -#if defined(CONFIG_GUS16) +#ifdef CONFIG_GUS16 int probe_gus_db16 (struct address_info *hw_config) diff -u --recursive --new-file v2.1.62/linux/drivers/sound/gus_midi.c linux/drivers/sound/gus_midi.c --- v2.1.62/linux/drivers/sound/gus_midi.c Wed May 28 10:51:32 1997 +++ linux/drivers/sound/gus_midi.c Tue Sep 30 08:46:51 1997 @@ -17,7 +17,8 @@ #include "gus_hw.h" -#if defined(CONFIG_GUSHW) && defined(CONFIG_MIDI) +#ifdef CONFIG_GUSHW +#ifdef CONFIG_MIDI static int midi_busy = 0, input_opened = 0; static int my_dev; @@ -295,4 +296,5 @@ restore_flags (flags); } +#endif #endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c --- v2.1.62/linux/drivers/sound/gus_wave.c Wed May 28 10:51:32 1997 +++ linux/drivers/sound/gus_wave.c Tue Sep 30 08:46:52 1997 @@ -19,7 +19,7 @@ #include #include "gus_hw.h" -#if defined(CONFIG_GUSHW) +#ifdef CONFIG_GUSHW #define GUS_BANK_SIZE (((iw_mode) ? 256*1024*1024 : 256*1024)) @@ -73,7 +73,6 @@ static int gus_dma2 = -1; static int dual_dma_mode = 0; static long gus_mem_size = 0; -static long gus_rom_size = 0; static long free_mem_ptr = 0; static int gus_busy = 0; static int gus_no_dma = 0; @@ -681,6 +680,7 @@ if (voices[voice].mode & WAVE_ENVELOPES) { start_release (voice, flags); + restore_flags (flags); return; } @@ -692,6 +692,7 @@ gus_voice_off (); gus_rampoff (); gus_voice_init (voice); + restore_flags (flags); return; } @@ -1026,9 +1027,7 @@ for (bank = 0; bank < 4; bank++) { - DDB (printk (" Bank %d, mem=%dk (limit %dk)\n", - bank, bank_sizes[bank] / 1024, - mem_decode[bits][bank] / 1024)); + DDB (printk (" Bank %d, mem=%dk (limit %dk)\n", bank, bank_sizes[bank] / 1024, mem_decode[bits][bank] / 1024)); if (bank_sizes[bank] > mem_decode[bits][bank]) total += mem_decode[bits][bank]; @@ -1180,8 +1179,7 @@ if (sample_ptrs[sample_no] == -1) /* Sample not loaded */ { - printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", - sample_no, instr_no, voice); + printk ("GUS: Sample #%d not loaded for patch %d (voice %d)\n", sample_no, instr_no, voice); return -EINVAL; } @@ -1724,7 +1722,7 @@ if ((err = DMAbuf_open_dma (gus_devnum)) < 0) { - /* printk ("GUS: Loading samples without DMA\n"); */ + /* printk( "GUS: Loading samples without DMA\n"); */ gus_no_dma = 1; /* Upload samples using PIO */ } else @@ -1808,8 +1806,7 @@ if (count < patch.len) { - printk ("GUS Warning: Patch record too short (%d<%d)\n", - count, (int) patch.len); + printk ("GUS Warning: Patch record too short (%d<%d)\n", count, (int) patch.len); patch.len = count; } @@ -2335,7 +2332,7 @@ if (gus_pnp_flag && mode & OPEN_READ) { - printk ("Sound: This audio device doesn't have recording capability\n"); + printk ("GUS: Audio device #%d is playback only.\n", dev); return -EIO; } gus_initialize (); @@ -3473,6 +3470,7 @@ default:; } + restore_flags (flags); } void diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/ChangeLog.awe linux/drivers/sound/lowlevel/ChangeLog.awe --- v2.1.62/linux/drivers/sound/lowlevel/ChangeLog.awe Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/lowlevel/ChangeLog.awe Tue Oct 28 02:10:48 1997 @@ -1,3 +1,69 @@ +ver.0.4.2 + - Use indirect voice allocation mode; used as default mode + - Add preset mapping + - Free buffers when resetting samples + - Set default preset/bank/drumset as variable + - Fix a bug in exclusive note-off + - Add channel reset control macro + - Change modwheel sensitivity as variable + - Add lock option in open_patch + - Add channel priority mode macro, and disable it as default + - Add unset effect macro + - Add user defined chorus/reverb modes + +ver.0.4.1 development versions + +ver.0.4.0c + - Fix kernel oops when setting AWE_FX_ATTEN + +ver.0.4.0b + - Do not kill_note in start_note when velocity is zero + +ver.0.4.0a + - Fix a bug in channel pressure effects + +ver.0.4.0 + - Support dynamic buffer allocation + - Add functions to open/close/unload a patch + - Change from pointer to integer index in voice/sample lists + - Support for Linux/Alpha-AXP + - Fix for FreeBSD + - Add sostenuto control + - Add midi channel priority + - Fix a bug in all notes off control + - Use AWE_DEFAULT_MEMSIZE always if defined + - Fix a bug in awe_reset causes seg fault when no DRAM onboard + - Use awe_mem_start variable instead of constant + +ver.0.3.3c + - Fix IOCTL_TO_USER for OSS-3.8 (on Linux-2.1.25) + - Fix i/o macros for mixer controls + +ver.0.3.3b + - Fix version number in awe_version.h + - Fix a small bug in noteoff/relese all + +ver.0.3.3a + - Fix all notes/sounds off + - Add layer effect control + - Add misc mode controls; realtime pan, version number, etc. + - Move gus bank control in misc mode control + - Modify awe_operations for OSS3.8b5 + - Fix installation script + +ver.0.3.3 + - Add bass/treble control in Emu8000 chip + - Add mixer device + - Fix sustain on to value 127 + +ver.0.3.2 + - Refuse linux-2.0.0 at installation + - Move awe_voice.h to /usr/include/linux + +ver.0.3.1b (not released) + - Rewrite chorus/reverb mode change functions + - Rewrite awe_detect & awe_check_dram routines + ver.0.3.1a - Fix a bug to reset voice counter in awe_reset - Fix voice balance on GUS mode diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/Makefile linux/drivers/sound/lowlevel/Makefile --- v2.1.62/linux/drivers/sound/lowlevel/Makefile Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/lowlevel/Makefile Tue Oct 28 02:10:48 1997 @@ -15,6 +15,13 @@ endif endif +ifndef TOPDIR +TOPDIR=/usr/src/linux +endif + +.c.o: + $(CC) $(CFLAGS) -c $< + lowlevel.o: $(OBJS) $(LD) -r -o lowlevel.o $(OBJS) @@ -35,10 +42,16 @@ dep: $(CPP) -M $(CFLAGS) -I. *.c > .depend -ifdef HOSTCC -include $(TOPDIR)/Rules.make -else +ifndef HOSTCC +# +# Running outside the kernel build. +# +CC = gcc +HOSTCC = gcc +CFLAGS = -O2 -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe -m486 USE_DEPEND=y +else +include $(TOPDIR)/Rules.make endif ifdef USE_DEPEND diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/README.awe linux/drivers/sound/lowlevel/README.awe --- v2.1.62/linux/drivers/sound/lowlevel/README.awe Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/lowlevel/README.awe Tue Oct 28 02:10:49 1997 @@ -1,6 +1,6 @@ ================================================================ AWE32 Sound Driver for Linux / FreeBSD - version 0.3.1; Jan. 11, 1997 + version 0.4.2; Sep. 1, 1997 ================================================================ * GENERAL NOTES @@ -8,9 +8,9 @@ This is a sound driver extension for SoundBlaster AWE32 and other compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable the wave synth operations. The driver is provided for both Linux -1.2.x and 2.[01].x kernels, and also FreeBSD. Consult the -installation document for installation on the original sound driver -package. +1.2.x and 2.[01].x kernels, and also FreeBSD on Intel x86 and DEC +Alpha systems. See INSTALL.awe (or INSTALL.fbsd) document for +installation of the driver package. This driver was written by Takashi Iwai (iwai@dragon.mm.t.u-tokyo.ac.jp) who also maintains the code. Please forward any questions, bug fixes @@ -18,47 +18,80 @@ Savolainen). -* CAUTION - -- On ver.0.3.1, some zero size array entries are removed from -awe_voice.h to avoid compile error in some non-ANSI compilers. -Due to this fix, the size of awe_voice_rec structure is changed from -older versions. Use a constant AWE_VOICE_REC_SIZE instead of -sizeof(awe_voice_rec). -You can still have a compatibility by defining AWE_COMPAT_030=1, but -this feature will be omitted in the future release. - - * NOTE TO LINUX USERS To enable this driver on linux-2.[01].x kernels, you need turn on both "lowlevel drivers support" and "AWE32 synth support" options in sound menu when configure your linux kernel and modules. For more details, see the installation document in the original driver package -(awedrv-0.x.x.tar.gz) available at the web page: +(awedrv-0.4.2.tar.gz) available at the web page: http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/ If you're using PnP cards, the card must be initialized before loading the sound driver. There're several options to do this: - Initialize the card via ISA PnP tools, and load the sound module. - Initialize the card on DOS, and load linux by loadlin.exe - - Use PnP driver (for Linux-2.0.x) + - Use PnP driver (for Linux-2.x.x) See the FAQ list on the URL above. +* USING THE DRIVER + +The GM and GS sounds include multiple instrument layers. +The current version supports this type of sounds with a special +extension, but it uses a non-standard way of sequencer calls. Then, +so far, only drvmidi and playmidi can play the multiple instruments +and stereo sounds properly. + +To load SoundFont files, sfxload utility is required. +All AWE32 driver and utilities can be downloaded from: + http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/ + +The sfxload is included in the package awesfx-0.4.2.tgz. Binary +packages are available there, too. See the instruction in each +package for installation. + +Sfxload reads a SoundFont file and transfers it to the sound driver. +Note that new sfxload no longer requires -i option. + + % sfxload synthgm.sbk + +You can tune up the sound via some new options, -A, -a and -d. + + % sfxload -A2 synthgm.sbk + +See the manual of sfxload for more details. + +Now you can hear midi musics by supported midi players (drvmidi or +playmidi-2.5). + + % drvmidi foo.mid + +If you have only 512kb on the sound card, I recommend to use dynamic +sample loading via -L option of drvmidi. 2MB GM/GS soundfont file is +available in most midi files. + + % sfxload synthgm + % drvmidi -L 2mbgmgs foo.mid + +Enjoy. + + * COMPILE FLAGS Compile conditions are defined in awe_config.h. +[Compatibility Conditions] +The following flags are defined automatically when using installation +shell script. + - AWE_OBSOLETE_VOXWARE (default: not defined) indicates the system is VoxWare-3.0.x (with linux 1.2.x or - FreeBSD) if defined. This option will be set automatically when - you use installation script. + FreeBSD) if defined. - AWE_NEW_KERNEL_INTERFACE (default: not defined) indicates the system is OSSLite on Linux 2.1.6 or later if - defined. This option will be set automatically when you use - installation script. + defined. - HAS_LOWLEVEL_H (default: not defined) indicates the system has "lowlevel.h" in the sound/lowlevel @@ -68,27 +101,32 @@ indicates the sound driver has no patch manager function (for OSS-3.707 (in Linux-2.1.13) or newer). +- AWE_OSS38 (default: not defined) + indicates the sound driver has an additional parameter in + operation table (for OSS-3.8b5 in Linux-2.1.25 or newer). + + +[Hardware Conditions] +You don't have to define the following two values. +Define them only when the driver couldn't detect the card properly. + - AWE_DEFAULT_BASE_ADDR (default: not defined) specifies the base port address of your AWE32 card. - Define this only when the driver couldn't detect your card - properly. - AWE_DEFAULT_MEM_SIZE (default: not defined) - specifies the memory size of your AWE32 card by kilo bytes. - Define this only when the driver couldn't detect memory size - properly. + specifies the memory size of your AWE32 card in kilo bytes. -- AWE_MAX_SAMPLES (default: 400) - specifies the maximum number of wave samples. - The default size is set for the original GM and GS presets from - CreativeLab. If you have a large set of samples (eg 2MB & 8MB GM - presets), increase this value to appropriate size. - -- AWE_MAX_INFOS (default: 900) - specifies the maximum number of instruments. - The default size is set for the original GM and GS presets from - CreativeLab. If you have a large set of samples (eg 2MB & 8MB GM - presets), increase this value to appropriate size. + +[Sample Table Size] +From ver.0.4.0, sample tables are allocated dynamically (except +Linux-1.2.x system), so you need NOT to touch these parameters. +Linux-1.2.x users may need to increase these values to apropriate size +if larger DRAM is equipped with the soundcard. + +- AWE_MAX_SF_LISTS, AWE_MAX_SAMPLES, AWE_MAX_INFOS + + +[Other Conditions] - AWE_ALWAYS_INIT_FM (default: not defined) indicates the AWE driver always initialize FM passthrough even @@ -99,22 +137,24 @@ - AWE_DEBUG_ON (default: defined) turns on debuggin messages if defined. -- AWE_CHECKSUM_DATA (default: defined) - verifies check sum of sample data with the transferred data if - defined. - -- AWE_CHECKSUM_MEMORY (default: defined) - Verifies check sum of sample data with the written data on DRAM. - - AWE_HAS_GUS_COMPATIBILITY (default: defined) Enables GUS compatibility mode if defined, reading GUS patches and GUS control commands. Define this option to use GMOD or other GUS module players. -- AWE_ACCEPT_ALL_SOUNDS_CONTROL (default: not defined) +- AWE_ACCEPT_ALL_SOUNDS_CONTROL (default: defined) Enables MIDI control #120 and #123 as "all notes off" and "all sounds off" events, respectively. +- CONFIG_AWE32_MIXER (default: defined) + Adds a mixer device for AWE32 bass/treble equalizer control. + You can access this device using /dev/mixer?? (usually mixer01). + +- AWE_LOOKUP_MIDI_PRIORIITY (default: defined) + Allocates voices according to MIDI channel priority. + Drum channels have the highest priorit, followed by #1, #2, and + so on. + - DEF_FM_CHORUS_DEPTH (default: 0x10) The default strength to be sent to the chorus effect engine. From 0 to 0xff. Larger numbers may often cause weird sounds. @@ -124,43 +164,12 @@ From 0 to 0xff. Larger numbers may often cause weird sounds. -* USING THE DRIVER - -To load SoundFont files, sfxload utility is required. -All AWE32 driver and utilities can be downloaded from: - http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/ - -The GM and GS sounds include multiple instrument layers. The older -driver couldn't handle multiple instruments. The current version -supports this type of sounds with a special extension, but so far only -drvmidi and playmidi can play the multiple instruments and stereo -sounds. - -To play drvmidi, load the SoundFont file directly uing sfxload utility. - - % sfxload -i synthgm.sf2 - -To use other sequencers like musserver, some sounds may become -inaudible unless converting to SFX file. Follow the instruction in -awesfx package to make patched GM and GS presets. Then, load the SFX -file on driver by sfxload utility. - - % sfxload -i gm.sfx - -Now you can hear midi musics by supported midi players -(awemidi-0.1.x.tar.gz or patch file to playmidi-2.3). - - % drvmidi foo.mid - -Enjoy. - - * ACKNOWLEDGMENTS Thanks to Witold Jachimczyk (witek@xfactor.wpi.edu) for many advices to programming of AWE32. Many codes are brought from his AWE32-native MOD player, ALMP. -The port of awedrv0.1.6 to FreeBSD is done by Randall Hopper +The port of awedrv to FreeBSD is done by Randall Hopper (rhh@ct.picker.com). I also thank linux-awe-ml members for their efforts to reboot their system many times :-) @@ -168,9 +177,9 @@ * BUGS & TODO'S +- Can't detect DRAM size on some card - More smart patch management - More smart DRAM memory control -- Dynamic buffer allocation - etc, etc, etc. diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/aedsp16.c linux/drivers/sound/lowlevel/aedsp16.c --- v2.1.62/linux/drivers/sound/lowlevel/aedsp16.c Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/lowlevel/aedsp16.c Tue Oct 28 02:10:50 1997 @@ -250,7 +250,7 @@ #undef AEDSP16_INFO 1 /* Define this to enable info code */ #if defined(AEDSP16_DEBUG) -# define DBG(x) printk x +# define DBG(x) printk x # if defined(AEDSP16_DEBUG_MORE) # define DBG1(x) printk x # else diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/awe_compat.h linux/drivers/sound/lowlevel/awe_compat.h --- v2.1.62/linux/drivers/sound/lowlevel/awe_compat.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/lowlevel/awe_compat.h Tue Oct 28 02:10:50 1997 @@ -0,0 +1,190 @@ +/*---------------------------------------------------------------- + * compatibility macros for AWE32 driver + *----------------------------------------------------------------*/ + +/* redefine following macros */ +#undef IOCTL_IN +#undef IOCTL_OUT +#undef OUTW +#undef COPY_FROM_USER +#undef COPY_TO_USER +#undef GET_BYTE_FROM_USER +#undef GET_SHORT_FROM_USER +#undef IOCTL_TO_USER + +#ifdef linux + +/*================================================================ + * Linux macros + *================================================================*/ + +/* use inline prefix */ +#define INLINE inline + +/*---------------------------------------------------------------- + * memory management for linux + *----------------------------------------------------------------*/ + +#ifdef AWE_OBSOLETE_VOXWARE +/* old type linux system */ + +/* i/o requests; nothing */ +#define awe_check_port() 0 /* always false */ +#define awe_request_region() /* nothing */ +#define awe_release_region() /* nothing */ + +static int _mem_start; /* memory pointer for permanent buffers */ + +#define my_malloc_init(memptr) _mem_start = (memptr) +#define my_malloc_memptr() _mem_start +#define my_free(ptr) /* do nothing */ +#define my_realloc(buf,oldsize,size) NULL /* no realloc */ + +static void *my_malloc(int size) +{ + char *ptr; + PERMANENT_MALLOC(ptr, char*, size, _mem_start); + return (void*)ptr; +} + +/* allocate buffer only once */ +#define INIT_TABLE(buffer,index,nums,type) {\ +buffer = my_malloc(sizeof(type) * (nums)); index = (nums);\ +} + +#else + +#define AWE_DYNAMIC_BUFFER + +#define my_malloc_init(ptr) /* nothing */ +#define my_malloc_memptr() 0 +#define my_malloc(size) vmalloc(size) +#define my_free(ptr) if (ptr) {vfree(ptr);} + +static void *my_realloc(void *buf, int oldsize, int size) +{ + void *ptr; + if ((ptr = vmalloc(size)) == NULL) + return NULL; + memcpy(ptr, buf, ((oldsize < size) ? oldsize : size) ); + vfree(buf); + return ptr; +} + +/* do not allocate buffer at beginning */ +#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;} + +/* old type macro */ +#define RET_ERROR(err) -err + +#endif + +/*---------------------------------------------------------------- + * i/o interfaces for linux + *----------------------------------------------------------------*/ + +#define OUTW(data,addr) outw(data, addr) + +#ifdef AWE_NEW_KERNEL_INTERFACE +#define COPY_FROM_USER(target,source,offs,count) \ + copy_from_user(target, (source)+(offs), count) +#define GET_BYTE_FROM_USER(target,addr,offs) \ + get_user(target, (unsigned char*)&((addr)[offs])) +#define GET_SHORT_FROM_USER(target,addr,offs) \ + get_user(target, (unsigned short*)&((addr)[offs])) +#ifdef AWE_OSS38 +#define IOCTL_TO_USER(target,offs,source,count) \ + memcpy(target, (source)+(offs), count) +#define IO_WRITE_CHECK(cmd) (_SIOC_DIR(cmd) & _IOC_WRITE) +#else +#define IOCTL_TO_USER(target,offs,source,count) \ + copy_to_user(target, (source)+(offs), count) +#define IO_WRITE_CHECK(cmd) (_IOC_DIR(cmd) & _IOC_WRITE) +#endif /* AWE_OSS38 */ +#define COPY_TO_USER IOCTL_TO_USER +#define IOCTL_IN(arg) (*(int*)(arg)) +#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val)) + +#else /* old type i/o */ +#define COPY_FROM_USER(target,source,offs,count) \ + memcpy_fromfs(target, (source)+(offs), (count)) +#define GET_BYTE_FROM_USER(target,addr,offs) \ + *((char *)&(target)) = get_fs_byte((addr)+(offs)) +#define GET_SHORT_FROM_USER(target,addr,offs) \ + *((short *)&(target)) = get_fs_word((addr)+(offs)) +#define IOCTL_TO_USER(target,offs,source,count) \ + memcpy_tofs(target, (source)+(offs), (count)) +#define COPY_TO_USER IOCTL_TO_USER +#define IO_WRITE_CHECK(cmd) (cmd & IOC_IN) +#define IOCTL_IN(arg) get_fs_long((long *)(arg)) +#define IOCTL_OUT(arg,ret) snd_ioctl_return((int *)arg, ret) + +#endif /* AWE_NEW_KERNEL_INTERFACE */ + +#define BZERO(target,len) memset(target, 0, len) +#define MEMCPY(dst,src,len) memcpy(dst, src, len) + + +#elif defined(__FreeBSD__) + +/*================================================================ + * FreeBSD macros + *================================================================*/ + +/* inline is not checked yet.. maybe it'll work */ +#define INLINE /*inline*/ + +/*---------------------------------------------------------------- + * memory management for freebsd + *----------------------------------------------------------------*/ + +/* i/o requests; nothing */ +#define awe_check_port() 0 /* always false */ +#define awe_request_region() /* nothing */ +#define awe_release_region() /* nothing */ + +#define AWE_DYNAMIC_BUFFER + +#define my_malloc_init(ptr) /* nothing */ +#define my_malloc_memptr() 0 +#define my_malloc(size) malloc(size, M_TEMP, M_WAITOK) +#define my_free(ptr) if (ptr) {free(ptr, M_TEMP);} + +#define INIT_TABLE(buffer,index,nums,type) {buffer=NULL; index=0;} + +/* it should be realloc? */ +static void *my_realloc(void *buf, int oldsize, int size) +{ + void *ptr; + if ((ptr = my_malloc(size)) == NULL) + return NULL; + memcpy(ptr, buf, ((oldsize < size) ? oldsize : size) ); + my_free(buf); + return ptr; +} + +/*---------------------------------------------------------------- + * i/o interfaces for freebsd + *----------------------------------------------------------------*/ + +/* according to linux rule; the arguments are swapped */ +#define OUTW(data,addr) outw(addr, data) + +#define COPY_FROM_USER(target,source,offs,count) \ + uiomove(((caddr_t)(target)),(count),((struct uio *)(source))) +#define COPY_TO_USER(target,source,offs,count) \ + uiomove(((caddr_t)(source)),(count),((struct uio *)(target))) +#define GET_BYTE_FROM_USER(target,addr,offs) \ + uiomove(((char*)&(target)), 1, ((struct uio *)(addr))) +#define GET_SHORT_FROM_USER(target,addr,offs) \ + uiomove(((char*)&(target)), 2, ((struct uio *)(addr))) +#define IOCTL_TO_USER(target,offs,source,count) \ + memcpy(&((target)[offs]), (source), (count)) +#define IO_WRITE_CHECK(cmd) (cmd & IOC_IN) +#define IOCTL_IN(arg) (*(int*)(arg)) +#define IOCTL_OUT(arg,val) (*(int*)(arg) = (val)) +#define BZERO(target,len) bzero((caddr_t)target, len) +#define MEMCPY(dst,src,len) bcopy((caddr_t)src, (caddr_t)dst, len) + +#endif + diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/awe_config.h linux/drivers/sound/lowlevel/awe_config.h --- v2.1.62/linux/drivers/sound/lowlevel/awe_config.h Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/lowlevel/awe_config.h Tue Oct 28 02:10:50 1997 @@ -2,9 +2,9 @@ * sound/awe_config.h * * Configuration of AWE32 sound driver - * version 0.2.99e; Dec. 10, 1997 + * version 0.4.2; Sep. 1, 1997 * - * Copyright (C) 1996,1997 Takashi Iwai + * Copyright (C) 1996 Takashi Iwai * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,25 +33,20 @@ */ #undef AWE_OBSOLETE_VOXWARE -#ifdef __FreeBSD__ -# define AWE_OBSOLETE_VOXWARE -#endif - -/* if you're using OSS-Lite on Linux 2.1.6 or later, define the - * following line. - */ -#define AWE_NEW_KERNEL_INTERFACE - /* if you have lowlevel.h in the lowlevel directory (OSS-Lite), define * the following line. */ -#define HAS_LOWLEVEL_H +#undef HAS_LOWLEVEL_H /* if your system doesn't support patch manager (OSS 3.7 or newer), * define the following line. */ #define AWE_NO_PATCHMGR +/* if your system has an additional parameter (OSS 3.8b5 or newer), + * define this. + */ +#define AWE_OSS38 /*---------------------------------------------------------------- * AWE32 card configuration: @@ -64,13 +59,20 @@ /*---------------------------------------------------------------- - * maximum size of sample table: - * the followings are for ROM GM and 512k GS samples. if your have - * additional DRAM and SoundFonts, increase these values. + * maximum size of soundfont list table: + * you usually don't need to touch this value. + *----------------------------------------------------------------*/ + +#define AWE_MAX_SF_LISTS 16 + + +/*---------------------------------------------------------------- + * chunk size of sample and voice tables: + * you usually don't need to touch these values. *----------------------------------------------------------------*/ #define AWE_MAX_SAMPLES 400 -#define AWE_MAX_INFOS 1500 +#define AWE_MAX_INFOS 800 /*---------------------------------------------------------------- @@ -87,29 +89,22 @@ *----------------------------------------------------------------*/ /* initialize FM passthrough even without extended RAM */ -#undef AWE_ALWAYS_INIT_FM +#define AWE_ALWAYS_INIT_FM /* debug on */ #define AWE_DEBUG_ON -/* verify checksum for uploading samples */ -#define AWE_CHECKSUM_DATA -#define AWE_CHECKSUM_MEMORY - /* GUS compatible mode */ #define AWE_HAS_GUS_COMPATIBILITY /* accept all notes/sounds off controls */ -#undef AWE_ACCEPT_ALL_SOUNDS_CONTROL - +#define AWE_ACCEPT_ALL_SOUNDS_CONTROL -#ifdef linux -/* i tested this only on my linux */ -#define INLINE __inline__ -#else -#define INLINE /**/ -#endif +/* add mixer control of emu8000 equalizer */ +#define CONFIG_AWE32_MIXER +/* look up voices according to MIDI channel priority */ +#define AWE_LOOKUP_MIDI_PRIORITY /*----------------------------------------------------------------*/ diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/awe_hw.h linux/drivers/sound/lowlevel/awe_hw.h --- v2.1.62/linux/drivers/sound/lowlevel/awe_hw.h Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/lowlevel/awe_hw.h Tue Oct 28 02:10:50 1997 @@ -3,7 +3,7 @@ * * Access routines and definitions for the low level driver for the * AWE32/Sound Blaster 32 wave table synth. - * version 0.3.1; Jan. 21, 1997 + * version 0.4.2; Sep. 1, 1997 * * Copyright (C) 1996,1997 Takashi Iwai * @@ -89,10 +89,12 @@ #define AWE_NORMAL_VOICES 30 /*30&31 are reserved for DRAM refresh*/ #define AWE_MAX_CHANNELS 32 /* max midi channels (must >= voices) */ +#define AWE_MAX_LAYERS AWE_MAX_VOICES /* maximum number of multiple layers */ #define AWE_DRAM_OFFSET 0x200000 #define AWE_MAX_DRAM_SIZE (28 * 1024) /* 28 MB is max onboard memory */ #define AWE_DEFAULT_ATTENUATION 32 /* 12dB below */ +#define AWE_DEFAULT_MOD_SENSE 18 #endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/awe_version.h linux/drivers/sound/lowlevel/awe_version.h --- v2.1.62/linux/drivers/sound/lowlevel/awe_version.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/lowlevel/awe_version.h Tue Oct 28 02:10:50 1997 @@ -0,0 +1,12 @@ +/* AWE32 driver version number */ + +#ifndef AWE_VERSION_H_DEF +#define AWE_VERSION_H_DEF + +#define AWE_VERSION_NUMBER 0x00040200 +#define AWEDRV_VERSION "0.4.2" +#define AWE_MAJOR_VERSION(id) (((id) >> 16) & 0xff) +#define AWE_MINOR_VERSION(id) (((id) >> 8) & 0xff) +#define AWE_TINY_VERSION(id) ((id) & 0xff) + +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/lowlevel/awe_wave.c linux/drivers/sound/lowlevel/awe_wave.c --- v2.1.62/linux/drivers/sound/lowlevel/awe_wave.c Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/lowlevel/awe_wave.c Tue Oct 28 02:10:53 1997 @@ -2,7 +2,7 @@ * sound/awe_wave.c * * The low level driver for the AWE32/Sound Blaster 32 wave table synth. - * version 0.3.1b; Jan. 21, 1997 + * version 0.4.2; Sep. 1, 1997 * * Copyright (C) 1996,1997 Takashi Iwai * @@ -21,7 +21,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define AWEDRV_VERSION "0.3.1b" #ifdef __FreeBSD__ # include #else @@ -34,12 +33,16 @@ #ifdef __FreeBSD__ # include +# include # include #else # include "awe_hw.h" +# include "awe_version.h" # include #endif +#ifdef AWE_HAS_GUS_COMPATIBILITY +/* include finetune table */ #ifdef AWE_OBSOLETE_VOXWARE # ifdef __FreeBSD__ # define SEQUENCER_C @@ -57,6 +60,8 @@ # include #endif +#endif /* AWE_HAS_GUS_COMPATIBILITY */ + /*---------------------------------------------------------------- * debug message @@ -77,33 +82,71 @@ * bank and voice record *----------------------------------------------------------------*/ +/* soundfont record */ +typedef struct _sf_list { + unsigned short sf_id; + unsigned short type; + int num_info; /* current info table index */ + int num_sample; /* current sample table index */ + int mem_ptr; /* current word byte pointer */ + int infos; + int samples; + /*char name[AWE_PATCH_NAME_LEN];*/ +} sf_list; + /* bank record */ typedef struct _awe_voice_list { + int next; /* index list */ unsigned char bank, instr; + char type, disabled; awe_voice_info v; - struct _awe_voice_list *next_instr; - struct _awe_voice_list *next_bank; + int next_instr; /* preset table list */ + int next_bank; /* preset table list */ } awe_voice_list; +#define V_ST_NORMAL 0 +#define V_ST_MAPPED 1 + +typedef struct _awe_sample_list { + int next; /* sf list */ + awe_sample_info v; +} awe_sample_list; + /* sample and information table */ -static awe_sample_info *samples; -static awe_voice_list *infos; +static int current_sf_id = 0; +static int locked_sf_id = 0; +static int max_sfs; +static sf_list *sflists = NULL; + +#define awe_free_mem_ptr() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].mem_ptr) +#define awe_free_info() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_info) +#define awe_free_sample() (current_sf_id <= 0 ? 0 : sflists[current_sf_id-1].num_sample) + +static int max_samples; +static awe_sample_list *samples = NULL; + +static int max_infos; +static awe_voice_list *infos = NULL; + #define AWE_MAX_PRESETS 256 +#define AWE_DEFAULT_PRESET 0 #define AWE_DEFAULT_BANK 0 +#define AWE_DEFAULT_DRUM 0 #define AWE_DRUM_BANK 128 +#define MAX_LAYERS AWE_MAX_VOICES + /* preset table index */ -static awe_voice_list *preset_table[AWE_MAX_PRESETS]; +static int preset_table[AWE_MAX_PRESETS]; /*---------------------------------------------------------------- * voice table *----------------------------------------------------------------*/ /* effects table */ -#define AWE_FX_NBYTES ((AWE_FX_END+7)/8) typedef struct FX_Rec { /* channel effects */ - unsigned char flags[AWE_FX_NBYTES]; + unsigned char flags[AWE_FX_END]; short val[AWE_FX_END]; } FX_Rec; @@ -117,28 +160,35 @@ int panning; /* panning (0-127) */ int main_vol; /* channel volume (0-127) */ int expression_vol; /* midi expression (0-127) */ - awe_voice_list *vrec; /* instrument list */ - awe_voice_list *def_vrec; /* default instrument list */ + int chan_press; /* channel pressure */ + int vrec; /* instrument list */ + int def_vrec; /* default instrument list */ FX_Rec fx; /* effects */ + FX_Rec fx_layer[MAX_LAYERS]; /* layer effects */ int sustained; /* sustain status in MIDI */ } awe_chan_info; /* voice parameters */ typedef struct _voice_info { int state; -#define AWE_ST_OFF 0 /* no sound */ -#define AWE_ST_ON 1 /* playing */ -#define AWE_ST_STANDBY 2 /* stand by for playing */ -#define AWE_ST_SUSTAINED 3 /* sustained */ -#define AWE_ST_MARK 4 /* marked for allocation */ +#define AWE_ST_OFF (1<<0) /* no sound */ +#define AWE_ST_ON (1<<1) /* playing */ +#define AWE_ST_STANDBY (1<<2) /* stand by for playing */ +#define AWE_ST_SUSTAINED (1<<3) /* sustained */ +#define AWE_ST_MARK (1<<4) /* marked for allocation */ +#define AWE_ST_DRAM (1<<5) /* DRAM read/write */ +#define AWE_ST_FM (1<<6) /* reserved for FM */ +#define AWE_ST_RELEASED (1<<7) /* released */ int ch; /* midi channel */ int key; /* internal key for search */ + int layer; /* layer number (for channel mode only) */ int time; /* allocated time */ awe_chan_info *cinfo; /* channel info */ int note; /* midi key (0-127) */ int velocity; /* midi velocity (0-127) */ + int sostenuto; /* sostenuto on/off */ awe_voice_info *sample; /* assigned voice */ /* EMU8000 parameters */ @@ -148,58 +198,80 @@ } voice_info; /* voice information */ -static voice_info voices[AWE_MAX_VOICES]; +static voice_info *voices; -#define IS_NO_SOUND(v) (voices[v].state == AWE_ST_OFF || voices[v].state == AWE_ST_STANDBY) +#define IS_NO_SOUND(v) (voices[v].state & (AWE_ST_OFF|AWE_ST_RELEASED|AWE_ST_STANDBY|AWE_ST_SUSTAINED)) #define IS_NO_EFFECT(v) (voices[v].state != AWE_ST_ON) -#define IS_PLAYING(v) (!IS_NO_SOUND(v)) +#define IS_PLAYING(v) (voices[v].state & (AWE_ST_ON|AWE_ST_SUSTAINED|AWE_ST_RELEASED)) /* MIDI channel effects information (for hw control) */ -#if AWE_MAX_CHANNELS < AWE_MAX_VOICES -static awe_chan_info channels[AWE_MAX_VOICES]; -#else -static awe_chan_info channels[AWE_MAX_CHANNELS]; -#endif +static awe_chan_info *channels; /*---------------------------------------------------------------- * global variables *----------------------------------------------------------------*/ +#ifndef AWE_DEFAULT_BASE_ADDR +#define AWE_DEFAULT_BASE_ADDR 0 /* autodetect */ +#endif + +#ifndef AWE_DEFAULT_MEM_SIZE +#define AWE_DEFAULT_MEM_SIZE 0 /* autodetect */ +#endif + /* awe32 base address (overwritten at initialization) */ -static int awe_base = 0; -/* memory byte size (overwritten at initialization) */ -static long awe_mem_size = 0; +static int awe_base = AWE_DEFAULT_BASE_ADDR; +/* memory byte size */ +static int awe_mem_size = AWE_DEFAULT_MEM_SIZE; +/* DRAM start offset */ +static int awe_mem_start = AWE_DRAM_OFFSET; /* maximum channels for playing */ static int awe_max_voices = AWE_MAX_VOICES; -static long free_mem_ptr = 0; /* free word byte size */ -static int free_info = 0; /* free info tables */ -static int last_info = 0; /* last loaded info index */ -static int free_sample = 0; /* free sample tables */ -static int last_sample = 0; /* last loaded sample index */ -static int loaded_once = 0; /* samples are loaded after init? */ -static unsigned short current_sf_id = 0; /* internal id */ - -static int reverb_mode = 0; /* reverb mode */ -static int chorus_mode = 0; /* chorus mode */ -static unsigned short init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */ +static int patch_opened = 0; /* sample already loaded? */ + +static int reverb_mode = 3; /* reverb mode */ +static int chorus_mode = 5; /* chorus mode */ +static short init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */ static int awe_present = FALSE; /* awe device present? */ static int awe_busy = FALSE; /* awe device opened? */ -#define DEFAULT_DRUM_FLAGS (1 << 9) +#define DEFAULT_DRUM_FLAGS ((1 << 9) | (1 << 25)) #define IS_DRUM_CHANNEL(c) (drum_flags & (1 << (c))) -static unsigned long drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */ +static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */ -static int awe_channel_mode = 0; /* channel control mode */ +static int playing_mode = AWE_PLAY_INDIRECT; +#define SINGLE_LAYER_MODE() (playing_mode == AWE_PLAY_INDIRECT || playing_mode == AWE_PLAY_DIRECT) +#define MULTI_LAYER_MODE() (playing_mode == AWE_PLAY_MULTI || playing_mode == AWE_PLAY_MULTI2) + +static int current_alloc_time = 0; /* voice allocation index for channel mode */ + +static struct MiscModeDef { + int value; + int init_each_time; +} misc_modes_default[AWE_MD_END] = { + {0,0}, {0,0}, /* <-- not used */ + {AWE_VERSION_NUMBER, FALSE}, + {TRUE, TRUE}, /* exclusive */ + {TRUE, TRUE}, /* realpan */ + {AWE_DEFAULT_BANK, TRUE}, /* gusbank */ + {FALSE, TRUE}, /* keep effect */ + {AWE_DEFAULT_ATTENUATION, FALSE}, /* zero_atten */ + {FALSE, TRUE}, /* chn_prior */ + {AWE_DEFAULT_MOD_SENSE, TRUE}, /* modwheel sense */ + {AWE_DEFAULT_PRESET, TRUE}, /* def_preset */ + {AWE_DEFAULT_BANK, TRUE}, /* def_bank */ + {AWE_DEFAULT_DRUM, TRUE}, /* def_drum */ +}; -static int current_alloc_time = 0; /* voice allocation time */ +static int misc_modes[AWE_MD_END]; -static int awe_gus_bank = AWE_DEFAULT_BANK; /* GUS default bank number */ -static int awe_exclusive_sound = TRUE; /* exclusive sound on */ +static int awe_bass_level = 5; +static int awe_treble_level = 9; static struct synth_info awe_info = { @@ -230,15 +302,16 @@ static void awe_reset_samples(void); /* emu8000 chip i/o access */ static void awe_poke(unsigned short cmd, unsigned short port, unsigned short data); -static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned long data); +static void awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data); static unsigned short awe_peek(unsigned short cmd, unsigned short port); -static unsigned long awe_peek_dw(unsigned short cmd, unsigned short port); +static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port); static void awe_wait(unsigned short delay); /* initialize emu8000 chip */ static void awe_initialize(void); /* set voice parameters */ +static void awe_init_misc_modes(int init_all); static void awe_init_voice_info(awe_voice_info *vp); static void awe_init_voice_parm(awe_voice_parm *pp); #ifdef AWE_HAS_GUS_COMPATIBILITY @@ -255,7 +328,8 @@ static void awe_note_on(int voice); static void awe_note_off(int voice); static void awe_terminate(int voice); -static void awe_exclusive_off(int voice, int exclass); +static void awe_exclusive_off(int voice); +static void awe_note_off_all(int do_sustain); /* calculate voice parameters */ typedef void (*fx_affect_func)(int voice, int forced); @@ -267,13 +341,12 @@ static void awe_fx_fmmod(int voice, int forced); static void awe_fx_tremfrq(int voice, int forced); static void awe_fx_fm2frq2(int voice, int forced); -static void awe_fx_cutoff(int voice, int forced); static void awe_calc_pitch(int voice); #ifdef AWE_HAS_GUS_COMPATIBILITY static void awe_calc_pitch_from_freq(int voice, int freq); #endif static void awe_calc_volume(int voice); -static void awe_voice_init(int voice); +static void awe_voice_init(int voice, int keep_ch_info); /* sequencer interface */ static int awe_open(int dev, int mode); @@ -304,25 +377,35 @@ #endif static void awe_hw_awe_control(int dev, int cmd, unsigned char *event); static void awe_voice_change(int voice, fx_affect_func func); +static void awe_sostenuto_on(int voice, int forced); static void awe_sustain_off(int voice, int forced); /* voice search */ -static awe_voice_list *awe_search_instr(int bank, int preset); -static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist); -static void awe_alloc_multi_voices(int ch, int note, int velocity); +static int awe_search_instr(int bank, int preset); +static int awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist); +static void awe_alloc_multi_voices(int ch, int note, int velocity, int key); static void awe_alloc_one_voice(int voice, int note, int velocity); static int awe_clear_voice(void); /* load / remove patches */ -static void awe_check_loaded(void); -static int awe_load_info(awe_patch_info *patch, const char *addr); -static int awe_load_data(awe_patch_info *patch, const char *addr); +static int awe_open_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_close_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_unload_patch(awe_patch_info *patch, const char *addr, int count); +static int awe_load_info(awe_patch_info *patch, const char *addr, int count); +static int awe_load_data(awe_patch_info *patch, const char *addr, int count); +static int awe_replace_data(awe_patch_info *patch, const char *addr, int count); +static int awe_load_map(awe_patch_info *patch, const char *addr, int count); #ifdef AWE_HAS_GUS_COMPATIBILITY static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag); #endif -static int awe_write_wave_data(const char *addr, long offset, int size); -static awe_voice_list *awe_get_removed_list(awe_voice_list *curp); -static void awe_remove_samples(void); +static int check_patch_opened(int type, char *name); +static int awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels); +static void add_sf_info(int rec); +static void add_sf_sample(int rec); +static void purge_old_list(int rec, int next); +static void add_info_list(int rec); +static void awe_remove_samples(int sf_id); +static void rebuild_preset_list(void); static short awe_set_sample(awe_voice_info *vp); /* lowlevel functions */ @@ -333,89 +416,28 @@ static void awe_tweak_voice(int voice); static void awe_tweak(void); static void awe_init_fm(void); -static int awe_open_dram_for_write(int offset); -static int awe_open_dram_for_read(int offset); +static int awe_open_dram_for_write(int offset, int channels); static void awe_open_dram_for_check(void); static void awe_close_dram(void); -static void awe_close_dram_for_read(void); static void awe_write_dram(unsigned short c); static int awe_detect_base(int addr); static int awe_detect(void); static int awe_check_dram(void); +static int awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count); static void awe_set_chorus_mode(int mode); +static int awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count); static void awe_set_reverb_mode(int mode); - -#ifdef AWE_OBSOLETE_VOXWARE - -#define awe_check_port() 0 /* always false */ -#define awe_request_region() /* nothing */ -#define awe_release_region() /* nothing */ - -#else /* AWE_OBSOLETE_VOXWARE */ - -/* the following macros are osbolete */ - -#define PERMANENT_MALLOC(type,var,size,memptr) \ - var = (type)(sound_mem_blocks[sound_nblocks++] = vmalloc(size)) -#define RET_ERROR(err) -err - -#endif /* AWE_OBSOLETE_VOXWARE */ - - -/* macros for Linux and FreeBSD compatibility */ - -#undef OUTW -#undef COPY_FROM_USER -#undef GET_BYTE_FROM_USER -#undef GET_SHORT_FROM_USER -#undef IOCTL_TO_USER - -#ifdef linux -# define NO_DATA_ERR ENODATA -# define OUTW(data, addr) outw(data, addr) - -#ifdef AWE_NEW_KERNEL_INTERFACE -# define COPY_FROM_USER(target, source, offs, count) \ - copy_from_user( ((caddr_t)(target)),(source)+(offs),(count) ) -# define GET_BYTE_FROM_USER(target, addr, offs) \ - get_user(target, (unsigned char*)&((addr)[offs])) -# define GET_SHORT_FROM_USER(target, addr, offs) \ - get_user(target, (unsigned short*)&((addr)[offs])) -# define IOCTL_TO_USER(target, offs, source, count) \ - copy_to_user ( ((caddr_t)(target)),(source)+(offs),(count) ) -#else /* AWE_NEW_KERNEL_INTERFACE */ -# define COPY_FROM_USER(target, source, offs, count) \ - memcpy_fromfs( ((caddr_t)(target)),(source)+(offs),(count) ) -# define GET_BYTE_FROM_USER(target, addr, offs) \ - *((char *)&(target)) = get_fs_byte( (addr)+(offs) ) -# define GET_SHORT_FROM_USER(target, addr, offs) \ - *((short *)&(target)) = get_fs_word( (addr)+(offs) ) -# define IOCTL_TO_USER(target, offs, source, count) \ - memcpy_tofs ( ((caddr_t)(target)),(source)+(offs),(count) ) -#endif /* AWE_NEW_KERNEL_INTERFACE */ - -# define BZERO(target,len) \ - memset( (caddr_t)target, '\0', len ) -# define MEMCPY(dst,src,len) \ - memcpy((caddr_t)dst, (caddr_t)src, len) - -#elif defined(__FreeBSD__) -# define NO_DATA_ERR EINVAL -# define OUTW(data, addr) outw(addr, data) -# define COPY_FROM_USER(target, source, offs, count) \ - uiomove( ((caddr_t)(target)),(count),((struct uio *)(source)) ) -# define GET_BYTE_FROM_USER(target, addr, offs) \ - uiomove( ((char*)&(target)), 1, ((struct uio *)(addr)) ) -# define GET_SHORT_FROM_USER(target, addr, offs) \ - uiomove( ((char*)&(target)), 2, ((struct uio *)(addr)) ) -# define IOCTL_TO_USER(target, offs, source, count) \ - memcpy( &((target)[offs]), (source), (count) ) -# define BZERO(target,len) \ - bzero( (caddr_t)target, len ) -# define MEMCPY(dst,src,len) \ - bcopy((caddr_t)src, (caddr_t)dst, len) +static void awe_equalizer(int bass, int treble); +#ifdef CONFIG_AWE32_MIXER +static int awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg); #endif +/* define macros for compatibility */ +#ifdef __FreeBSD__ +# include +#else +# include "awe_compat.h" +#endif /*---------------------------------------------------------------- * synth operation table @@ -423,7 +445,9 @@ static struct synth_operations awe_operations = { - "EMU8K", +#ifdef AWE_OSS38 + "Emu8000", +#endif &awe_info, 0, SYNTH_TYPE_SAMPLE, @@ -449,6 +473,15 @@ awe_setup_voice }; +#ifdef CONFIG_AWE32_MIXER +static struct mixer_operations awe_mixer_operations = { +#ifndef __FreeBSD__ + "AWE32", +#endif + "AWE32 Equalizer", + awe_mixer_ioctl, +}; +#endif /*================================================================ @@ -456,10 +489,13 @@ *================================================================*/ #ifdef AWE_OBSOLETE_VOXWARE -long attach_awe_obsolete(long mem_start, struct address_info *hw_config) +#define ATTACH_DECL static #else -int attach_awe(void) +#define ATTACH_DECL /**/ #endif + +ATTACH_DECL +int attach_awe(void) { /* check presence of AWE32 card */ if (! awe_detect()) { @@ -473,16 +509,29 @@ return 0; } - /* allocate sample tables */ - PERMANENT_MALLOC(awe_sample_info *, samples, - AWE_MAX_SAMPLES * AWE_SAMPLE_INFO_SIZE, mem_start); - PERMANENT_MALLOC(awe_voice_list *, infos, - AWE_MAX_INFOS * sizeof(awe_voice_list), mem_start); - if (samples == NULL || infos == NULL) { + /* set buffers to NULL */ + voices = NULL; + channels = NULL; + sflists = NULL; + samples = NULL; + infos = NULL; + + /* voice & channel info */ + voices = (voice_info*)my_malloc(AWE_MAX_VOICES * sizeof(voice_info)); + channels = (awe_chan_info*)my_malloc(AWE_MAX_CHANNELS * sizeof(awe_chan_info)); + + if (voices == NULL || channels == NULL) { + my_free(voices); + my_free(channels); printk("AWE32: can't allocate sample tables\n"); return 0; } + /* allocate sample tables */ + INIT_TABLE(sflists, max_sfs, AWE_MAX_SF_LISTS, sf_list); + INIT_TABLE(samples, max_samples, AWE_MAX_SAMPLES, awe_sample_list); + INIT_TABLE(infos, max_infos, AWE_MAX_INFOS, awe_voice_list); + if (num_synths >= MAX_SYNTH_DEV) printk("AWE32 Error: too many synthesizers\n"); else { @@ -491,6 +540,12 @@ synth_devs[num_synths++] = &awe_operations; } +#ifdef CONFIG_AWE32_MIXER + if (num_mixers < MAX_MIXER_DEV) { + mixer_devs[num_mixers++] = &awe_mixer_operations; + } +#endif + /* reserve I/O ports for awedrv */ awe_request_region(); @@ -500,15 +555,16 @@ /* intialize AWE32 hardware */ awe_initialize(); -#ifndef __FreeBSD__ - printk("AWE32 Sound Driver v%s (DRAM %dk)\n", - AWEDRV_VERSION, (int)awe_mem_size/1024); -#else - DEBUG(0,printk("AWE32 Sound Driver v%s (DRAM %dk)\n", - AWEDRV_VERSION, (int)awe_mem_size/1024)); + sprintf(awe_info.name, "AWE32-%s (RAM%dk)", + AWEDRV_VERSION, awe_mem_size/1024); +#ifdef __FreeBSD__ + printk("awe0: ", awe_mem_size/1024); +#elif defined(AWE_DEBUG_ON) + printk("%s\n", awe_info.name); #endif - sprintf(awe_info.name, "AWE32 Driver v%s (DRAM %dk)", - AWEDRV_VERSION, (int)awe_mem_size/1024); + + /* set default values */ + awe_init_misc_modes(TRUE); /* set reverb & chorus modes */ awe_set_reverb_mode(reverb_mode); @@ -516,24 +572,57 @@ awe_present = TRUE; -#ifdef AWE_OBSOLETE_VOXWARE - return mem_start; -#else return 1; -#endif } +#ifdef AWE_DYNAMIC_BUFFER +static void free_tables(void) +{ + my_free(sflists); + sflists = NULL; max_sfs = 0; + my_free(samples); + samples = NULL; max_samples = 0; + my_free(infos); + infos = NULL; max_infos = 0; +} +#else +#define free_buffers() /**/ +#endif + + +ATTACH_DECL void unload_awe(void) { if (awe_present) { awe_reset_samples(); awe_release_region(); + my_free(voices); + my_free(channels); + free_tables(); + awe_present = FALSE; } } +/*---------------------------------------------------------------- + * old type interface + *----------------------------------------------------------------*/ + #ifdef AWE_OBSOLETE_VOXWARE + +#ifdef __FreeBSD__ +long attach_awe_obsolete(long mem_start, struct address_info *hw_config) +#else +int attach_awe_obsolete(int mem_start, struct address_info *hw_config) +#endif +{ + my_malloc_init(mem_start); + if (! attach_awe()) + return 0; + return my_malloc_memptr(); +} + int probe_awe_obsolete(struct address_info *hw_config) { return 1; @@ -542,6 +631,7 @@ #endif + /*================================================================ * clear sample tables *================================================================*/ @@ -552,15 +642,14 @@ int i; /* free all bank tables */ - for (i = 0; i < AWE_MAX_PRESETS; i++) { - preset_table[i] = NULL; - } + for (i = 0; i < AWE_MAX_PRESETS; i++) + preset_table[i] = -1; + + free_tables(); - free_mem_ptr = 0; - last_sample = free_sample = 0; - last_info = free_info = 0; current_sf_id = 0; - loaded_once = 0; + locked_sf_id = 0; + patch_opened = 0; } @@ -584,7 +673,7 @@ /* write 32bit data */ INLINE static void -awe_poke_dw(unsigned short cmd, unsigned short port, unsigned long data) +awe_poke_dw(unsigned short cmd, unsigned short port, unsigned int data) { awe_set_cmd(cmd); OUTW(data, awe_port(port)); /* write lower 16 bits */ @@ -602,10 +691,10 @@ } /* read 32bit data */ -INLINE static unsigned long +INLINE static unsigned int awe_peek_dw(unsigned short cmd, unsigned short port) { - unsigned long k1, k2; + unsigned int k1, k2; awe_set_cmd(cmd); k1 = inw(awe_port(port)); k2 = inw(awe_port(port)+2); @@ -636,6 +725,13 @@ break; } +/* write a word data */ +INLINE static void +awe_write_dram(unsigned short c) +{ + awe_poke(AWE_SMLD, c); +} + #ifndef AWE_OBSOLETE_VOXWARE @@ -706,6 +802,9 @@ /* enable audio */ awe_poke(AWE_HWCF3, 0x0004); + + /* set equalizer */ + awe_equalizer(5, 9); } @@ -717,7 +816,7 @@ static void awe_init_voice_info(awe_voice_info *vp) { - vp->sf_id = 0; + vp->sf_id = 0; /* normal mode */ vp->sample = 0; vp->rate_offset = 0; @@ -792,10 +891,10 @@ freq_to_note(int mHz) { /* abscents = log(mHz/8176) / log(2) * 1200 */ - unsigned long max_val = (unsigned long)0xffffffff / 10000; + unsigned int max_val = (unsigned int)0xffffffff / 10000; int i, times; - unsigned long base; - unsigned long freq; + unsigned int base; + unsigned int freq; int note, tune; if (mHz == 0) @@ -913,7 +1012,7 @@ static int calc_parm_hold(int msec) { - int val = 0x7f - (unsigned char)(msec / 92); + int val = (0x7f * 92 - msec) / 92; if (val < 1) val = 1; if (val > 127) val = 127; return val; @@ -956,45 +1055,72 @@ /* set an effect value */ #define FX_SET(rec,type,value) \ - ((rec)->flags[(type)/8] |= (1 << ((type) % 8)), \ - (rec)->val[type] = (value)) + ((rec)->flags[type] = 1, (rec)->val[type] = (value)) +#define FX_ADD(rec,type,value) \ + ((rec)->flags[type] = 2, (rec)->val[type] = (value)) +#define FX_UNSET(rec,type) \ + ((rec)->flags[type] = 0, (rec)->val[type] = 0) /* check the effect value is set */ -#define FX_ON(rec,type) ((rec)->flags[(type)/8] & (1<<((type)%8))) +#define FX_ON(rec,type) ((rec)->flags[type]) + +static unsigned char +FX_BYTE(FX_Rec *rec, FX_Rec *lay, int type, unsigned char value) +{ + int effect = 0; + int on = 0; + if (lay && (on = FX_ON(lay, type)) != 0) + effect = lay->val[type]; + if (!on && (on = FX_ON(rec, type)) != 0) + effect = rec->val[type]; + if (on == 1) + return (unsigned char)effect; + else if (on == 2) + return (unsigned char)(effect + (int)value); + return value; +} -/* get byte effect value */ -#define FX_BYTE(rec,type,value) \ - (unsigned char)(FX_ON(rec,type) ? (rec)->val[type] : (value)) /* get word effect value */ -#define FX_WORD(rec,type,value) \ - (unsigned short)(FX_ON(rec,type) ? (rec)->val[type] : (value)) +static unsigned short +FX_WORD(FX_Rec *rec, FX_Rec *lay, int type, unsigned short value) +{ + int effect = 0; + int on = 0; + if (lay && (on = FX_ON(lay, type)) != 0) + effect = lay->val[type]; + if (!on && (on = FX_ON(rec, type)) != 0) + effect = rec->val[type]; + if (on == 1) + return (unsigned short)effect; + else if (on == 2) + return (unsigned short)(effect + (int)value); + return value; +} /* get word (upper=type1/lower=type2) effect value */ static unsigned short -FX_COMB(FX_Rec *rec, int type1, int type2, unsigned short value) +FX_COMB(FX_Rec *rec, FX_Rec *lay, int type1, int type2, unsigned short value) { unsigned short tmp; - if (FX_ON(rec, type1)) - tmp = (unsigned short)(rec->val[type1]) << 8; - else - tmp = value & 0xff00; - if (FX_ON(rec, type2)) - tmp |= (unsigned short)(rec->val[type2]) & 0xff; - else - tmp |= value & 0xff; + tmp = FX_BYTE(rec, lay, type1, (unsigned char)(value >> 8)); + tmp <<= 8; + tmp |= FX_BYTE(rec, lay, type2, (unsigned char)(value & 0xff)); return tmp; } /* address offset */ -static long -FX_OFFSET(FX_Rec *rec, int lo, int hi, int mode) +static int +FX_OFFSET(FX_Rec *rec, FX_Rec *lay, int lo, int hi, int mode) { - long addr = 0; - if (FX_ON(rec, hi)) { + int addr = 0; + if (lay && FX_ON(lay, hi)) + addr = (short)lay->val[hi]; + else if (FX_ON(rec, hi)) addr = (short)rec->val[hi]; - addr = addr << 15; - } - if (FX_ON(rec, lo)) + addr = addr << 15; + if (lay && FX_ON(lay, lo)) + addr += (short)lay->val[lo]; + else if (FX_ON(rec, lo)) addr += (short)rec->val[lo]; if (!(mode & AWE_SAMPLE_8BITS)) addr /= 2; @@ -1009,10 +1135,13 @@ static void awe_note_on(int voice) { - unsigned long temp; - long addr; + unsigned int temp; + int addr; awe_voice_info *vp; FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; /* A voice sample must assigned before calling */ if ((vp = voices[voice].sample) == NULL || vp->index < 0) @@ -1027,17 +1156,17 @@ /* modulation & volume envelope */ awe_poke(AWE_ENVVAL(voice), - FX_WORD(fx, AWE_FX_ENV1_DELAY, vp->parm.moddelay)); + FX_WORD(fx, fx_lay, AWE_FX_ENV1_DELAY, vp->parm.moddelay)); awe_poke(AWE_ATKHLD(voice), - FX_COMB(fx, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK, + FX_COMB(fx, fx_lay, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK, vp->parm.modatkhld)); awe_poke(AWE_DCYSUS(voice), - FX_COMB(fx, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY, + FX_COMB(fx, fx_lay, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY, vp->parm.moddcysus)); awe_poke(AWE_ENVVOL(voice), - FX_WORD(fx, AWE_FX_ENV2_DELAY, vp->parm.voldelay)); + FX_WORD(fx, fx_lay, AWE_FX_ENV2_DELAY, vp->parm.voldelay)); awe_poke(AWE_ATKHLDV(voice), - FX_COMB(fx, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK, + FX_COMB(fx, fx_lay, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK, vp->parm.volatkhld)); /* decay/sustain parameter for volume envelope must be set at last */ @@ -1049,14 +1178,14 @@ /* modulation envelope heights */ awe_poke(AWE_PEFE(voice), - FX_COMB(fx, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF, + FX_COMB(fx, fx_lay, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF, vp->parm.pefe)); /* lfo1/2 delay */ awe_poke(AWE_LFO1VAL(voice), - FX_WORD(fx, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay)); + FX_WORD(fx, fx_lay, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay)); awe_poke(AWE_LFO2VAL(voice), - FX_WORD(fx, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay)); + FX_WORD(fx, fx_lay, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay)); /* lfo1 pitch & cutoff shift */ awe_fx_fmmod(voice, TRUE); @@ -1065,27 +1194,25 @@ /* lfo2 pitch & freq */ awe_fx_fm2frq2(voice, TRUE); /* pan & loop start */ - awe_set_pan(voice, 1); + awe_set_pan(voice, TRUE); /* chorus & loop end (chorus 8bit, MSB) */ addr = vp->loopend - 1; - addr += FX_OFFSET(fx, AWE_FX_LOOP_END, + addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_END, AWE_FX_COARSE_LOOP_END, vp->mode); - temp = FX_BYTE(fx, AWE_FX_CHORUS, vp->parm.chorus); - temp = (temp <<24) | (unsigned long)addr; + temp = FX_BYTE(fx, fx_lay, AWE_FX_CHORUS, vp->parm.chorus); + temp = (temp <<24) | (unsigned int)addr; awe_poke_dw(AWE_CSL(voice), temp); - DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", - (int)vp->loopend, (int)addr)); + DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n", vp->loopend, addr)); /* Q & current address (Q 4bit value, MSB) */ addr = vp->start - 1; - addr += FX_OFFSET(fx, AWE_FX_SAMPLE_START, + addr += FX_OFFSET(fx, fx_lay, AWE_FX_SAMPLE_START, AWE_FX_COARSE_SAMPLE_START, vp->mode); - temp = FX_BYTE(fx, AWE_FX_FILTERQ, vp->parm.filterQ); - temp = (temp<<28) | (unsigned long)addr; + temp = FX_BYTE(fx, fx_lay, AWE_FX_FILTERQ, vp->parm.filterQ); + temp = (temp<<28) | (unsigned int)addr; awe_poke_dw(AWE_CCCA(voice), temp); - DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", - (int)vp->start, (int)addr)); + DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n", vp->start, addr)); /* reset volume */ awe_poke_dw(AWE_VTFT(voice), 0x0000FFFF); @@ -1093,13 +1220,15 @@ /* turn on envelope */ awe_poke(AWE_DCYSUSV(voice), - FX_COMB(fx, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY, + FX_COMB(fx, fx_lay, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY, vp->parm.voldcysus)); - /* set chorus */ - temp = FX_BYTE(fx, AWE_FX_REVERB, vp->parm.reverb); + /* set reverb */ + temp = FX_BYTE(fx, fx_lay, AWE_FX_REVERB, vp->parm.reverb); temp = (awe_peek_dw(AWE_PTRX(voice)) & 0xffff0000) | (temp<<8); awe_poke_dw(AWE_PTRX(voice), temp); awe_poke_dw(AWE_CPF(voice), 0x40000000); + + voices[voice].state = AWE_ST_ON; } @@ -1110,24 +1239,22 @@ awe_voice_info *vp; unsigned short tmp; FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + if ((vp = voices[voice].sample) == NULL) { - awe_voice_init(voice); + voices[voice].state = AWE_ST_OFF; return; } - if (FX_ON(fx, AWE_FX_ENV1_RELEASE)) - tmp = 0x8000 | fx->val[AWE_FX_ENV1_RELEASE]; - else - tmp = vp->parm.modrelease; + tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV1_RELEASE, + (unsigned char)vp->parm.modrelease); awe_poke(AWE_DCYSUS(voice), tmp); - if (FX_ON(fx, AWE_FX_ENV2_RELEASE)) - tmp = 0x8000 | fx->val[AWE_FX_ENV2_RELEASE]; - else - tmp = vp->parm.volrelease; + tmp = 0x8000 | FX_BYTE(fx, fx_lay, AWE_FX_ENV2_RELEASE, + (unsigned char)vp->parm.volrelease); awe_poke(AWE_DCYSUSV(voice), tmp); - voices[voice].state = AWE_ST_OFF; - - awe_voice_init(voice); + awe_voice_init(voice, TRUE); } /* force to terminate the voice (no releasing echo) */ @@ -1136,26 +1263,27 @@ { awe_poke(AWE_DCYSUSV(voice), 0x807F); awe_tweak_voice(voice); - awe_voice_init(voice); + awe_voice_init(voice, FALSE); } - /* turn off other voices with the same exclusive class (for drums) */ static void -awe_exclusive_off(int voice, int exclass) +awe_exclusive_off(int voice) { - int i; + int i, exclass; - if (exclass == 0) /* not exclusive */ + if (voices[voice].sample == NULL) return; + if ((exclass = voices[voice].sample->exclusiveClass) == 0) + return; /* not exclusive */ /* turn off voices with the same class */ for (i = 0; i < awe_max_voices; i++) { - if (i != voice && IS_PLAYING(voice) && - voices[i].sample && + if (i != voice && IS_PLAYING(i) && + voices[i].sample && voices[i].ch == voices[voice].ch && voices[i].sample->exclusiveClass == exclass) { DEBUG(4,printk("AWE32: [exoff(%d)]\n", i)); - awe_note_off(i); + awe_terminate(i); } } } @@ -1182,20 +1310,25 @@ awe_set_pitch(voice, forced); } -/* change volume */ +/* change volume & cutoff */ static void awe_set_volume(int voice, int forced) { awe_voice_info *vp; unsigned short tmp2; FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; if (IS_NO_EFFECT(voice) && !forced) return; if ((vp = voices[voice].sample) == NULL || vp->index < 0) return; - tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff); - tmp2 = (tmp2 << 8) | voices[voice].avol; + tmp2 = FX_BYTE(fx, fx_lay, AWE_FX_CUTOFF, vp->parm.cutoff); + tmp2 = (tmp2 << 8); + tmp2 |= FX_BYTE(fx, fx_lay, AWE_FX_ATTEN, + (unsigned char)voices[voice].avol); awe_poke(AWE_IFATN(voice), tmp2); } @@ -1212,10 +1345,13 @@ static void awe_set_pan(int voice, int forced) { - unsigned long temp; - long addr; + unsigned int temp; + int addr; awe_voice_info *vp; FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; if (IS_NO_EFFECT(voice) && !forced) return; if ((vp = voices[voice].sample) == NULL || vp->index < 0) @@ -1239,13 +1375,12 @@ } if (forced || temp != voices[voice].apan) { addr = vp->loopstart - 1; - addr += FX_OFFSET(fx, AWE_FX_LOOP_START, + addr += FX_OFFSET(fx, fx_lay, AWE_FX_LOOP_START, AWE_FX_COARSE_LOOP_START, vp->mode); - temp = (temp<<24) | (unsigned long)addr; + temp = (temp<<24) | (unsigned int)addr; awe_poke_dw(AWE_PSST(voice), temp); voices[voice].apan = temp; - DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", - (int)vp->loopstart, (int)addr)); + DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n", vp->loopstart, addr)); } } @@ -1255,11 +1390,15 @@ { awe_voice_info *vp; FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + if (IS_NO_EFFECT(voice) && !forced) return; if ((vp = voices[voice].sample) == NULL || vp->index < 0) return; awe_poke(AWE_FMMOD(voice), - FX_COMB(fx, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF, + FX_COMB(fx, fx_lay, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF, vp->parm.fmmod)); } @@ -1269,11 +1408,15 @@ { awe_voice_info *vp; FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + if (IS_NO_EFFECT(voice) && !forced) return; if ((vp = voices[voice].sample) == NULL || vp->index < 0) return; awe_poke(AWE_TREMFRQ(voice), - FX_COMB(fx, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ, + FX_COMB(fx, fx_lay, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ, vp->parm.tremfrq)); } @@ -1283,29 +1426,18 @@ { awe_voice_info *vp; FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + if (IS_NO_EFFECT(voice) && !forced) return; if ((vp = voices[voice].sample) == NULL || vp->index < 0) return; awe_poke(AWE_FM2FRQ2(voice), - FX_COMB(fx, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ, + FX_COMB(fx, fx_lay, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ, vp->parm.fm2frq2)); } -/* set total cutoff & current pitch */ -static void -awe_fx_cutoff(int voice, int forced) -{ - unsigned short tmp2; - awe_voice_info *vp; - FX_Rec *fx = &voices[voice].cinfo->fx; - if (IS_NO_EFFECT(voice)) return; - if ((vp = voices[voice].sample) == NULL || vp->index < 0) - return; - tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff); - tmp2 = (tmp2 << 8) | voices[voice].avol; - awe_poke(AWE_IFATN(voice), tmp2); -} - /*================================================================ * calculate pitch offset @@ -1326,6 +1458,7 @@ if ((ap = vp->sample) == NULL) return; if (ap->index < 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); if (awe_set_sample(ap) < 0) return; } @@ -1350,10 +1483,10 @@ DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset)); /* add initial pitch correction */ - if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) { - DEBUG(3,printk("AWE32: fx_pitch(%d) %d\n", voice, cp->fx.val[AWE_FX_INIT_PITCH])); + if (FX_ON(&cp->fx_layer[vp->layer], AWE_FX_INIT_PITCH)) + offset += cp->fx_layer[vp->layer].val[AWE_FX_INIT_PITCH]; + else if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) offset += cp->fx.val[AWE_FX_INIT_PITCH]; - } /* 0xe000: root pitch */ vp->apitch = 0xe000 + ap->rate_offset + offset; @@ -1373,20 +1506,27 @@ voice_info *vp = &voices[voice]; awe_voice_info *ap; FX_Rec *fx = &voices[voice].cinfo->fx; + FX_Rec *fx_lay = NULL; int offset; int note; + if (voices[voice].layer < MAX_LAYERS) + fx_lay = &voices[voice].cinfo->fx_layer[voices[voice].layer]; + /* search voice information */ if ((ap = vp->sample) == NULL) return; if (ap->index < 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); if (awe_set_sample(ap) < 0) return; } note = freq_to_note(freq); offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200; offset = (offset * ap->scaleTuning) / 100; - if (FX_ON(fx, AWE_FX_INIT_PITCH)) + if (fx_lay && FX_ON(fx_lay, AWE_FX_INIT_PITCH)) + offset += fx_lay->val[AWE_FX_INIT_PITCH]; + else if (FX_ON(fx, AWE_FX_INIT_PITCH)) offset += fx->val[AWE_FX_INIT_PITCH]; vp->apitch = 0xe000 + ap->rate_offset + offset; if (vp->apitch > 0xffff) @@ -1430,6 +1570,7 @@ ap = vp->sample; if (ap->index < 0) { + DEBUG(3,printk("AWE32: set sample (%d)\n", ap->sample)); if (awe_set_sample(ap) < 0) return; } @@ -1437,6 +1578,7 @@ /* 0 - 127 */ vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127); vol = vol * ap->amplitude / 127; + if (vol < 0) vol = 0; if (vol > 127) vol = 127; @@ -1450,56 +1592,121 @@ } +/* set sostenuto on */ +static void awe_sostenuto_on(int voice, int forced) +{ + if (IS_NO_EFFECT(voice) && !forced) return; + voices[voice].sostenuto = 127; +} + + +/* drop sustain */ +static void awe_sustain_off(int voice, int forced) +{ + if (voices[voice].state == AWE_ST_SUSTAINED) + awe_note_off(voice); +} + + /*================================================================ * synth operation routines *================================================================*/ #define AWE_VOICE_KEY(v) (0x8000 | (v)) #define AWE_CHAN_KEY(c,n) (((c) << 8) | ((n) + 1)) +#define KEY_CHAN_MATCH(key,c) (((key) >> 8) == (c)) /* initialize the voice */ static void -awe_voice_init(int voice) +awe_voice_init(int voice, int keep_ch_info) { - voices[voice].note = -1; - voices[voice].velocity = 0; - voices[voice].sample = NULL; - voices[voice].state = AWE_ST_OFF; - voices[voice].cinfo = &channels[voice]; - voices[voice].ch = -1; - voices[voice].key = AWE_VOICE_KEY(voice); - voices[voice].time = current_alloc_time; + voice_info *vp = &voices[voice]; + vp->note = -1; + vp->velocity = 0; + vp->sostenuto = 0; + if (playing_mode == AWE_PLAY_DIRECT) + vp->key = AWE_VOICE_KEY(voice); + else + vp->key = 0; + vp->time = current_alloc_time; - /* clear voice mapping */ - voice_alloc->map[voice] = 0; + /* keep channel info for terminating release echo if necessary */ + if (keep_ch_info) + vp->state = AWE_ST_RELEASED; + else { + vp->sample = NULL; + vp->cinfo = &channels[voice]; + vp->ch = voice; + vp->state = AWE_ST_OFF; + } /* emu8000 parameters */ - voices[voice].apitch = 0; - voices[voice].avol = 255; - voices[voice].apan = -1; + vp->apitch = 0; + vp->avol = 255; + vp->apan = -1; + + /* clear voice mapping */ + voice_alloc->map[voice] = 0; /* clear effects */ - if (! awe_channel_mode) - BZERO(&voices[voice].cinfo->fx, sizeof(FX_Rec)); + if (SINGLE_LAYER_MODE() && !misc_modes[AWE_MD_KEEP_EFFECT]) { + BZERO(&vp->cinfo->fx, sizeof(vp->cinfo->fx)); + BZERO(&vp->cinfo->fx_layer, sizeof(vp->cinfo->fx_layer)); + } + + /*awe_tweak_voice(voice);*/ } /* initialize channel info */ -static void awe_channel_init(int ch) +static void awe_channel_init(int ch, int init_all) { - channels[ch].panning = 0; /* zero center */ - channels[ch].bender = 0; /* zero tune skew */ - channels[ch].bender_range = 200; /* sense * 100 */ - channels[ch].main_vol = 127; - channels[ch].expression_vol = 127; - if (awe_channel_mode && IS_DRUM_CHANNEL(ch)) - channels[ch].bank = AWE_DRUM_BANK; - else - channels[ch].bank = AWE_DEFAULT_BANK; - channels[ch].instr = 0; - channels[ch].vrec = NULL; - channels[ch].def_vrec = NULL; - channels[ch].sustained = 0; - BZERO(&channels[ch].fx, sizeof(FX_Rec)); + awe_chan_info *cp = &channels[ch]; + if (init_all) { + cp->panning = 0; /* zero center */ + cp->bender_range = 200; /* sense * 100 */ + cp->main_vol = 127; + if (MULTI_LAYER_MODE() && IS_DRUM_CHANNEL(ch)) { + cp->instr = misc_modes[AWE_MD_DEF_DRUM]; + cp->bank = AWE_DRUM_BANK; + } else { + cp->instr = misc_modes[AWE_MD_DEF_PRESET]; + cp->bank = misc_modes[AWE_MD_DEF_BANK]; + } + cp->vrec = -1; + cp->def_vrec = -1; + } + + cp->bender = 0; /* zero tune skew */ + cp->expression_vol = 127; + cp->chan_press = 0; + cp->sustained = 0; + + if (! misc_modes[AWE_MD_KEEP_EFFECT]) { + BZERO(&cp->fx, sizeof(cp->fx)); + BZERO(&cp->fx_layer, sizeof(cp->fx_layer)); + } +} + + +/* change the voice parameters; voice = channel */ +static void awe_voice_change(int voice, fx_affect_func func) +{ + int i; + switch (playing_mode) { + case AWE_PLAY_DIRECT: + func(voice, FALSE); + break; + case AWE_PLAY_INDIRECT: + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == AWE_VOICE_KEY(voice)) + func(i, FALSE); + break; + default: + for (i = 0; i < awe_max_voices; i++) + if (KEY_CHAN_MATCH(voices[i].key, voice)) + func(i, FALSE); + break; + } } @@ -1520,14 +1727,13 @@ awe_reset(dev); /* clear sample position flag */ - loaded_once = 0; + patch_opened = 0; /* set default mode */ - init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */ - awe_gus_bank = AWE_DEFAULT_BANK; + awe_init_misc_modes(FALSE); + init_atten = misc_modes[AWE_MD_ZERO_ATTEN]; drum_flags = DEFAULT_DRUM_FLAGS; - awe_exclusive_sound = TRUE; - awe_channel_mode = 0; + playing_mode = AWE_PLAY_INDIRECT; return 0; } @@ -1544,6 +1750,19 @@ } +/* set miscellaneous mode parameters + */ +static void +awe_init_misc_modes(int init_all) +{ + int i; + for (i = 0; i < AWE_MD_END; i++) { + if (init_all || misc_modes_default[i].init_each_time) + misc_modes[i] = misc_modes_default[i].value; + } +} + + /* sequencer I/O control: */ static int @@ -1551,7 +1770,10 @@ { switch (cmd) { case SNDCTL_SYNTH_INFO: - awe_info.nr_voices = awe_max_voices; + if (playing_mode == AWE_PLAY_DIRECT) + awe_info.nr_voices = awe_max_voices; + else + awe_info.nr_voices = AWE_MAX_CHANNELS; IOCTL_TO_USER((char*)arg, 0, &awe_info, sizeof(awe_info)); return 0; break; @@ -1568,62 +1790,104 @@ break; case SNDCTL_SYNTH_MEMAVL: - DEBUG(0,printk("AWE32: [ioctl memavl = %d]\n", (int)free_mem_ptr)); - return awe_mem_size - free_mem_ptr*2; + return awe_mem_size - awe_free_mem_ptr() * 2; default: - ERRMSG(printk("AWE32: unsupported ioctl %d\n", cmd)); + printk("AWE32: unsupported ioctl %d\n", cmd); return RET_ERROR(EINVAL); } } +static int voice_in_range(int voice) +{ + if (playing_mode == AWE_PLAY_DIRECT) { + if (voice < 0 || voice >= awe_max_voices) + return FALSE; + } else { + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return FALSE; + } + return TRUE; +} + +static void release_voice(int voice, int do_sustain) +{ + if (IS_NO_SOUND(voice)) + return; + if (do_sustain && (voices[voice].cinfo->sustained == 127 || + voices[voice].sostenuto == 127)) + voices[voice].state = AWE_ST_SUSTAINED; + else + awe_note_off(voice); +} + +/* release all notes */ +static void awe_note_off_all(int do_sustain) +{ + int i; + for (i = 0; i < awe_max_voices; i++) + release_voice(i, do_sustain); +} + /* kill a voice: * not terminate, just release the voice. */ static int awe_kill_note(int dev, int voice, int note, int velocity) { - int i, key, besttime; + int i, v2, key; DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity)); - if (awe_channel_mode) { - if (awe_channel_mode == 2) { /* get channel */ - int v2 = voice_alloc->map[voice] >> 8; - voice_alloc->map[voice] = 0; - voice = v2; - } + if (! voice_in_range(voice)) + return RET_ERROR(EINVAL); + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + key = AWE_VOICE_KEY(voice); + break; + + case AWE_PLAY_MULTI2: + v2 = voice_alloc->map[voice] >> 8; + voice_alloc->map[voice] = 0; + voice = v2; if (voice < 0 || voice >= AWE_MAX_CHANNELS) return RET_ERROR(EINVAL); - if (channels[voice].instr > 128) - note = channels[voice].instr - 128; + /* continue to below */ + default: key = AWE_CHAN_KEY(voice, note); - } else { - if (voice < 0 || voice >= awe_max_voices) - return RET_ERROR(EINVAL); - key = AWE_VOICE_KEY(voice); + break; } - besttime = current_alloc_time + 1; - for (i = 0; i < awe_max_voices; i++) { - if (voices[i].key == key) { - if (voices[i].time < besttime) - besttime = voices[i].time; - } - } for (i = 0; i < awe_max_voices; i++) { - if (voices[i].key == key && - (!awe_exclusive_sound || voices[i].time == besttime)) { - if (voices[i].cinfo->sustained) - voices[i].state = AWE_ST_SUSTAINED; - else - awe_note_off(i); - } + if (voices[i].key == key) + release_voice(i, TRUE); } return 0; } +static void start_or_volume_change(int voice, int velocity) +{ + voices[voice].velocity = velocity; + awe_calc_volume(voice); + if (voices[voice].state == AWE_ST_STANDBY) + awe_note_on(voice); + else if (voices[voice].state == AWE_ST_ON) + awe_set_volume(voice, FALSE); +} + +static void set_and_start_voice(int voice, int state) +{ + /* calculate pitch & volume parameters */ + voices[voice].state = state; + awe_calc_pitch(voice); + awe_calc_volume(voice); + if (state == AWE_ST_ON) + awe_note_on(voice); +} + /* start a voice: * if note is 255, identical with aftertouch function. * Otherwise, start a voice with specified not and volume. @@ -1634,107 +1898,87 @@ int i, key, state, volonly; DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity)); - volonly = 0; - if (awe_channel_mode) { - if (awe_channel_mode == 2) /* get channel */ - voice = voice_alloc->map[voice] >> 8; - else if (voice & 0x80) { /* channel volume mode */ - voice &= ~0x80; - volonly = 2; - } + if (! voice_in_range(voice)) + return RET_ERROR(EINVAL); + + if (velocity == 0) + state = AWE_ST_STANDBY; /* stand by for playing */ + else + state = AWE_ST_ON; /* really play */ + volonly = FALSE; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + key = AWE_VOICE_KEY(voice); + if (note == 255) + volonly = TRUE; + break; + + case AWE_PLAY_MULTI2: + voice = voice_alloc->map[voice] >> 8; if (voice < 0 || voice >= AWE_MAX_CHANNELS) return RET_ERROR(EINVAL); - if (channels[voice].instr > 128) - note = channels[voice].instr - 128; + /* continue to below */ + default: if (note >= 128) { /* key volume mode */ note -= 128; - volonly = 1; + volonly = TRUE; } key = AWE_CHAN_KEY(voice, note); - } else { - if (voice < 0 || voice >= awe_max_voices) - return RET_ERROR(EINVAL); - if (voices[voice].cinfo->instr > 128) - note = voices[voice].cinfo->instr - 128; - key = AWE_VOICE_KEY(voice); - if (note == 255) - volonly = 1; + break; } /* dynamic volume change */ if (volonly) { for (i = 0; i < awe_max_voices; i++) { - if ((volonly == 2 && voices[i].ch == voice) || - voices[i].key == key) { - voices[i].velocity = velocity; - if (velocity == 0) /* for GUS compatibility */ - return awe_kill_note(dev, voice, note, velocity); - awe_calc_volume(i); - state = voices[i].state; - voices[i].state = AWE_ST_ON; - if (state == AWE_ST_STANDBY) - awe_note_on(i); - else - awe_set_volume(i, FALSE); - } + if (voices[i].key == key) + start_or_volume_change(i, velocity); } return 0; } - /* stop the sound if still playing */ - if (awe_exclusive_sound) { - for (i = 0; i < awe_max_voices; i++) - if (voices[i].key == key && - voices[i].state != AWE_ST_OFF) + /* if the same note still playing, stop it */ + for (i = 0; i < awe_max_voices; i++) + if (voices[i].key == key) { + if (voices[i].state == AWE_ST_ON) awe_note_off(i); - } + else if (voices[i].state == AWE_ST_STANDBY) + awe_voice_init(i, FALSE); + } /* allocate voices */ - if (awe_channel_mode) - awe_alloc_multi_voices(voice, note, velocity); - else + if (playing_mode == AWE_PLAY_DIRECT) awe_alloc_one_voice(voice, note, velocity); - - /* turn off other voices (for drums) */ - for (i = 0; i < awe_max_voices; i++) - if (voices[i].key == key && voices[i].sample) - awe_exclusive_off(i, voices[i].sample->exclusiveClass); - - if (velocity == 0) - state = AWE_ST_STANDBY; /* stand by for playing */ else - state = AWE_ST_ON; /* really play */ + awe_alloc_multi_voices(voice, note, velocity, key); - /* set up pitch and volume parameters */ + /* turn off other voices exlusively (for drums) */ for (i = 0; i < awe_max_voices; i++) - if (voices[i].key == key) { - /* calculate pitch & volume parameters */ - voices[i].state = state; - voices[i].note = note; - voices[i].velocity = velocity; - awe_calc_pitch(i); - awe_calc_volume(i); - if (state == AWE_ST_ON) - awe_note_on(i); - } + if (voices[i].key == key) + awe_exclusive_off(i); + + /* set up pitch and volume parameters */ + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key && voices[i].state == AWE_ST_OFF) + set_and_start_voice(i, state); + } return 0; } /* search instrument from preset table with the specified bank */ -static awe_voice_list * +static int awe_search_instr(int bank, int preset) { - awe_voice_list *p; - int maxc; + int i; - for (maxc = AWE_MAX_INFOS, p = preset_table[preset]; - p && maxc; p = p->next_bank, maxc--) { - if (p->bank == bank) - return p; + for (i = preset_table[preset]; i >= 0; i = infos[i].next_bank) { + if (infos[i].bank == bank) + return i; } - return NULL; + return -1; } @@ -1742,53 +1986,45 @@ static int awe_set_instr_2(int dev, int voice, int instr_no) { - if (awe_channel_mode == 2) + if (playing_mode == AWE_PLAY_MULTI2) { voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return RET_ERROR(EINVAL); + } return awe_set_instr(dev, voice, instr_no); } -/* assign the instrument to a voice */ +/* assign the instrument to a channel; voice is the channel number */ static int awe_set_instr(int dev, int voice, int instr_no) { awe_chan_info *cinfo; int def_bank; - if (awe_channel_mode) { - /* skip the percussion instr in SEQ2 mode */ - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return RET_ERROR(EINVAL); - cinfo = &channels[voice]; - if (IS_DRUM_CHANNEL(voice)) - def_bank = AWE_DRUM_BANK; - else - def_bank = cinfo->bank; - } else { - if (voice < 0 || voice >= awe_max_voices) - return RET_ERROR(EINVAL); - cinfo = voices[voice].cinfo; - def_bank = cinfo->bank; - } + if (! voice_in_range(voice)) + return RET_ERROR(EINVAL); if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS) return RET_ERROR(EINVAL); - cinfo->vrec = NULL; - cinfo->def_vrec = NULL; - if (instr_no > 128) { - cinfo->vrec = awe_search_instr(128, cinfo->bank); - if (cinfo->bank != 0) - cinfo->def_vrec = awe_search_instr(128, 0); - } else { - cinfo->vrec = awe_search_instr(def_bank, instr_no); - if (def_bank == AWE_DRUM_BANK) - cinfo->def_vrec = awe_search_instr(def_bank, 0); - else - cinfo->def_vrec = awe_search_instr(AWE_DEFAULT_BANK, instr_no); + cinfo = &channels[voice]; + def_bank = cinfo->bank; + + if (MULTI_LAYER_MODE()) { + if (IS_DRUM_CHANNEL(voice)) + def_bank = AWE_DRUM_BANK; } - if (cinfo->vrec == NULL && cinfo->def_vrec == NULL) { + + cinfo->vrec = -1; + cinfo->def_vrec = -1; + cinfo->vrec = awe_search_instr(def_bank, instr_no); + if (def_bank == AWE_DRUM_BANK) + cinfo->def_vrec = awe_search_instr(def_bank, misc_modes[AWE_MD_DEF_DRUM]); + else + cinfo->def_vrec = awe_search_instr(misc_modes[AWE_MD_DEF_BANK], instr_no); + + if (cinfo->vrec < 0 && cinfo->def_vrec < 0) { DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no)); - return 0; } cinfo->instr = instr_no; @@ -1804,10 +2040,10 @@ int i; current_alloc_time = 0; /* don't turn off voice 31 and 32. they are used also for FM voices */ - for (i = 0; i < AWE_NORMAL_VOICES; i++) + for (i = 0; i < awe_max_voices; i++) awe_terminate(i); for (i = 0; i < AWE_MAX_CHANNELS; i++) - awe_channel_init(i); + awe_channel_init(i, TRUE); for (i = 0; i < 16; i++) { awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127; awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127; @@ -1839,55 +2075,38 @@ static void awe_hw_gus_control(int dev, int cmd, unsigned char *event) { - int voice; + int voice, i, key; unsigned short p1; short p2; int plong; + if (MULTI_LAYER_MODE()) + return; + if (cmd == _GUS_NUMVOICES) + return; + voice = event[3]; + if (! voice_in_range(voice)) + return; + p1 = *(unsigned short *) &event[4]; p2 = *(short *) &event[6]; plong = *(int*) &event[4]; switch (cmd) { - case _GUS_NUMVOICES: - if (p1 >= awe_max_voices) - DEBUG(0,printk("AWE32: num_voices: voices out of range %d\n", p1)); - break; case _GUS_VOICESAMPLE: - if (voice < awe_max_voices) - awe_set_instr(dev, voice, p1); - break; - - case _GUS_VOICEON: - if (voice < awe_max_voices) - awe_note_on(voice); - break; - - case _GUS_VOICEOFF: - if (voice < awe_max_voices) - awe_note_off(voice); - break; - - case _GUS_VOICEMODE: - /* not supported */ - break; + awe_set_instr(dev, voice, p1); + return; case _GUS_VOICEBALA: /* 0 to 15 --> -128 to 127 */ - if (voice < awe_max_voices) - awe_panning(dev, voice, ((int)p1 << 4) - 128); - break; + awe_panning(dev, voice, ((int)p1 << 4) - 128); + return; - case _GUS_VOICEFREQ: - if (voice < awe_max_voices) - awe_calc_pitch_from_freq(voice, plong); - break; - case _GUS_VOICEVOL: case _GUS_VOICEVOL2: /* not supported yet */ - break; + return; case _GUS_RAMPRANGE: case _GUS_RAMPRATE: @@ -1895,19 +2114,40 @@ case _GUS_RAMPON: case _GUS_RAMPOFF: /* volume ramping not supported */ - break; + return; case _GUS_VOLUME_SCALE: - break; + return; case _GUS_VOICE_POS: - if (voice < awe_max_voices) { - FX_SET(&voices[voice].cinfo->fx, AWE_FX_SAMPLE_START, - (short)(plong & 0x7fff)); - FX_SET(&voices[voice].cinfo->fx, AWE_FX_COARSE_SAMPLE_START, - (plong >> 15) & 0xffff); + FX_SET(&channels[voice].fx, AWE_FX_SAMPLE_START, + (short)(plong & 0x7fff)); + FX_SET(&channels[voice].fx, AWE_FX_COARSE_SAMPLE_START, + (plong >> 15) & 0xffff); + return; + } + + key = AWE_VOICE_KEY(voice); + for (i = 0; i < awe_max_voices; i++) { + if (voices[i].key == key) { + switch (cmd) { + case _GUS_VOICEON: + awe_note_on(i); + break; + + case _GUS_VOICEOFF: + awe_terminate(i); + break; + + case _GUS_VOICEFADE: + awe_note_off(i); + break; + + case _GUS_VOICEFREQ: + awe_calc_pitch_from_freq(i, plong); + break; + } } - break; } } @@ -1926,9 +2166,11 @@ /* lfo2: delay, freq, pitch */ NULL, awe_fx_fm2frq2, awe_fx_fm2frq2, /* global: initpitch, chorus, reverb, cutoff, filterQ */ - awe_set_voice_pitch, NULL, NULL, awe_fx_cutoff, NULL, + awe_set_voice_pitch, NULL, NULL, awe_set_volume, NULL, /* sample: start, loopstart, loopend */ - NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + /* global: attenuation */ + awe_set_volume, }; @@ -1939,49 +2181,41 @@ int voice; unsigned short p1; short p2; - int chn; awe_chan_info *cinfo; + FX_Rec *fx; int i; - chn = event[1]; voice = event[3]; - p1 = *(unsigned short *) &event[4]; - p2 = *(short *) &event[6]; - - if (awe_channel_mode) { + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; if (voice < 0 || voice >= AWE_MAX_CHANNELS) return; - cinfo = &channels[voice]; - } else { - if (voice < 0 || voice >= awe_max_voices) - return; - cinfo = voices[voice].cinfo; } + p1 = *(unsigned short *) &event[4]; + p2 = *(short *) &event[6]; + cinfo = &channels[voice]; + switch (cmd) { case _AWE_DEBUG_MODE: debug_mode = p1; printk("AWE32: debug mode = %d\n", debug_mode); break; case _AWE_REVERB_MODE: - if (p1 <= 7) { - reverb_mode = p1; - DEBUG(0,printk("AWE32: reverb mode %d\n", reverb_mode)); - awe_set_reverb_mode(reverb_mode); - } + awe_set_reverb_mode(p1); break; case _AWE_CHORUS_MODE: - if (p1 <= 7) { - chorus_mode = p1; - DEBUG(0,printk("AWE32: chorus mode %d\n", chorus_mode)); - awe_set_chorus_mode(chorus_mode); - } + awe_set_chorus_mode(p1); break; case _AWE_REMOVE_LAST_SAMPLES: DEBUG(0,printk("AWE32: remove last samples\n")); - awe_remove_samples(); + if (locked_sf_id > 0) + awe_remove_samples(locked_sf_id); break; case _AWE_INITIALIZE_CHIP: @@ -1989,19 +2223,37 @@ break; case _AWE_SEND_EFFECT: + fx = &cinfo->fx; + i = 0; /* set */ + if (p1 >= 0x100) { + int layer = (p1 >> 8); + if (layer >= 0 && layer < MAX_LAYERS) + fx = &cinfo->fx_layer[layer]; + p1 &= 0xff; + } + if (p1 & 0x40) i = 2; /* clear */ + if (p1 & 0x80) i = 1; /* add */ + p1 &= 0x3f; if (p1 < AWE_FX_END) { - FX_SET(&cinfo->fx, p1, p2); DEBUG(0,printk("AWE32: effects (%d) %d %d\n", voice, p1, cinfo->fx.val[p1])); - FX_SET(&cinfo->fx, p1, p2); - if (fx_realtime[p1]) { + if (i == 0) + FX_SET(fx, p1, p2); + else if (i == 1) + FX_ADD(fx, p1, p2); + else + FX_UNSET(fx, p1); + if (i != 2 && fx_realtime[p1]) { DEBUG(0,printk("AWE32: fx_realtime (%d)\n", voice)); awe_voice_change(voice, fx_realtime[p1]); } } break; + case _AWE_RESET_CHANNEL: + awe_channel_init(voice, !p1); + break; + case _AWE_TERMINATE_ALL: - DEBUG(0,printk("AWE32: terminate all\n")); awe_reset(0); break; @@ -2009,63 +2261,54 @@ awe_voice_change(voice, (fx_affect_func)awe_terminate); break; + case _AWE_RELEASE_ALL: + awe_note_off_all(FALSE); + break; case _AWE_NOTEOFF_ALL: - for (i = 0; i < awe_max_voices; i++) - awe_note_off(i); + awe_note_off_all(TRUE); break; case _AWE_INITIAL_VOLUME: DEBUG(0,printk("AWE32: init attenuation %d\n", p1)); - init_atten = p1; + if (p2 == 0) /* absolute value */ + init_atten = (short)p1; + else /* relative value */ + init_atten = misc_modes[AWE_MD_ZERO_ATTEN] + (short)p1; + if (init_atten < 0) init_atten = 0; for (i = 0; i < awe_max_voices; i++) awe_set_voice_vol(i, FALSE); break; - case _AWE_SET_GUS_BANK: - DEBUG(0,printk("AWE32: set gus bank %d\n", p1)); - awe_gus_bank = p1; + case _AWE_CHN_PRESSURE: + cinfo->chan_press = p1; + p1 = p1 * misc_modes[AWE_MD_MOD_SENSE] / 1200; + FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, p1); + awe_voice_change(voice, awe_fx_fmmod); + FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, p1); + awe_voice_change(voice, awe_fx_fm2frq2); break; - - /* v0.3 stuffs */ + case _AWE_CHANNEL_MODE: DEBUG(0,printk("AWE32: channel mode = %d\n", p1)); - awe_channel_mode = p1; + playing_mode = p1; awe_reset(0); break; case _AWE_DRUM_CHANNELS: DEBUG(0,printk("AWE32: drum flags = %x\n", p1)); - drum_flags = p1; + drum_flags = *(unsigned int*)&event[4]; + break; + + case _AWE_MISC_MODE: + DEBUG(0,printk("AWE32: misc mode = %d %d\n", p1, p2)); + if (p1 > AWE_MD_VERSION && p1 < AWE_MD_END) + misc_modes[p1] = p2; + break; + + case _AWE_EQUALIZER: + awe_equalizer((int)p1, (int)p2); break; - case _AWE_EXCLUSIVE_SOUND: - DEBUG(0,printk("AWE32: exclusive mode = %d\n", p1)); - awe_exclusive_sound = p1; - break; - - case _AWE_GET_CURRENT_MODE: - { - awe_mode_rec tmprec; - tmprec.base_addr = awe_base; - tmprec.mem_size = awe_mem_size / 2; - tmprec.max_voices = awe_max_voices; - tmprec.max_infos = AWE_MAX_INFOS; - tmprec.max_samples = AWE_MAX_SAMPLES; - tmprec.current_sf_id = current_sf_id; - tmprec.free_mem = free_mem_ptr; - tmprec.free_info = free_info; - tmprec.free_sample = free_sample; - tmprec.reverb_mode = reverb_mode; - tmprec.chorus_mode = chorus_mode; - tmprec.init_atten = init_atten; - tmprec.channel_mode = awe_channel_mode; - tmprec.gus_bank = awe_gus_bank; - tmprec.exclusive_sound = awe_exclusive_sound; - tmprec.drum_flags = drum_flags; - tmprec.debug_mode = debug_mode; - IOCTL_TO_USER(*(awe_mode_rec**)&event[4], 0, &tmprec, sizeof(tmprec)); - break; - } default: DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice)); break; @@ -2073,6 +2316,211 @@ } +/* voice pressure change */ +static void +awe_aftertouch(int dev, int voice, int pressure) +{ + int note; + + DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure)); + if (! voice_in_range(voice)) + return; + + switch (playing_mode) { + case AWE_PLAY_DIRECT: + case AWE_PLAY_INDIRECT: + awe_start_note(dev, voice, 255, pressure); + break; + case AWE_PLAY_MULTI2: + note = (voice_alloc->map[voice] & 0xff) - 1; + awe_start_note(dev, voice, note + 0x80, pressure); + break; + } +} + + +/* voice control change */ +static void +awe_controller(int dev, int voice, int ctrl_num, int value) +{ + int i; + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + cinfo = &channels[voice]; + + switch (ctrl_num) { + case CTL_BANK_SELECT: /* MIDI control #0 */ + DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value)); + cinfo->bank = value; + awe_set_instr(dev, voice, cinfo->instr); + break; + + case CTL_MODWHEEL: /* MIDI control #1 */ + DEBUG(2,printk("AWE32: [modwheel(%d) %d]\n", voice, value)); + i = value * misc_modes[AWE_MD_MOD_SENSE] / 1200; + FX_ADD(&cinfo->fx, AWE_FX_LFO1_PITCH, i); + awe_voice_change(voice, awe_fx_fmmod); + FX_ADD(&cinfo->fx, AWE_FX_LFO2_PITCH, i); + awe_voice_change(voice, awe_fx_fm2frq2); + break; + + case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */ + DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value)); + /* zero centered */ + cinfo->bender = value; + awe_voice_change(voice, awe_set_voice_pitch); + break; + + case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value)); + /* value = sense x 100 */ + cinfo->bender_range = value; + /* no audible pitch change yet.. */ + break; + + case CTL_EXPRESSION: /* MIDI control #11 */ + if (SINGLE_LAYER_MODE()) + value /= 128; + case CTRL_EXPRESSION: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value)); + /* 0 - 127 */ + cinfo->expression_vol = value; + awe_voice_change(voice, awe_set_voice_vol); + break; + + case CTL_PAN: /* MIDI control #10 */ + DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value)); + /* (0-127) -> signed 8bit */ + cinfo->panning = value * 2 - 128; + if (misc_modes[AWE_MD_REALTIME_PAN]) + awe_voice_change(voice, awe_set_pan); + break; + + case CTL_MAIN_VOLUME: /* MIDI control #7 */ + if (SINGLE_LAYER_MODE()) + value = (value * 100) / 16383; + case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */ + DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value)); + /* 0 - 127 */ + cinfo->main_vol = value; + awe_voice_change(voice, awe_set_voice_vol); + break; + + case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */ + DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value)); + FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2); + break; + + case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */ + DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value)); + FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2); + break; + +#ifdef AWE_ACCEPT_ALL_SOUNDS_CONTROLL + case 120: /* all sounds off */ + awe_note_off_all(FALSE); + break; + case 123: /* all notes off */ + awe_note_off_all(TRUE); + break; +#endif + + case CTL_SUSTAIN: /* MIDI control #64 */ + cinfo->sustained = value; + if (value != 127) + awe_voice_change(voice, awe_sustain_off); + break; + + case CTL_SOSTENUTO: /* MIDI control #66 */ + if (value == 127) + awe_voice_change(voice, awe_sostenuto_on); + else + awe_voice_change(voice, awe_sustain_off); + break; + + default: + DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n", + voice, ctrl_num, value)); + break; + } +} + + +/* voice pan change (value = -128 - 127) */ +static void +awe_panning(int dev, int voice, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + cinfo = &channels[voice]; + cinfo->panning = value; + DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning)); + if (misc_modes[AWE_MD_REALTIME_PAN]) + awe_voice_change(voice, awe_set_pan); +} + + +/* volume mode change */ +static void +awe_volume_method(int dev, int mode) +{ + /* not impremented */ + DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode)); +} + + +#ifndef AWE_NO_PATCHMGR +/* patch manager */ +static int +awe_patchmgr(int dev, struct patmgr_info *rec) +{ + printk("AWE32 Warning: patch manager control not supported\n"); + return 0; +} +#endif + + +/* pitch wheel change: 0-16384 */ +static void +awe_bender(int dev, int voice, int value) +{ + awe_chan_info *cinfo; + + if (! voice_in_range(voice)) + return; + + if (playing_mode == AWE_PLAY_MULTI2) { + voice = voice_alloc->map[voice] >> 8; + if (voice < 0 || voice >= AWE_MAX_CHANNELS) + return; + } + + /* convert to zero centered value */ + cinfo = &channels[voice]; + cinfo->bender = value - 8192; + DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender)); + awe_voice_change(voice, awe_set_voice_pitch); +} + + /*---------------------------------------------------------------- * load a sound patch: * three types of patches are accepted: AWE, GUS, and SYSEX. @@ -2094,12 +2542,12 @@ /* no system exclusive message supported yet */ return 0; } else if (format != AWE_PATCH) { - FATALERR(printk("AWE32 Error: Invalid patch format (key) 0x%x\n", format)); + printk("AWE32 Error: Invalid patch format (key) 0x%x\n", format); return RET_ERROR(EINVAL); } if (count < AWE_PATCH_INFO_SIZE) { - FATALERR(printk("AWE32 Error: Patch header too short\n")); + printk("AWE32 Error: Patch header too short\n"); return RET_ERROR(EINVAL); } COPY_FROM_USER(((char*)&patch) + offs, addr, offs, @@ -2107,27 +2555,43 @@ count -= AWE_PATCH_INFO_SIZE; if (count < patch.len) { - FATALERR(printk("AWE32 Warning: Patch record too short (%d<%d)\n", - count, (int)patch.len)); - patch.len = count; + printk("AWE32: sample: Patch record too short (%d<%d)\n", + count, patch.len); + return RET_ERROR(EINVAL); } switch (patch.type) { case AWE_LOAD_INFO: - rc = awe_load_info(&patch, addr); + rc = awe_load_info(&patch, addr, count); break; - case AWE_LOAD_DATA: - rc = awe_load_data(&patch, addr); - /* - if (!pmgr_flag && rc == 0) - pmgr_inform(dev, PM_E_PATCH_LOADED, instr, free_sample, 0, 0); - */ + rc = awe_load_data(&patch, addr, count); + break; + case AWE_OPEN_PATCH: + rc = awe_open_patch(&patch, addr, count); + break; + case AWE_CLOSE_PATCH: + rc = awe_close_patch(&patch, addr, count); + break; + case AWE_UNLOAD_PATCH: + rc = awe_unload_patch(&patch, addr, count); + break; + case AWE_REPLACE_DATA: + rc = awe_replace_data(&patch, addr, count); + break; + case AWE_MAP_PRESET: + rc = awe_load_map(&patch, addr, count); + break; + case AWE_LOAD_CHORUS_FX: + rc = awe_load_chorus_fx(&patch, addr, count); + break; + case AWE_LOAD_REVERB_FX: + rc = awe_load_reverb_fx(&patch, addr, count); break; default: - FATALERR(printk("AWE32 Error: unknown patch format type %d\n", - patch.type)); + printk("AWE32 Error: unknown patch format type %d\n", + patch.type); rc = RET_ERROR(EINVAL); } @@ -2135,140 +2599,384 @@ } +/* create an sflist record */ +static int +awe_create_sf(int type, char *name) +{ + sf_list *rec; + + /* terminate sounds */ + awe_reset(0); + if (current_sf_id >= max_sfs) { + int newsize = max_sfs + AWE_MAX_SF_LISTS; + sf_list *newlist = my_realloc(sflists, sizeof(sf_list)*max_sfs, + sizeof(sf_list)*newsize); + if (newlist == NULL) + return 1; + sflists = newlist; + max_sfs = newsize; + } + rec = &sflists[current_sf_id]; + rec->sf_id = current_sf_id + 1; + rec->type = type; + if (current_sf_id == 0 || (type & AWE_PAT_LOCKED) != 0) + locked_sf_id = current_sf_id + 1; + /* + if (name) + MEMCPY(rec->name, name, AWE_PATCH_NAME_LEN); + else + BZERO(rec->name, AWE_PATCH_NAME_LEN); + */ + rec->num_info = awe_free_info(); + rec->num_sample = awe_free_sample(); + rec->mem_ptr = awe_free_mem_ptr(); + rec->infos = -1; + rec->samples = -1; + + current_sf_id++; + return 0; +} + + +/* open patch; create sf list and set opened flag */ +static int +awe_open_patch(awe_patch_info *patch, const char *addr, int count) +{ + awe_open_parm parm; + COPY_FROM_USER(&parm, addr, AWE_PATCH_INFO_SIZE, sizeof(parm)); + if (awe_create_sf(parm.type, parm.name)) { + printk("AWE32: can't open: failed to alloc new list\n"); + return RET_ERROR(ENOSPC); + } + patch_opened = TRUE; + return current_sf_id; +} + +/* check if the patch is already opened */ +static int +check_patch_opened(int type, char *name) +{ + if (! patch_opened) { + if (awe_create_sf(type, name)) { + printk("AWE32: failed to alloc new list\n"); + return RET_ERROR(ENOSPC); + } + patch_opened = TRUE; + return current_sf_id; + } + return current_sf_id; +} + +/* close the patch; if no voice is loaded, remove the patch */ +static int +awe_close_patch(awe_patch_info *patch, const char *addr, int count) +{ + if (patch_opened && current_sf_id > 0) { + /* if no voice is loaded, release the current patch */ + if (sflists[current_sf_id-1].infos == -1) + awe_remove_samples(current_sf_id - 1); + } + patch_opened = 0; + return 0; +} + + +/* remove the latest patch */ +static int +awe_unload_patch(awe_patch_info *patch, const char *addr, int count) +{ + if (current_sf_id > 0) + awe_remove_samples(current_sf_id - 1); + return 0; +} + +/* allocate voice info list records */ +static int alloc_new_info(int nvoices) +{ + int newsize, free_info; + awe_voice_list *newlist; + free_info = awe_free_info(); + if (free_info + nvoices >= max_infos) { + do { + newsize = max_infos + AWE_MAX_INFOS; + } while (free_info + nvoices >= newsize); + newlist = my_realloc(infos, sizeof(awe_voice_list)*max_infos, + sizeof(awe_voice_list)*newsize); + if (newlist == NULL) { + printk("AWE32: can't alloc info table\n"); + return RET_ERROR(ENOSPC); + } + infos = newlist; + max_infos = newsize; + } + return 0; +} + +/* allocate sample info list records */ +static int alloc_new_sample(void) +{ + int newsize, free_sample; + awe_sample_list *newlist; + free_sample = awe_free_sample(); + if (free_sample >= max_samples) { + newsize = max_samples + AWE_MAX_SAMPLES; + newlist = my_realloc(samples, + sizeof(awe_sample_list)*max_samples, + sizeof(awe_sample_list)*newsize); + if (newlist == NULL) { + printk("AWE32: can't alloc sample table\n"); + return RET_ERROR(ENOSPC); + } + samples = newlist; + max_samples = newsize; + } + return 0; +} + +/* load voice map */ +static int +awe_load_map(awe_patch_info *patch, const char *addr, int count) +{ + awe_voice_map map; + awe_voice_list *rec; + int free_info; + + if (check_patch_opened(AWE_PAT_TYPE_MAP, NULL) < 0) + return RET_ERROR(ENOSPC); + if (alloc_new_info(1) < 0) + return RET_ERROR(ENOSPC); + + COPY_FROM_USER(&map, addr, AWE_PATCH_INFO_SIZE, sizeof(map)); + + free_info = awe_free_info(); + rec = &infos[free_info]; + rec->bank = map.map_bank; + rec->instr = map.map_instr; + rec->type = V_ST_MAPPED; + rec->disabled = FALSE; + awe_init_voice_info(&rec->v); + if (map.map_key >= 0) { + rec->v.low = map.map_key; + rec->v.high = map.map_key; + } + rec->v.start = map.src_instr; + rec->v.end = map.src_bank; + rec->v.fixkey = map.src_key; + rec->v.sf_id = current_sf_id; + add_info_list(free_info); + add_sf_info(free_info); + + return 0; +} + /* load voice information data */ static int -awe_load_info(awe_patch_info *patch, const char *addr) +awe_load_info(awe_patch_info *patch, const char *addr, int count) { - awe_voice_list *rec, *curp; - long offset; - short i, nvoices; - unsigned char bank, instr; + int offset; + awe_voice_rec_hdr hdr; + int i; int total_size; - if (patch->len < AWE_VOICE_REC_SIZE) { - FATALERR(printk("AWE32 Error: invalid patch info length\n")); + if (count < AWE_VOICE_REC_SIZE) { + printk("AWE32 Error: invalid patch info length\n"); return RET_ERROR(EINVAL); } offset = AWE_PATCH_INFO_SIZE; - GET_BYTE_FROM_USER(bank, addr, offset); offset++; - GET_BYTE_FROM_USER(instr, addr, offset); offset++; - GET_SHORT_FROM_USER(nvoices, addr, offset); offset+=2; + COPY_FROM_USER((char*)&hdr, addr, offset, AWE_VOICE_REC_SIZE); + offset += AWE_VOICE_REC_SIZE; - if (nvoices <= 0 || nvoices >= 100) { - FATALERR(printk("AWE32 Error: Illegal voice number %d\n", nvoices)); + if (hdr.nvoices <= 0 || hdr.nvoices >= 100) { + printk("AWE32 Error: Illegal voice number %d\n", hdr.nvoices); return RET_ERROR(EINVAL); } - if (free_info + nvoices > AWE_MAX_INFOS) { - ERRMSG(printk("AWE32 Error: Too many voice informations\n")); - return RET_ERROR(ENOSPC); + total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * hdr.nvoices; + if (count < total_size) { + printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n", + count, hdr.nvoices); + return RET_ERROR(EINVAL); } - total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * nvoices; - if (patch->len < total_size) { - ERRMSG(printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n", - (int)patch->len, nvoices)); - return RET_ERROR(EINVAL); + if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0) + return RET_ERROR(ENOSPC); + +#if 0 /* it looks like not so useful.. */ + /* check if the same preset already exists in the info list */ + for (i = sflists[current_sf_id-1].infos; i >= 0; i = infos[i].next) { + if (infos[i].disabled) continue; + if (infos[i].bank == hdr.bank && infos[i].instr == hdr.instr) { + /* in exclusive mode, do skip loading this */ + if (hdr.write_mode == AWE_WR_EXCLUSIVE) + return 0; + /* in replace mode, disable the old data */ + else if (hdr.write_mode == AWE_WR_REPLACE) + infos[i].disabled = TRUE; + } } + if (hdr.write_mode == AWE_WR_REPLACE) + rebuild_preset_list(); +#endif + + if (alloc_new_info(hdr.nvoices) < 0) + return RET_ERROR(ENOSPC); - curp = awe_search_instr(bank, instr); - for (i = 0; i < nvoices; i++) { - rec = &infos[free_info + i]; + for (i = 0; i < hdr.nvoices; i++) { + int rec = awe_free_info(); - rec->bank = bank; - rec->instr = instr; - if (i < nvoices - 1) - rec->next_instr = rec + 1; - else - rec->next_instr = curp; - rec->next_bank = NULL; + infos[rec].bank = hdr.bank; + infos[rec].instr = hdr.instr; + infos[rec].type = V_ST_NORMAL; + infos[rec].disabled = FALSE; /* copy awe_voice_info parameters */ - COPY_FROM_USER(&rec->v, addr, offset, AWE_VOICE_INFO_SIZE); + COPY_FROM_USER(&infos[rec].v, addr, offset, AWE_VOICE_INFO_SIZE); offset += AWE_VOICE_INFO_SIZE; - rec->v.sf_id = current_sf_id; - if (rec->v.mode & AWE_MODE_INIT_PARM) - awe_init_voice_parm(&rec->v.parm); - awe_set_sample(&rec->v); + infos[rec].v.sf_id = current_sf_id; + if (infos[rec].v.mode & AWE_MODE_INIT_PARM) + awe_init_voice_parm(&infos[rec].v.parm); + awe_set_sample(&infos[rec].v); + add_info_list(rec); + add_sf_info(rec); } - /* prepend to top of the list */ - infos[free_info].next_bank = preset_table[instr]; - preset_table[instr] = &infos[free_info]; - free_info += nvoices; - return 0; } - /* load wave sample data */ static int -awe_load_data(awe_patch_info *patch, const char *addr) +awe_load_data(awe_patch_info *patch, const char *addr, int count) { - long offset; - int size; - int rc; + int offset, size; + int rc, free_sample; + awe_sample_info *rec; - if (free_sample >= AWE_MAX_SAMPLES) { - ERRMSG(printk("AWE32 Error: Sample table full\n")); + if (check_patch_opened(AWE_PAT_TYPE_MISC, NULL) < 0) return RET_ERROR(ENOSPC); - } - size = (patch->len - AWE_SAMPLE_INFO_SIZE) / 2; + if (alloc_new_sample() < 0) + return RET_ERROR(ENOSPC); + + free_sample = awe_free_sample(); + rec = &samples[free_sample].v; + + size = (count - AWE_SAMPLE_INFO_SIZE) / 2; offset = AWE_PATCH_INFO_SIZE; - COPY_FROM_USER(&samples[free_sample], addr, offset, - AWE_SAMPLE_INFO_SIZE); + COPY_FROM_USER(rec, addr, offset, AWE_SAMPLE_INFO_SIZE); offset += AWE_SAMPLE_INFO_SIZE; - if (size != samples[free_sample].size) { - ERRMSG(printk("AWE32 Warning: sample size differed (%d != %d)\n", - (int)samples[free_sample].size, (int)size)); - samples[free_sample].size = size; + if (size != rec->size) { + printk("AWE32: load: sample size differed (%d != %d)\n", + rec->size, size); + return RET_ERROR(EINVAL); } - if (samples[free_sample].size > 0) - if ((rc = awe_write_wave_data(addr, offset, size)) != 0) + if (rec->size > 0) + if ((rc = awe_write_wave_data(addr, offset, rec, -1)) != 0) return rc; - awe_check_loaded(); - samples[free_sample].sf_id = current_sf_id; + rec->sf_id = current_sf_id; + + add_sf_sample(free_sample); - free_sample++; return 0; } -/* check the other samples are already loaded */ -static void -awe_check_loaded(void) + +/* replace wave sample data */ +static int +awe_replace_data(awe_patch_info *patch, const char *addr, int count) { - if (!loaded_once) { - /* it's the first time */ - if (current_sf_id == 1) { - last_sample = free_sample; - last_info = free_info; - } - current_sf_id++; - loaded_once = 1; + int offset; + int size; + int rc, i; + int channels; + awe_sample_info cursmp; + int save_mem_ptr; + + if (! patch_opened) { + printk("AWE32: replace: patch not opened\n"); + return RET_ERROR(EINVAL); + } + + size = (count - AWE_SAMPLE_INFO_SIZE) / 2; + offset = AWE_PATCH_INFO_SIZE; + COPY_FROM_USER(&cursmp, addr, offset, AWE_SAMPLE_INFO_SIZE); + offset += AWE_SAMPLE_INFO_SIZE; + if (cursmp.size == 0 || size != cursmp.size) { + printk("AWE32: replace: illegal sample size (%d!=%d)\n", + cursmp.size, size); + return RET_ERROR(EINVAL); + } + channels = patch->optarg; + if (channels <= 0 || channels > AWE_NORMAL_VOICES) { + printk("AWE32: replace: illegal channels %d\n", channels); + return RET_ERROR(EINVAL); + } + + for (i = sflists[current_sf_id-1].samples; + i >= 0; i = samples[i].next) { + if (samples[i].v.sample == cursmp.sample) + break; + } + if (i < 0) { + printk("AWE32: replace: cannot find existing sample data %d\n", + cursmp.sample); + return RET_ERROR(EINVAL); + } + + if (samples[i].v.size != cursmp.size) { + printk("AWE32: replace: exiting size differed (%d!=%d)\n", + samples[i].v.size, cursmp.size); + return RET_ERROR(EINVAL); } + + save_mem_ptr = awe_free_mem_ptr(); + sflists[current_sf_id-1].mem_ptr = samples[i].v.start - awe_mem_start; + MEMCPY(&samples[i].v, &cursmp, sizeof(cursmp)); + if ((rc = awe_write_wave_data(addr, offset, &samples[i].v, channels)) != 0) + return rc; + sflists[current_sf_id-1].mem_ptr = save_mem_ptr; + samples[i].v.sf_id = current_sf_id; + + return 0; } /*----------------------------------------------------------------*/ static const char *readbuf_addr; -static long readbuf_offs; +static int readbuf_offs; static int readbuf_flags; +#ifdef __FreeBSD__ +static unsigned short *readbuf_loop; +static int readbuf_loopstart, readbuf_loopend; +#endif /* initialize read buffer */ -static void -awe_init_readbuf(const char *addr, long offset, int size, int mode_flags) +static int +readbuf_init(const char *addr, int offset, awe_sample_info *sp) { +#ifdef __FreeBSD__ + readbuf_loop = NULL; + readbuf_loopstart = sp->loopstart; + readbuf_loopend = sp->loopend; + if (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) { + int looplen = sp->loopend - sp->loopstart; + readbuf_loop = my_malloc(looplen * 2); + if (readbuf_loop == NULL) { + printk("AWE32: can't malloc temp buffer\n"); + return RET_ERROR(ENOSPC); + } + } +#endif readbuf_addr = addr; readbuf_offs = offset; - readbuf_flags = mode_flags; + readbuf_flags = sp->mode_flags; + return 0; } /* read directly from user buffer */ static unsigned short -awe_read_word(int pos) +readbuf_word(int pos) { unsigned short c; /* read from user buffer */ @@ -2281,10 +2989,43 @@ } if (readbuf_flags & AWE_SAMPLE_UNSIGNED) c ^= 0x8000; /* unsigned -> signed */ +#ifdef __FreeBSD__ + /* write on cache for reverse loop */ + if (readbuf_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP)) { + if (pos >= readbuf_loopstart && pos < readbuf_loopend) + readbuf_loop[pos - readbuf_loopstart] = c; + } +#endif return c; } +#ifdef __FreeBSD__ +/* read from cache */ +static unsigned short +readbuf_word_cache(int pos) +{ + if (pos >= readbuf_loopstart && pos < readbuf_loopend) + return readbuf_loop[pos - readbuf_loopstart]; + return 0; +} + +static void +readbuf_end(void) +{ + if (readbuf_loop) { + my_free(readbuf_loop); + } + readbuf_loop = NULL; +} + +#else + +#define readbuf_word_cache readbuf_word +#define readbuf_end() /**/ + +#endif +/*----------------------------------------------------------------*/ #define BLANK_LOOP_START 8 #define BLANK_LOOP_END 40 @@ -2292,28 +3033,26 @@ /* loading onto memory */ static int -awe_write_wave_data(const char *addr, long offset, int size) +awe_write_wave_data(const char *addr, int offset, awe_sample_info *sp, int channels) { - awe_sample_info *sp = &samples[free_sample]; - int i, truesize; + int i, truesize, dram_offset; int rc; - unsigned long csum1, csum2; /* be sure loop points start < end */ if (sp->loopstart > sp->loopend) { - long tmp = sp->loopstart; + int tmp = sp->loopstart; sp->loopstart = sp->loopend; sp->loopend = tmp; } /* compute true data size to be loaded */ - truesize = size; + truesize = sp->size; if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) truesize += sp->loopend - sp->loopstart; if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) truesize += BLANK_LOOP_SIZE; - if (size > 0 && free_mem_ptr + truesize >= awe_mem_size/2) { - ERRMSG(printk("AWE32 Error: Sample memory full\n")); + if (awe_free_mem_ptr() + truesize >= awe_mem_size/2) { + printk("AWE32 Error: Sample memory full\n"); return RET_ERROR(ENOSPC); } @@ -2321,32 +3060,36 @@ sp->end -= sp->start; sp->loopstart -= sp->start; sp->loopend -= sp->start; - sp->size = truesize; - sp->start = free_mem_ptr + AWE_DRAM_OFFSET; - sp->end += free_mem_ptr + AWE_DRAM_OFFSET; - sp->loopstart += free_mem_ptr + AWE_DRAM_OFFSET; - sp->loopend += free_mem_ptr + AWE_DRAM_OFFSET; + dram_offset = awe_free_mem_ptr() + awe_mem_start; + sp->start = dram_offset; + sp->end += dram_offset; + sp->loopstart += dram_offset; + sp->loopend += dram_offset; + + /* set the total size (store onto obsolete checksum value) */ + if (sp->size == 0) + sp->checksum = 0; + else + sp->checksum = truesize; - if ((rc = awe_open_dram_for_write(free_mem_ptr)) != 0) { + if ((rc = awe_open_dram_for_write(dram_offset, channels)) != 0) return rc; - } - awe_init_readbuf(addr, offset, size, sp->mode_flags); - csum1 = 0; - for (i = 0; i < size; i++) { + if (readbuf_init(addr, offset, sp) < 0) + return RET_ERROR(ENOSPC); + + for (i = 0; i < sp->size; i++) { unsigned short c; - c = awe_read_word(i); - csum1 += c; + c = readbuf_word(i); awe_write_dram(c); if (i == sp->loopend && (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) { int looplen = sp->loopend - sp->loopstart; /* copy reverse loop */ int k; - for (k = 0; k < looplen; k++) { - /* non-buffered data */ - c = awe_read_word(i - k); + for (k = 1; k <= looplen; k++) { + c = readbuf_word_cache(i - k); awe_write_dram(c); } if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) { @@ -2357,6 +3100,7 @@ } } } + readbuf_end(); /* if no blank loop is attached in the sample, add it */ if (sp->mode_flags & AWE_SAMPLE_NO_BLANK) { @@ -2366,40 +3110,14 @@ sp->loopstart = sp->end + BLANK_LOOP_START; sp->loopend = sp->end + BLANK_LOOP_END; } - sp->size += BLANK_LOOP_SIZE; } + sflists[current_sf_id-1].mem_ptr += truesize; awe_close_dram(); - if (sp->checksum_flag) { -#ifdef AWE_CHECKSUM_DATA - if (sp->checksum_flag != 2 && csum1 != sp->checksum) { - ERRMSG(printk("AWE32: [%d] checksum mismatch on data %x:%x\n", - free_sample, (int)sp->checksum, (int)csum1)); - return RET_ERROR(NO_DATA_ERR); - } -#endif /* AWE_CHECKSUM_DATA */ -#ifdef AWE_CHECKSUM_MEMORY - if (awe_open_dram_for_read(free_mem_ptr) == 0) { - csum2 = 0; - for (i = 0; i < size; i++) { - unsigned short c; - c = awe_peek(AWE_SMLD); - csum2 += c; - } - awe_close_dram_for_read(); - if (csum1 != csum2) { - ERRMSG(printk("AWE32: [%d] checksum mismatch on DRAM %x:%x\n", - free_sample, (int)csum1, (int)csum2)); - return RET_ERROR(NO_DATA_ERR); - } - } -#endif /* AWE_CHECKSUM_MEMORY */ - } - free_mem_ptr += sp->size; - /* re-initialize FM passthrough */ + /* initialize FM */ awe_init_fm(); - awe_tweak(); + /*awe_tweak();*/ return 0; } @@ -2435,91 +3153,89 @@ awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag) { struct patch_info patch; - awe_voice_list *rec, *curp; - long sizeof_patch; - int note; + awe_voice_info *rec; + awe_sample_info *smp; + int sizeof_patch; + int note, free_sample, free_info; int rc; - sizeof_patch = (long)&patch.data[0] - (long)&patch; /* header size */ - if (free_sample >= AWE_MAX_SAMPLES) { - ERRMSG(printk("AWE32 Error: Sample table full\n")); - return RET_ERROR(ENOSPC); - } - if (free_info >= AWE_MAX_INFOS) { - ERRMSG(printk("AWE32 Error: Too many voice informations\n")); - return RET_ERROR(ENOSPC); - } + sizeof_patch = (int)((long)&patch.data[0] - (long)&patch); /* header size */ if (size < sizeof_patch) { - ERRMSG(printk("AWE32 Error: Patch header too short\n")); + printk("AWE32 Error: Patch header too short\n"); return RET_ERROR(EINVAL); } COPY_FROM_USER(((char*)&patch) + offs, addr, offs, sizeof_patch - offs); size -= sizeof_patch; if (size < patch.len) { - FATALERR(printk("AWE32 Warning: Patch record too short (%d<%d)\n", - size, (int)patch.len)); - patch.len = size; + printk("AWE32 Warning: Patch record too short (%d<%d)\n", + size, patch.len); + return RET_ERROR(EINVAL); } + if (check_patch_opened(AWE_PAT_TYPE_GUS, NULL) < 0) + return RET_ERROR(ENOSPC); + if (alloc_new_sample() < 0) + return RET_ERROR(ENOSPC); + if (alloc_new_info(1)) + return RET_ERROR(ENOSPC); - samples[free_sample].sf_id = 0; - samples[free_sample].sample = free_sample; - samples[free_sample].start = 0; - samples[free_sample].end = patch.len; - samples[free_sample].loopstart = patch.loop_start; - samples[free_sample].loopend = patch.loop_end + 1; - samples[free_sample].size = patch.len; + free_sample = awe_free_sample(); + smp = &samples[free_sample].v; + + smp->sample = free_sample; + smp->start = 0; + smp->end = patch.len; + smp->loopstart = patch.loop_start; + smp->loopend = patch.loop_end; + smp->size = patch.len; /* set up mode flags */ - samples[free_sample].mode_flags = 0; + smp->mode_flags = 0; if (!(patch.mode & WAVE_16_BITS)) - samples[free_sample].mode_flags |= AWE_SAMPLE_8BITS; + smp->mode_flags |= AWE_SAMPLE_8BITS; if (patch.mode & WAVE_UNSIGNED) - samples[free_sample].mode_flags |= AWE_SAMPLE_UNSIGNED; - samples[free_sample].mode_flags |= AWE_SAMPLE_NO_BLANK; + smp->mode_flags |= AWE_SAMPLE_UNSIGNED; + smp->mode_flags |= AWE_SAMPLE_NO_BLANK; if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK))) - samples[free_sample].mode_flags |= AWE_SAMPLE_SINGLESHOT; + smp->mode_flags |= AWE_SAMPLE_SINGLESHOT; if (patch.mode & WAVE_BIDIR_LOOP) - samples[free_sample].mode_flags |= AWE_SAMPLE_BIDIR_LOOP; + smp->mode_flags |= AWE_SAMPLE_BIDIR_LOOP; if (patch.mode & WAVE_LOOP_BACK) - samples[free_sample].mode_flags |= AWE_SAMPLE_REVERSE_LOOP; + smp->mode_flags |= AWE_SAMPLE_REVERSE_LOOP; - DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, - samples[free_sample].mode_flags)); + DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no, smp->mode_flags)); if (patch.mode & WAVE_16_BITS) { /* convert to word offsets */ - samples[free_sample].size /= 2; - samples[free_sample].end /= 2; - samples[free_sample].loopstart /= 2; - samples[free_sample].loopend /= 2; + smp->size /= 2; + smp->end /= 2; + smp->loopstart /= 2; + smp->loopend /= 2; } - samples[free_sample].checksum_flag = 0; - samples[free_sample].checksum = 0; + smp->checksum_flag = 0; + smp->checksum = 0; - if ((rc = awe_write_wave_data(addr, sizeof_patch, - samples[free_sample].size)) != 0) + if ((rc = awe_write_wave_data(addr, sizeof_patch, smp, -1)) != 0) return rc; - awe_check_loaded(); - samples[free_sample].sf_id = current_sf_id; - free_sample++; + smp->sf_id = current_sf_id; + add_sf_sample(free_sample); /* set up voice info */ - rec = &infos[free_info]; - awe_init_voice_info(&rec->v); - rec->v.sf_id = current_sf_id; - rec->v.sample = free_sample - 1; /* the last sample */ - rec->v.rate_offset = calc_rate_offset(patch.base_freq); + free_info = awe_free_info(); + rec = &infos[free_info].v; + awe_init_voice_info(rec); + rec->sample = free_sample; /* the last sample */ + rec->rate_offset = calc_rate_offset(patch.base_freq); note = freq_to_note(patch.base_note); - rec->v.root = note / 100; - rec->v.tune = -(note % 100); - rec->v.low = freq_to_note(patch.low_note) / 100; - rec->v.high = freq_to_note(patch.high_note) / 100; + rec->root = note / 100; + rec->tune = -(note % 100); + rec->low = freq_to_note(patch.low_note) / 100; + rec->high = freq_to_note(patch.high_note) / 100; DEBUG(1,printk("AWE32: [gus base offset=%d, note=%d, range=%d-%d(%d-%d)]\n", - rec->v.rate_offset, note, - rec->v.low, rec->v.high, + rec->rate_offset, note, + rec->low, rec->high, patch.low_note, patch.high_note)); /* panning position; -128 - 127 => 0-127 */ - rec->v.pan = (patch.panning + 128) / 2; + rec->pan = (patch.panning + 128) / 2; /* detuning is ignored */ /* 6points volume envelope */ @@ -2542,367 +3258,215 @@ release += calc_gus_envelope_time (patch.env_rate[5], patch.env_offset[4], patch.env_offset[5]); - rec->v.parm.volatkhld = (calc_parm_attack(attack) << 8) | + rec->parm.volatkhld = (calc_parm_attack(attack) << 8) | calc_parm_hold(hold); - rec->v.parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | + rec->parm.voldcysus = (calc_gus_sustain(patch.env_offset[2]) << 8) | calc_parm_decay(decay); - rec->v.parm.volrelease = 0x8000 | calc_parm_decay(release); + rec->parm.volrelease = 0x8000 | calc_parm_decay(release); DEBUG(2,printk("AWE32: [gusenv atk=%d, hld=%d, dcy=%d, rel=%d]\n", attack, hold, decay, release)); - rec->v.attenuation = calc_gus_attenuation(patch.env_offset[0]); + rec->attenuation = calc_gus_attenuation(patch.env_offset[0]); } /* tremolo effect */ if (patch.mode & WAVE_TREMOLO) { int rate = (patch.tremolo_rate * 1000 / 38) / 42; - rec->v.parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; + rec->parm.tremfrq = ((patch.tremolo_depth / 2) << 8) | rate; DEBUG(2,printk("AWE32: [gusenv tremolo rate=%d, dep=%d, tremfrq=%x]\n", patch.tremolo_rate, patch.tremolo_depth, - rec->v.parm.tremfrq)); + rec->parm.tremfrq)); } /* vibrato effect */ if (patch.mode & WAVE_VIBRATO) { int rate = (patch.vibrato_rate * 1000 / 38) / 42; - rec->v.parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; + rec->parm.fm2frq2 = ((patch.vibrato_depth / 6) << 8) | rate; DEBUG(2,printk("AWE32: [gusenv vibrato rate=%d, dep=%d, tremfrq=%x]\n", patch.tremolo_rate, patch.tremolo_depth, - rec->v.parm.tremfrq)); + rec->parm.tremfrq)); } /* scale_freq, scale_factor, volume, and fractions not implemented */ - /* set the voice index */ - awe_set_sample(&rec->v); + /* append to the tail of the list */ + infos[free_info].bank = misc_modes[AWE_MD_GUS_BANK]; + infos[free_info].instr = patch.instr_no; + infos[free_info].disabled = FALSE; + infos[free_info].type = V_ST_NORMAL; + infos[free_info].v.sf_id = current_sf_id; + + add_info_list(free_info); + add_sf_info(free_info); - /* prepend to top of the list */ - curp = awe_search_instr(awe_gus_bank, patch.instr_no); - rec->bank = awe_gus_bank; - rec->instr = patch.instr_no; - rec->next_instr = curp; - rec->next_bank = preset_table[rec->instr]; - preset_table[rec->instr] = rec; - free_info++; + /* set the voice index */ + awe_set_sample(rec); return 0; } #endif /* AWE_HAS_GUS_COMPATIBILITY */ -/*----------------------------------------------------------------*/ - -/* remove samples with different sf_id from instrument list */ -static awe_voice_list * -awe_get_removed_list(awe_voice_list *curp) -{ - awe_voice_list *lastp, **prevp; - int maxc; - lastp = curp; - prevp = &lastp; - for (maxc = AWE_MAX_INFOS; - curp && maxc; curp = curp->next_instr, maxc--) { - if (curp->v.sf_id != 1) - *prevp = curp->next_instr; - else - prevp = &curp->next_instr; - } - return lastp; -} - - -/* remove last loaded samples */ -static void -awe_remove_samples(void) -{ - awe_voice_list **prevp, *p, *nextp; - int maxc; - int i; - - /* no sample is loaded yet */ - if (last_sample == free_sample && last_info == free_info) - return; - - /* only the primary samples are loaded */ - if (current_sf_id <= 1) - return; - - /* remove the records from preset table */ - for (i = 0; i < AWE_MAX_PRESETS; i++) { - prevp = &preset_table[i]; - for (maxc = AWE_MAX_INFOS, p = preset_table[i]; - p && maxc; p = nextp, maxc--) { - nextp = p->next_bank; - p = awe_get_removed_list(p); - if (p == NULL) - *prevp = nextp; - else { - *prevp = p; - prevp = &p->next_bank; - } - } - } - - for (i = last_sample; i < free_sample; i++) - free_mem_ptr -= samples[i].size; - - free_sample = last_sample; - free_info = last_info; - current_sf_id = 1; - loaded_once = 0; -} - +/*---------------------------------------------------------------- + * sample and voice list handlers + *----------------------------------------------------------------*/ -/* search the specified sample */ -static short -awe_set_sample(awe_voice_info *vp) +/* append this to the sf list */ +static void add_sf_info(int rec) { - int i; - for (i = 0; i < free_sample; i++) { - if (samples[i].sf_id == vp->sf_id && - samples[i].sample == vp->sample) { - /* set the actual sample offsets */ - vp->start += samples[i].start; - vp->end += samples[i].end; - vp->loopstart += samples[i].loopstart; - vp->loopend += samples[i].loopend; - /* copy mode flags */ - vp->mode = samples[i].mode_flags; - /* set index */ - vp->index = i; - return i; + int sf_id = infos[rec].v.sf_id; + if (sf_id == 0) return; + sf_id--; + if (sflists[sf_id].infos < 0) + sflists[sf_id].infos = rec; + else { + int i, prev; + prev = sflists[sf_id].infos; + while ((i = infos[prev].next) >= 0) + prev = i; + infos[prev].next = rec; + } + infos[rec].next = -1; + sflists[sf_id].num_info++; +} + +/* prepend this sample to sf list */ +static void add_sf_sample(int rec) +{ + int sf_id = samples[rec].v.sf_id; + if (sf_id == 0) return; + sf_id--; + samples[rec].next = sflists[sf_id].samples; + sflists[sf_id].samples = rec; + sflists[sf_id].num_sample++; +} + +/* purge the old records which don't belong with the same file id */ +static void purge_old_list(int rec, int next) +{ + infos[rec].next_instr = next; + if (infos[rec].bank == AWE_DRUM_BANK) { + /* remove samples with the same note range */ + int cur, *prevp = &infos[rec].next_instr; + int low = infos[rec].v.low; + int high = infos[rec].v.high; + for (cur = next; cur >= 0; cur = infos[cur].next_instr) { + if (infos[cur].v.low == low && + infos[cur].v.high == high && + infos[cur].v.sf_id != infos[rec].v.sf_id) + *prevp = infos[cur].next_instr; + prevp = &infos[cur].next_instr; } - } - return -1; -} - - -/* voice pressure change */ -static void -awe_aftertouch(int dev, int voice, int pressure) -{ - DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure)); - if (awe_channel_mode == 2) { - int note = (voice_alloc->map[voice] & 0xff) - 1; - awe_start_note(dev, voice, note + 0x80, pressure); - - } else if (awe_channel_mode == 0) { - if (voice < 0 || voice >= awe_max_voices) - return; - voices[voice].velocity = pressure; - awe_set_voice_vol(voice, FALSE); - } -} - - -/* voice control change */ -static void -awe_controller(int dev, int voice, int ctrl_num, int value) -{ - awe_chan_info *cinfo; - - if (awe_channel_mode) { - if (awe_channel_mode == 2) /* get channel */ - voice = voice_alloc->map[voice] >> 8; - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return; - cinfo = &channels[voice]; } else { - if (voice < 0 || voice >= awe_max_voices) - return; - cinfo = voices[voice].cinfo; - } - - switch (ctrl_num) { - case CTL_BANK_SELECT: /* SEQ1 control */ - DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value)); - cinfo->bank = value; - awe_set_instr(dev, voice, cinfo->instr); - break; - - case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */ - DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value)); - /* zero centered */ - cinfo->bender = value; - awe_voice_change(voice, awe_set_voice_pitch); - break; - - case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */ - DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value)); - /* value = sense x 100 */ - cinfo->bender_range = value; - /* no audible pitch change yet.. */ - break; - - case CTL_EXPRESSION: /* SEQ1 control */ - if (!awe_channel_mode) - value /= 128; - case CTRL_EXPRESSION: /* SEQ1 V2 control */ - DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value)); - /* 0 - 127 */ - cinfo->expression_vol = value; - awe_voice_change(voice, awe_set_voice_vol); - break; - - case CTL_PAN: /* SEQ1 control */ - DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value)); - /* (0-127) -> signed 8bit */ - cinfo->panning = value * 2 - 128; - awe_voice_change(voice, awe_set_pan); - break; - - case CTL_MAIN_VOLUME: /* SEQ1 control */ - if (!awe_channel_mode) - value = (value * 100) / 16383; - case CTRL_MAIN_VOLUME: /* SEQ1 V2 control */ - DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value)); - /* 0 - 127 */ - cinfo->main_vol = value; - awe_voice_change(voice, awe_set_voice_vol); - break; - - case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */ - DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value)); - FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2); - break; - - case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */ - DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value)); - FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2); - break; - -#ifdef AWE_ACCEPT_ALL_SOUNDS_CONTROLL - case 120: /* all sounds off */ - {int i; for (i = 0; i < AWE_NORMAL_VOICES; i++) - awe_terminate(i); - } - /*awe_reset(0);*/ - break; - case 123: /* all notes off */ - {int i; - for (i = 0; i < awe_max_voices; i++) - awe_note_off(i); - } - break; -#endif - - case CTL_SUSTAIN: /* sustain the channel */ - cinfo->sustained = value; - if (value == 0) - awe_voice_change(voice, awe_sustain_off); - break; - - default: - DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n", - voice, ctrl_num, value)); - break; - } -} - - - -/* change the voice parameters */ -static void awe_voice_change(int voice, fx_affect_func func) -{ - int i; - if (! awe_channel_mode) { - func(voice, FALSE); - return; + if (infos[next].v.sf_id != infos[rec].v.sf_id) + infos[rec].next_instr = -1; } - - for (i = 0; i < awe_max_voices; i++) - if (voices[i].ch == voice) - func(i, FALSE); } - -/* drop sustain */ -static void awe_sustain_off(int voice, int forced) +/* prepend to top of the preset table */ +static void add_info_list(int rec) { - if (voices[voice].state == AWE_ST_SUSTAINED) - awe_note_off(voice); -} + int *prevp, cur; + int instr = infos[rec].instr; + int bank = infos[rec].bank; - -/* voice pan change (value = -128 - 127) */ -static void -awe_panning(int dev, int voice, int value) -{ - awe_chan_info *cinfo; - if (awe_channel_mode) { - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return; - cinfo = &channels[voice]; - } else { - if (voice < 0 || voice >= awe_max_voices) + if (infos[rec].disabled) + return; + + prevp = &preset_table[instr]; + cur = *prevp; + while (cur >= 0) { + /* search the first record with the same bank number */ + if (infos[cur].bank == bank) { + /* replace the list with the new record */ + infos[rec].next_bank = infos[cur].next_bank; + *prevp = rec; + purge_old_list(rec, cur); return; - cinfo = voices[voice].cinfo; + } + prevp = &infos[cur].next_bank; + cur = infos[cur].next_bank; } - cinfo->panning = value; - DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning)); - awe_voice_change(voice, awe_set_pan); + /* this is the first bank record.. just add this */ + infos[rec].next_instr = -1; + infos[rec].next_bank = preset_table[instr]; + preset_table[instr] = rec; } - -/* volume mode change */ +/* remove samples later than the specified sf_id */ static void -awe_volume_method(int dev, int mode) +awe_remove_samples(int sf_id) { - /* not impremented */ - DEBUG(0,printk("AWE32: [volmethod mode=%d]\n", mode)); -} + if (sf_id <= 0) { + awe_reset_samples(); + return; + } + /* already removed? */ + if (current_sf_id <= sf_id) + return; + current_sf_id = sf_id; + if (locked_sf_id > sf_id) + locked_sf_id = sf_id; -#ifndef AWE_NO_PATCHMGR -/* patch manager */ -static int -awe_patchmgr(int dev, struct patmgr_info *rec) -{ - FATALERR(printk("AWE32 Warning: patch manager control not supported\n")); - return 0; + rebuild_preset_list(); } -#endif +/* rebuild preset search list */ +static void rebuild_preset_list(void) +{ + int i, j; -/* pitch wheel change: 0-16384 */ -static void -awe_bender(int dev, int voice, int value) + for (i = 0; i < AWE_MAX_PRESETS; i++) + preset_table[i] = -1; + + for (i = 0; i < current_sf_id; i++) { + for (j = sflists[i].infos; j >= 0; j = infos[j].next) + add_info_list(j); + } +} + +/* search the specified sample */ +static short +awe_set_sample(awe_voice_info *vp) { - awe_chan_info *cinfo; - if (awe_channel_mode) { - if (awe_channel_mode == 2) - voice = voice_alloc->map[voice] >> 8; - if (voice < 0 || voice >= AWE_MAX_CHANNELS) - return; - cinfo = &channels[voice]; - } else { - if (voice < 0 || voice >= awe_max_voices) - return; - cinfo = voices[voice].cinfo; + int i; + vp->index = -1; + for (i = sflists[vp->sf_id-1].samples; i >= 0; i = samples[i].next) { + if (samples[i].v.sample == vp->sample) { + /* set the actual sample offsets */ + vp->start += samples[i].v.start; + vp->end += samples[i].v.end; + vp->loopstart += samples[i].v.loopstart; + vp->loopend += samples[i].v.loopend; + /* copy mode flags */ + vp->mode = samples[i].v.mode_flags; + /* set index */ + vp->index = i; + return i; + } } - /* convert to zero centered value */ - cinfo->bender = value - 8192; - DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender)); - awe_voice_change(voice, awe_set_voice_pitch); + return -1; } -/* calculate the number of voices for this note & velocity */ + +/*---------------------------------------------------------------- + * voice allocation + *----------------------------------------------------------------*/ + +/* look for all voices associated with the specified note & velocity */ static int -awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist) +awe_search_multi_voices(int rec, int note, int velocity, awe_voice_info **vlist) { - int maxc; int nvoices; - unsigned short sf_id; - sf_id = current_sf_id; nvoices = 0; - for (maxc = AWE_MAX_INFOS; - rec && maxc; rec = rec->next_instr, maxc--) { - if (rec->v.low <= note && note <= rec->v.high && - velocity >= rec->v.vellow && velocity <= rec->v.velhigh) { - if (nvoices == 0) - sf_id = rec->v.sf_id; - else if (rec->v.sf_id != sf_id) - continue; - vlist[nvoices] = &rec->v; + for (; rec >= 0; rec = infos[rec].next_instr) { + if (note >= infos[rec].v.low && + note <= infos[rec].v.high && + velocity >= infos[rec].v.vellow && + velocity <= infos[rec].v.velhigh) { + vlist[nvoices] = &infos[rec].v; + if (infos[rec].type == V_ST_MAPPED) /* mapper */ + return -1; nvoices++; if (nvoices >= AWE_MAX_VOICES) break; @@ -2911,112 +3475,178 @@ return nvoices; } +/* store the voice list from the specified note and velocity. + if the preset is mapped, seek for the destination preset, and rewrite + the note number if necessary. + */ +static int +really_alloc_voices(int vrec, int def_vrec, int *note, int velocity, awe_voice_info **vlist, int level) +{ + int nvoices; + + nvoices = awe_search_multi_voices(vrec, *note, velocity, vlist); + if (nvoices == 0) + nvoices = awe_search_multi_voices(def_vrec, *note, velocity, vlist); + if (nvoices < 0) { /* mapping */ + int preset = vlist[0]->start; + int bank = vlist[0]->end; + int key = vlist[0]->fixkey; + if (level > 5) { + printk("AWE32: too deep mapping level\n"); + return 0; + } + vrec = awe_search_instr(bank, preset); + if (bank == AWE_DRUM_BANK) + def_vrec = awe_search_instr(bank, 0); + else + def_vrec = awe_search_instr(0, preset); + if (key >= 0) + *note = key; + return really_alloc_voices(vrec, def_vrec, note, velocity, vlist, level+1); + } + + return nvoices; +} + /* allocate voices corresponding note and velocity; supports multiple insts. */ static void -awe_alloc_multi_voices(int ch, int note, int velocity) +awe_alloc_multi_voices(int ch, int note, int velocity, int key) { int i, v, nvoices; awe_voice_info *vlist[AWE_MAX_VOICES]; - if (channels[ch].vrec == NULL && channels[ch].def_vrec == NULL) + if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0) awe_set_instr(0, ch, channels[ch].instr); - nvoices = awe_search_multi_voices(channels[ch].vrec, note, velocity, vlist); - if (nvoices == 0) - nvoices = awe_search_multi_voices(channels[ch].def_vrec, note, velocity, vlist); + /* check the possible voices; note may be changeable if mapped */ + nvoices = really_alloc_voices(channels[ch].vrec, channels[ch].def_vrec, + ¬e, velocity, vlist, 0); - /* allocate the voices */ + /* set the voices */ current_alloc_time++; for (i = 0; i < nvoices; i++) { v = awe_clear_voice(); - voices[v].key = AWE_CHAN_KEY(ch, note); + voices[v].key = key; voices[v].ch = ch; + voices[v].note = note; + voices[v].velocity = velocity; voices[v].time = current_alloc_time; voices[v].cinfo = &channels[ch]; voices[v].sample = vlist[i]; voices[v].state = AWE_ST_MARK; + voices[v].layer = nvoices - i - 1; /* in reverse order */ } /* clear the mark in allocated voices */ - for (i = 0; i < awe_max_voices; i++) + for (i = 0; i < awe_max_voices; i++) { if (voices[i].state == AWE_ST_MARK) voices[i].state = AWE_ST_OFF; + + } } -/* search an empty voice; used internally */ +/* search the best voice from the specified status condition */ static int -awe_clear_voice(void) +search_best_voice(int condition) { int i, time, best; - - /* looking for the oldest empty voice */ best = -1; - time = 0x7fffffff; + time = current_alloc_time + 1; for (i = 0; i < awe_max_voices; i++) { - if (voices[i].state == AWE_ST_OFF && voices[i].time < time) { + if ((voices[i].state & condition) && + (best < 0 || voices[i].time < time)) { best = i; time = voices[i].time; } } + /* clear voice */ if (best >= 0) - return best; + awe_terminate(best); + + return best; +} + +/* search an empty voice. + if no empty voice is found, at least terminate a voice + */ +static int +awe_clear_voice(void) +{ + int best; + /* looking for the oldest empty voice */ + if ((best = search_best_voice(AWE_ST_OFF)) >= 0) + return best; + if ((best = search_best_voice(AWE_ST_RELEASED)) >= 0) + return best; /* looking for the oldest sustained voice */ - time = 0x7fffffff; - for (i = 0; i < awe_max_voices; i++) { - if (voices[i].state == AWE_ST_SUSTAINED && - voices[i].time < time) { - best = i; - time = voices[i].time; - } - } - if (best >= 0) { - awe_note_off(best); + if ((best = search_best_voice(AWE_ST_SUSTAINED)) >= 0) return best; - } - /* looking for the oldest voice not marked */ - time = 0x7fffffff; - best = 0; - for (i = 0; i < awe_max_voices; i++) { - if (voices[i].state != AWE_ST_MARK && voices[i].time < time) { - best = i; - time = voices[i].time; +#ifdef AWE_LOOKUP_MIDI_PRIORITY + if (MULTI_LAYER_MODE() && misc_modes[AWE_MD_CHN_PRIOR]) { + int ch = -1; + int time = current_alloc_time + 1; + int i; + /* looking for the voices from high channel (except drum ch) */ + for (i = 0; i < awe_max_voices; i++) { + if (IS_DRUM_CHANNEL(voices[i].ch)) continue; + if (voices[i].ch < ch) continue; + if (voices[i].state != AWE_ST_MARK && + (voices[i].ch > ch || voices[i].time < time)) { + best = i; + time = voices[i].time; + ch = voices[i].ch; + } } } - /*awe_terminate(best);*/ - awe_note_off(best); - return best; +#endif + if (best < 0) + best = search_best_voice(~AWE_ST_MARK); + + if (best >= 0) + return best; + + return 0; } -/* allocate a voice corresponding note and velocity; single instrument */ +/* search sample for the specified note & velocity and set it on the voice; + * note that voice is the voice index (not channel index) + */ static void awe_alloc_one_voice(int voice, int note, int velocity) { - int nvoices; + int ch, nvoices; awe_voice_info *vlist[AWE_MAX_VOICES]; - if (voices[voice].cinfo->vrec == NULL && voices[voice].cinfo->def_vrec == NULL) - awe_set_instr(0, voice, voices[voice].cinfo->instr); - - nvoices = awe_search_multi_voices(voices[voice].cinfo->vrec, note, velocity, vlist); - if (nvoices == 0) - nvoices = awe_search_multi_voices(voices[voice].cinfo->def_vrec, note, velocity, vlist); + ch = voices[voice].ch; + if (channels[ch].vrec < 0 && channels[ch].def_vrec < 0) + awe_set_instr(0, ch, channels[ch].instr); + nvoices = really_alloc_voices(voices[voice].cinfo->vrec, + voices[voice].cinfo->def_vrec, + ¬e, velocity, vlist, 0); if (nvoices > 0) { voices[voice].time = ++current_alloc_time; voices[voice].sample = vlist[0]; /* use the first one */ + voices[voice].layer = 0; + voices[voice].note = note; + voices[voice].velocity = velocity; } } +/*---------------------------------------------------------------- + * sequencer2 functions + *----------------------------------------------------------------*/ + /* search an empty voice; used by sequencer2 */ static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc) { - awe_channel_mode = 2; + playing_mode = AWE_PLAY_MULTI2; return awe_clear_voice(); } @@ -3053,6 +3683,75 @@ } +#ifdef CONFIG_AWE32_MIXER +/*================================================================ + * AWE32 mixer device control + *================================================================*/ + +static int +awe_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) +{ + int i, level; + + if (((cmd >> 8) & 0xff) != 'M') + return RET_ERROR(EINVAL); + + level = (int)IOCTL_IN(arg); + level = ((level & 0xff) + (level >> 8)) / 2; + DEBUG(0,printk("AWEMix: cmd=%x val=%d\n", cmd & 0xff, level)); + + if (IO_WRITE_CHECK(cmd)) { + switch (cmd & 0xff) { + case SOUND_MIXER_BASS: + awe_bass_level = level * 12 / 100; + if (awe_bass_level >= 12) + awe_bass_level = 11; + awe_equalizer(awe_bass_level, awe_treble_level); + break; + case SOUND_MIXER_TREBLE: + awe_treble_level = level * 12 / 100; + if (awe_treble_level >= 12) + awe_treble_level = 11; + awe_equalizer(awe_bass_level, awe_treble_level); + break; + case SOUND_MIXER_VOLUME: + level = level * 127 / 100; + if (level >= 128) level = 127; + init_atten = vol_table[level]; + for (i = 0; i < awe_max_voices; i++) + awe_set_voice_vol(i, FALSE); + break; + } + } + switch (cmd & 0xff) { + case SOUND_MIXER_BASS: + level = awe_bass_level * 100 / 24; + level = (level << 8) | level; + break; + case SOUND_MIXER_TREBLE: + level = awe_treble_level * 100 / 24; + level = (level << 8) | level; + break; + case SOUND_MIXER_VOLUME: + for (i = 127; i > 0; i--) { + if (init_atten <= vol_table[i]) + break; + } + level = i * 100 / 127; + level = (level << 8) | level; + break; + case SOUND_MIXER_DEVMASK: + level = SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_VOLUME; + break; + default: + level = 0; + break; + } + return IOCTL_OUT(arg, level); +} +#endif /* CONFIG_AWE32_MIXER */ + + /*================================================================ * initialization of AWE32 *================================================================*/ @@ -3299,13 +3998,16 @@ awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 | (DEF_FM_CHORUS_DEPTH << 24)); awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8)); - awe_poke_dw(AWE_CPF(31), 0); + awe_poke_dw(AWE_CPF(31), 0x8000); awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3); /* skew volume & cutoff */ awe_poke_dw(AWE_VTFT(30), 0x8000FFFF); awe_poke_dw(AWE_VTFT(31), 0x8000FFFF); + voices[30].state = AWE_ST_FM; + voices[31].state = AWE_ST_FM; + /* change maximum channels to 30 */ awe_max_voices = AWE_NORMAL_VOICES; awe_info.nr_voices = awe_max_voices; @@ -3318,20 +4020,32 @@ /* open DRAM write accessing mode */ static int -awe_open_dram_for_write(int offset) +awe_open_dram_for_write(int offset, int channels) { + int vidx[AWE_NORMAL_VOICES]; int i; + if (channels < 0 || channels >= AWE_NORMAL_VOICES) { + channels = AWE_NORMAL_VOICES; + for (i = 0; i < AWE_NORMAL_VOICES; i++) + vidx[i] = i; + } else { + for (i = 0; i < channels; i++) + vidx[i] = awe_clear_voice(); + } + /* use all channels for DMA transfer */ - for (i = 0; i < AWE_NORMAL_VOICES; i++) { - awe_poke(AWE_DCYSUSV(i), 0x80); - awe_poke_dw(AWE_VTFT(i), 0); - awe_poke_dw(AWE_CVCF(i), 0); - awe_poke_dw(AWE_PTRX(i), 0x40000000); - awe_poke_dw(AWE_CPF(i), 0x40000000); - awe_poke_dw(AWE_PSST(i), 0); - awe_poke_dw(AWE_CSL(i), 0); - awe_poke_dw(AWE_CCCA(i), 0x06000000); + for (i = 0; i < channels; i++) { + if (vidx[i] < 0) continue; + awe_poke(AWE_DCYSUSV(vidx[i]), 0x80); + awe_poke_dw(AWE_VTFT(vidx[i]), 0); + awe_poke_dw(AWE_CVCF(vidx[i]), 0); + awe_poke_dw(AWE_PTRX(vidx[i]), 0x40000000); + awe_poke_dw(AWE_CPF(vidx[i]), 0x40000000); + awe_poke_dw(AWE_PSST(vidx[i]), 0); + awe_poke_dw(AWE_CSL(vidx[i]), 0); + awe_poke_dw(AWE_CCCA(vidx[i]), 0x06000000); + voices[vidx[i]].state = AWE_ST_DRAM; } /* point channels 31 & 32 to ROM samples for DRAM refresh */ awe_poke_dw(AWE_VTFT(30), 0); @@ -3342,16 +4056,20 @@ awe_poke_dw(AWE_PSST(31), 0x1d8); awe_poke_dw(AWE_CSL(31), 0x1e0); awe_poke_dw(AWE_CCCA(31), 0x1d8); + voices[30].state = AWE_ST_FM; + voices[31].state = AWE_ST_FM; /* if full bit is on, not ready to write on */ if (awe_peek_dw(AWE_SMALW) & 0x80000000) { - for (i = 0; i < AWE_NORMAL_VOICES; i++) - awe_poke_dw(AWE_CCCA(i), 0); + for (i = 0; i < channels; i++) { + awe_poke_dw(AWE_CCCA(vidx[i]), 0); + voices[i].state = AWE_ST_OFF; + } return RET_ERROR(ENOSPC); } /* set address to write */ - awe_poke_dw(AWE_SMALW, offset + AWE_DRAM_OFFSET); + awe_poke_dw(AWE_SMALW, offset); return 0; } @@ -3373,6 +4091,7 @@ awe_poke_dw(AWE_CCCA(i), 0x06000000); else /* DMA read */ awe_poke_dw(AWE_CCCA(i), 0x04000000); + voices[i].state = AWE_ST_DRAM; } } @@ -3390,79 +4109,14 @@ } for (i = 0; i < AWE_NORMAL_VOICES; i++) { - awe_poke_dw(AWE_CCCA(i), 0); - awe_poke(AWE_DCYSUSV(i), 0x807F); - } -} - - -#ifdef AWE_CHECKSUM_MEMORY -/* open DRAM read accessing mode */ -static int -awe_open_dram_for_read(int offset) -{ - int i; - - /* use all channels for DMA transfer */ - for (i = 0; i < AWE_NORMAL_VOICES; i++) { - awe_poke(AWE_DCYSUSV(i), 0x80); - awe_poke_dw(AWE_VTFT(i), 0); - awe_poke_dw(AWE_CVCF(i), 0); - awe_poke_dw(AWE_PTRX(i), 0x40000000); - awe_poke_dw(AWE_CPF(i), 0x40000000); - awe_poke_dw(AWE_PSST(i), 0); - awe_poke_dw(AWE_CSL(i), 0); - awe_poke_dw(AWE_CCCA(i), 0x04000000); - } - /* point channels 31 & 32 to ROM samples for DRAM refresh */ - awe_poke_dw(AWE_VTFT(30), 0); - awe_poke_dw(AWE_PSST(30), 0x1d8); - awe_poke_dw(AWE_CSL(30), 0x1e0); - awe_poke_dw(AWE_CCCA(30), 0x1d8); - awe_poke_dw(AWE_VTFT(31), 0); - awe_poke_dw(AWE_PSST(31), 0x1d8); - awe_poke_dw(AWE_CSL(31), 0x1e0); - awe_poke_dw(AWE_CCCA(31), 0x1d8); - - /* if empty flag is on, not ready to read */ - if (awe_peek_dw(AWE_SMALR) & 0x80000000) { - for (i = 0; i < AWE_NORMAL_VOICES; i++) + if (voices[i].state == AWE_ST_DRAM) { awe_poke_dw(AWE_CCCA(i), 0); - return RET_ERROR(ENOSPC); - } - - /* set address to read */ - awe_poke_dw(AWE_SMALR, offset + AWE_DRAM_OFFSET); - /* drop stale data */ - awe_peek(AWE_SMLD); - return 0; -} - -/* close dram access for read */ -static void -awe_close_dram_for_read(void) -{ - int i; - /* wait until FULL bit in SMAxW register be false */ - for (i = 0; i < 10000; i++) { - if (!(awe_peek_dw(AWE_SMALR) & 0x80000000)) - break; - awe_wait(10); - } - for (i = 0; i < AWE_NORMAL_VOICES; i++) { - awe_poke_dw(AWE_CCCA(i), 0); - awe_poke(AWE_DCYSUSV(i), 0x807F); + awe_poke(AWE_DCYSUSV(i), 0x807F); + voices[i].state = AWE_ST_OFF; + } } } -#endif /* AWE_CHECKSUM_MEMORY */ - -/* write a word data */ -static void -awe_write_dram(unsigned short c) -{ - awe_poke(AWE_SMLD, c); -} /*================================================================ * detect presence of AWE32 and check memory size @@ -3488,18 +4142,14 @@ awe_detect(void) { int base; -#ifdef AWE_DEFAULT_BASE_ADDR - if (awe_detect_base(AWE_DEFAULT_BASE_ADDR)) - return 1; -#endif if (awe_base == 0) { for (base = 0x620; base <= 0x680; base += 0x20) if (awe_detect_base(base)) return 1; + DEBUG(0,printk("AWE32 not found\n")); + return 0; } - FATALERR(printk("AWE32 not found\n")); - awe_base = 0; - return 0; + return 1; } @@ -3515,14 +4165,20 @@ static int awe_check_dram(void) { + if (awe_mem_size > 0) { + awe_mem_size *= 1024; /* convert to Kbytes */ + return awe_mem_size; + } + awe_open_dram_for_check(); + awe_mem_size = 0; + /* set up unique two id numbers */ awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET); awe_poke(AWE_SMLD, UNIQUE_ID1); awe_poke(AWE_SMLD, UNIQUE_ID2); - awe_mem_size = 0; while (awe_mem_size < AWE_MAX_DRAM_SIZE) { awe_wait(2); /* read a data on the DRAM start address */ @@ -3540,14 +4196,17 @@ */ awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L); awe_poke(AWE_SMLD, UNIQUE_ID3); + awe_wait(2); + /* read a data on the just written DRAM address */ + awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET + awe_mem_size*512L); + awe_peek(AWE_SMLD); /* discard stale data */ + if (awe_peek(AWE_SMLD) != UNIQUE_ID3) + break; } awe_close_dram(); - DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", (int)awe_mem_size)); -#ifdef AWE_DEFAULT_MEM_SIZE - if (awe_mem_size == 0) - awe_mem_size = AWE_DEFAULT_MEM_SIZE; -#endif + DEBUG(0,printk("AWE32: %d Kbytes memory detected\n", awe_mem_size)); + /* convert to Kbytes */ awe_mem_size *= 1024; return awe_mem_size; @@ -3559,26 +4218,49 @@ *================================================================*/ /* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ -static unsigned short chorus_parm[8][5] = { - {0xE600, 0x03F6, 0xBC2C ,0x0000, 0x006D}, /* chorus 1 */ - {0xE608, 0x031A, 0xBC6E, 0x0000, 0x017C}, /* chorus 2 */ - {0xE610, 0x031A, 0xBC84, 0x0000, 0x0083}, /* chorus 3 */ - {0xE620, 0x0269, 0xBC6E, 0x0000, 0x017C}, /* chorus 4 */ - {0xE680, 0x04D3, 0xBCA6, 0x0000, 0x005B}, /* feedback */ - {0xE6E0, 0x044E, 0xBC37, 0x0000, 0x0026}, /* flanger */ - {0xE600, 0x0B06, 0xBC00, 0xE000, 0x0083}, /* short delay */ - {0xE6C0, 0x0B06, 0xBC00, 0xE000, 0x0083}, /* short delay + feedback */ +static char chorus_defined[AWE_CHORUS_NUMBERS]; +static awe_chorus_fx_rec chorus_parm[AWE_CHORUS_NUMBERS] = { + {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ + {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ + {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ + {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ + {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ + {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ + {0xE600, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay */ + {0xE6C0, 0x0B06, 0xBC00, 0x0000E000, 0x00000083}, /* short delay + feedback */ }; -static void awe_set_chorus_mode(int effect) +static int +awe_load_chorus_fx(awe_patch_info *patch, const char *addr, int count) +{ + if (patch->optarg < AWE_CHORUS_PREDEFINED || patch->optarg >= AWE_CHORUS_NUMBERS) { + printk("AWE32 Error: illegal chorus mode %d for uploading\n", patch->optarg); + return RET_ERROR(EINVAL); + } + if (count < sizeof(awe_chorus_fx_rec)) { + printk("AWE32 Error: too short chorus fx parameters\n"); + return RET_ERROR(EINVAL); + } + COPY_FROM_USER(&chorus_parm[patch->optarg], addr, AWE_PATCH_INFO_SIZE, + sizeof(awe_chorus_fx_rec)); + chorus_defined[patch->optarg] = TRUE; + return 0; +} + +static void +awe_set_chorus_mode(int effect) { - awe_poke(AWE_INIT3(9), chorus_parm[effect][0]); - awe_poke(AWE_INIT3(12), chorus_parm[effect][1]); - awe_poke(AWE_INIT4(3), chorus_parm[effect][2]); - awe_poke_dw(AWE_HWCF4, (unsigned long)chorus_parm[effect][3]); - awe_poke_dw(AWE_HWCF5, (unsigned long)chorus_parm[effect][4]); + if (effect < 0 || effect >= AWE_CHORUS_NUMBERS || + (effect >= AWE_CHORUS_PREDEFINED && !chorus_defined[effect])) + return; + awe_poke(AWE_INIT3(9), chorus_parm[effect].feedback); + awe_poke(AWE_INIT3(12), chorus_parm[effect].delay_offset); + awe_poke(AWE_INIT4(3), chorus_parm[effect].lfo_depth); + awe_poke_dw(AWE_HWCF4, chorus_parm[effect].delay); + awe_poke_dw(AWE_HWCF5, chorus_parm[effect].lfo_freq); awe_poke_dw(AWE_HWCF6, 0x8000); awe_poke_dw(AWE_HWCF7, 0x0000); + chorus_mode = effect; } /*----------------------------------------------------------------*/ @@ -3586,55 +4268,56 @@ /* reverb mode settings; write the following 28 data of 16 bit length * on the corresponding ports in the reverb_cmds array */ -static unsigned short reverb_parm[8][28] = { -{ /* room 1 */ +static char reverb_defined[AWE_CHORUS_NUMBERS]; +static awe_reverb_fx_rec reverb_parm[AWE_REVERB_NUMBERS] = { +{{ /* room 1 */ 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, -}, -{ /* room 2 */ +}}, +{{ /* room 2 */ 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, -}, -{ /* room 3 */ +}}, +{{ /* room 3 */ 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, -}, -{ /* hall 1 */ +}}, +{{ /* hall 1 */ 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, -}, -{ /* hall 2 */ +}}, +{{ /* hall 2 */ 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, -}, -{ /* plate */ +}}, +{{ /* plate */ 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, -}, -{ /* delay */ +}}, +{{ /* delay */ 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, -}, -{ /* panning delay */ +}}, +{{ /* panning delay */ 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, -}, +}}, }; static struct ReverbCmdPair { @@ -3649,13 +4332,97 @@ {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, }; +static int +awe_load_reverb_fx(awe_patch_info *patch, const char *addr, int count) +{ + if (patch->optarg < AWE_REVERB_PREDEFINED || patch->optarg >= AWE_REVERB_NUMBERS) { + printk("AWE32 Error: illegal reverb mode %d for uploading\n", patch->optarg); + return RET_ERROR(EINVAL); + } + if (count < sizeof(awe_reverb_fx_rec)) { + printk("AWE32 Error: too short reverb fx parameters\n"); + return RET_ERROR(EINVAL); + } + COPY_FROM_USER(&reverb_parm[patch->optarg], addr, AWE_PATCH_INFO_SIZE, + sizeof(awe_reverb_fx_rec)); + reverb_defined[patch->optarg] = TRUE; + return 0; +} -static void awe_set_reverb_mode(int effect) +static void +awe_set_reverb_mode(int effect) { int i; + if (effect < 0 || effect >= AWE_REVERB_NUMBERS || + (effect >= AWE_REVERB_PREDEFINED && !reverb_defined[effect])) + return; for (i = 0; i < 28; i++) awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port, - reverb_parm[effect][i]); + reverb_parm[effect].parms[i]); + reverb_mode = effect; +} + +/*================================================================ + * treble/bass equalizer control + *================================================================*/ + +static unsigned short bass_parm[12][3] = { + {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ + {0xD25B, 0xD35B, 0x0000}, /* -8 */ + {0xD24C, 0xD34C, 0x0000}, /* -6 */ + {0xD23D, 0xD33D, 0x0000}, /* -4 */ + {0xD21F, 0xD31F, 0x0000}, /* -2 */ + {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ + {0xC219, 0xC319, 0x0001}, /* +2 */ + {0xC22A, 0xC32A, 0x0001}, /* +4 */ + {0xC24C, 0xC34C, 0x0001}, /* +6 */ + {0xC26E, 0xC36E, 0x0001}, /* +8 */ + {0xC248, 0xC348, 0x0002}, /* +10 */ + {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ +}; + +static unsigned short treble_parm[12][9] = { + {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ + {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, + {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ + {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, + {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +12 dB */ +}; + + +/* + * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] + */ +static void +awe_equalizer(int bass, int treble) +{ + unsigned short w; + + if (bass < 0 || bass > 11 || treble < 0 || treble > 11) + return; + awe_bass_level = bass; + awe_treble_level = treble; + awe_poke(AWE_INIT4(0x01), bass_parm[bass][0]); + awe_poke(AWE_INIT4(0x11), bass_parm[bass][1]); + awe_poke(AWE_INIT3(0x11), treble_parm[treble][0]); + awe_poke(AWE_INIT3(0x13), treble_parm[treble][1]); + awe_poke(AWE_INIT3(0x1B), treble_parm[treble][2]); + awe_poke(AWE_INIT4(0x07), treble_parm[treble][3]); + awe_poke(AWE_INIT4(0x0B), treble_parm[treble][4]); + awe_poke(AWE_INIT4(0x0D), treble_parm[treble][5]); + awe_poke(AWE_INIT4(0x17), treble_parm[treble][6]); + awe_poke(AWE_INIT4(0x19), treble_parm[treble][7]); + w = bass_parm[bass][2] + treble_parm[treble][8]; + awe_poke(AWE_INIT4(0x15), (unsigned short)(w + 0x0262)); + awe_poke(AWE_INIT4(0x1D), (unsigned short)(w + 0x8362)); } + #endif /* CONFIG_AWE32_SYNTH */ diff -u --recursive --new-file v2.1.62/linux/drivers/sound/mad16.c linux/drivers/sound/mad16.c --- v2.1.62/linux/drivers/sound/mad16.c Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/mad16.c Tue Sep 30 08:46:52 1997 @@ -60,7 +60,7 @@ #include "sound_config.h" -#if defined(CONFIG_MAD16) +#ifdef CONFIG_MAD16 #include "sb.h" @@ -297,8 +297,7 @@ if ((inb (hw_config->io_base + 3) & 0x3f) != 0x04 && (inb (hw_config->io_base + 3) & 0x3f) != 0x00) { - DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n", - hw_config->io_base, inb (hw_config->io_base + 3))); + DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb (hw_config->io_base + 3))); return 0; } diff -u --recursive --new-file v2.1.62/linux/drivers/sound/maui.c linux/drivers/sound/maui.c --- v2.1.62/linux/drivers/sound/maui.c Wed Sep 24 20:05:47 1997 +++ linux/drivers/sound/maui.c Tue Sep 30 08:46:52 1997 @@ -18,7 +18,7 @@ #include "sound_config.h" -#if defined(CONFIG_MAUI) +#ifdef CONFIG_MAUI static int maui_base = 0x330; @@ -95,8 +95,8 @@ maui_sleep_flag.opts |= WK_TIMEOUT; } maui_sleep_flag.opts &= ~WK_SLEEP; - } - if (signal_pending(current)) + }; + if ((current->signal & ~current->blocked)) { return 0; } @@ -321,8 +321,7 @@ if (count < header.len) { - printk ("Maui warning: Host command record too short (%d<%d)\n", - count, (int) header.len); + printk ("Maui warning: Host command record too short (%d<%d)\n", count, (int) header.len); header.len = count; } diff -u --recursive --new-file v2.1.62/linux/drivers/sound/midi_synth.c linux/drivers/sound/midi_synth.c --- v2.1.62/linux/drivers/sound/midi_synth.c Wed Sep 24 20:05:47 1997 +++ linux/drivers/sound/midi_synth.c Tue Sep 30 08:46:52 1997 @@ -18,7 +18,7 @@ #include "sound_config.h" -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI #define _MIDI_SYNTH_C_ @@ -89,7 +89,7 @@ break; default: - /* printk ("MPU: Unknown midi channel message %02x\n", msg[0]); */ + /* printk( "MPU: Unknown midi channel message %02x\n", msg[0]); */ ; } } @@ -231,8 +231,7 @@ break; /* MST_SYSEX */ default: - printk ("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, - (int) data); + printk ("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data); inc->m_state = MST_INIT; } } @@ -523,8 +522,7 @@ if (count < sysex.len) { - printk ("MIDI Warning: Sysex record too short (%d<%d)\n", - count, (int) sysex.len); + printk ("MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len); sysex.len = count; } @@ -533,7 +531,7 @@ sysex_sleep_flag.opts = WK_NONE; - for (i = 0; i < left && !signal_pending(current); i++) + for (i = 0; i < left && !(current->signal & ~current->blocked); i++) { unsigned char data; @@ -554,7 +552,7 @@ } while (!midi_devs[orig_dev]->outputc (orig_dev, (unsigned char) (data & 0xff)) && - !signal_pending(current)) + !(current->signal & ~current->blocked)) { unsigned long tlimit; diff -u --recursive --new-file v2.1.62/linux/drivers/sound/midibuf.c linux/drivers/sound/midibuf.c --- v2.1.62/linux/drivers/sound/midibuf.c Wed Sep 24 20:05:47 1997 +++ linux/drivers/sound/midibuf.c Tue Sep 30 08:46:52 1997 @@ -16,7 +16,7 @@ #include "sound_config.h" -#if defined(CONFIG_MIDI) +#ifdef CONFIG_MIDI /* * Don't make MAX_QUEUE_SIZE larger than 4000 @@ -93,7 +93,7 @@ */ if (midi_devs[dev]->buffer_status != NULL) - while (!signal_pending(current) && + while (!(current->signal & ~current->blocked) && midi_devs[dev]->buffer_status (dev)) { @@ -289,7 +289,7 @@ * devices */ - while (!signal_pending(current) && + while (!(current->signal & ~current->blocked) && DATA_AVAIL (midi_out_buf[dev])) { @@ -370,8 +370,8 @@ midi_sleep_flag[dev].opts |= WK_TIMEOUT; } midi_sleep_flag[dev].opts &= ~WK_SLEEP; - } - if (signal_pending(current)) + }; + if ((current->signal & ~current->blocked)) { restore_flags (flags); return -EINTR; @@ -430,7 +430,7 @@ } input_sleep_flag[dev].opts &= ~WK_SLEEP; }; - if (signal_pending(current)) + if ((current->signal & ~current->blocked)) c = -EINTR; /* * The user is getting restless */ diff -u --recursive --new-file v2.1.62/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c --- v2.1.62/linux/drivers/sound/mpu401.c Wed May 28 10:51:32 1997 +++ linux/drivers/sound/mpu401.c Tue Sep 30 08:46:53 1997 @@ -55,7 +55,7 @@ #define MBUF_MAX 10 #define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ - {printk("MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} + {printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} int m_busy; unsigned char m_buf[MBUF_MAX]; int m_ptr; @@ -213,7 +213,7 @@ default: if (midic <= 0xef) { - /* printk("mpu time: %d ", midic); */ + /* printk( "mpu time: %d ", midic); */ devc->m_state = ST_TIMED; } else @@ -229,7 +229,7 @@ if (msg < 8) /* Data byte */ { - /* printk("midi msg (running status) "); */ + /* printk( "midi msg (running status) "); */ msg = ((int) (devc->last_status & 0xf0) >> 4); msg -= 8; devc->m_left = len_tab[msg] - 1; @@ -252,15 +252,15 @@ switch (midic) { case 0xf8: - /* printk("NOP "); */ + /* printk( "NOP "); */ break; case 0xf9: - /* printk("meas end "); */ + /* printk( "meas end "); */ break; case 0xfc: - /* printk("data end "); */ + /* printk( "data end "); */ break; default: @@ -270,7 +270,7 @@ else { devc->last_status = midic; - /* printk ("midi msg "); */ + /* printk( "midi msg "); */ msg -= 8; devc->m_left = len_tab[msg]; @@ -309,7 +309,7 @@ break; case 0xf6: - /* printk("tune_request\n"); */ + /* printk( "tune_request\n"); */ devc->m_state = ST_INIT; /* @@ -342,7 +342,7 @@ break; case 0xff: - /* printk("midi hard reset"); */ + /* printk( "midi hard reset"); */ devc->m_state = ST_INIT; break; @@ -659,7 +659,7 @@ if (!ok) { restore_flags (flags); - /* printk ("MPU: No ACK to command (0x%x)\n", (int) cmd->cmd); */ + /* printk( "MPU: No ACK to command (0x%x)\n", (int) cmd->cmd); */ return -EIO; } @@ -693,7 +693,7 @@ if (!ok) { restore_flags (flags); - /* printk ("MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd); */ + /* printk( "MPU: No response(%d) to command (0x%x)\n", i, (int) cmd->cmd); */ return -EIO; } } @@ -1274,8 +1274,7 @@ if (check_region (hw_config->io_base, 2)) { - printk ("\n\nmpu401.c: I/O port %x already in use\n\n", - hw_config->io_base); + printk ("\n\nmpu401.c: I/O port %x already in use\n\n", hw_config->io_base); return 0; } diff -u --recursive --new-file v2.1.62/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c --- v2.1.62/linux/drivers/sound/opl3.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/opl3.c Tue Sep 30 08:46:53 1997 @@ -22,7 +22,7 @@ #include "sound_config.h" -#if defined(CONFIG_YM3812) +#ifdef CONFIG_YM3812 #include "opl3.h" @@ -111,8 +111,8 @@ { struct sbi_instrument ins; + printk ("Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); memcpy ((char *) &ins, (&((char *) arg)[0]), sizeof (ins)); - printk("Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { @@ -519,9 +519,7 @@ if (instr->channel < 0) { - printk ( - "OPL3: Initializing voice %d with undefined instrument\n", - voice); + printk ("OPL3: Initializing voice %d with undefined instrument\n", voice); return 0; } diff -u --recursive --new-file v2.1.62/linux/drivers/sound/opl3sa.c linux/drivers/sound/opl3sa.c --- v2.1.62/linux/drivers/sound/opl3sa.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/opl3sa.c Tue Sep 30 08:46:53 1997 @@ -0,0 +1,287 @@ +/* + * sound/Xopl3sa.c + * + * Low level driver for Yamaha YMF701B aka OPL3-SA chip + * + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +#include + +#undef SB_OK + +#include "sound_config.h" +#ifdef SB_OK +#include "sb.h" +static int sb_initialized = 0; + +#endif + +#ifdef CONFIG_OPL3SA1 + +static int kilroy_was_here = 0; /* Don't detect twice */ +static int mpu_initialized = 0; + +static int *opl3sa_osp = NULL; + +static unsigned char +opl3sa_read (int addr) +{ + unsigned long flags; + unsigned char tmp; + + save_flags (flags); + cli (); + outb ((0x1d), 0xf86); /* password */ + outb (((unsigned char) addr), 0xf86); /* address */ + tmp = inb (0xf87); /* data */ + restore_flags (flags); + + return tmp; +} + +static void +opl3sa_write (int addr, int data) +{ + unsigned long flags; + + save_flags (flags); + cli (); + outb ((0x1d), 0xf86); /* password */ + outb (((unsigned char) addr), 0xf86); /* address */ + outb (((unsigned char) data), 0xf87); /* data */ + restore_flags (flags); +} + +static int +opl3sa_detect (void) +{ + int tmp; + + if (((tmp = opl3sa_read (0x01)) & 0xc4) != 0x04) + { + DDB (printk ("OPL3-SA detect error 1 (%x)\n", opl3sa_read (0x01))); + /* return 0; */ + } + +/* + * Check that the password feature has any effect + */ + if (inb (0xf87) == tmp) + { + DDB (printk ("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb (0xf87))); + return 0; + } + + tmp = (opl3sa_read (0x04) & 0xe0) >> 5; + + if (tmp != 0 && tmp != 1) + { + DDB (printk ("OPL3-SA detect failed 3 (%d)\n", tmp)); + return 0; + } + + DDB (printk ("OPL3-SA mode %x detected\n", tmp)); + + opl3sa_write (0x01, 0x00); /* Disable MSS */ + opl3sa_write (0x02, 0x00); /* Disable SB */ + opl3sa_write (0x03, 0x00); /* Disable MPU */ + + return 1; +} + +/* + * Probe and attach routines for the Windows Sound System mode of + * OPL3-SA + */ + +int +probe_opl3sa_wss (struct address_info *hw_config) +{ + int ret; + unsigned char tmp = 0x24; /* WSS enable */ + + if (check_region (0xf86, 2)) /* Control port is busy */ + return 0; + /* + * Check if the IO port returns valid signature. The original MS Sound + * system returns 0x04 while some cards (OPL3-SA for example) + * return 0x00. + */ + if (check_region (hw_config->io_base, 8)) + { + printk ("OPL3-SA: MSS I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + + opl3sa_osp = hw_config->osp; + + if (!opl3sa_detect ()) + { + printk ("OSS: OPL3-SA chip not found\n"); + return 0; + } + + switch (hw_config->io_base) + { + case 0x530: + tmp |= 0x00; + break; + case 0xe80: + tmp |= 0x08; + break; + case 0xf40: + tmp |= 0x10; + break; + case 0x604: + tmp |= 0x18; + break; + default: + printk ("OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base); + return 0; + } + + opl3sa_write (0x01, tmp); /* WSS setup register */ + kilroy_was_here = 1; + + ret = probe_ms_sound (hw_config); + if (ret) + request_region (0xf86, 2, "OPL3-SA"); + + return ret; +} + +void +attach_opl3sa_wss (struct address_info *hw_config) +{ + int nm = num_mixers; + + attach_ms_sound (hw_config); + if (num_mixers > nm) /* A mixer was installed */ + { + AD1848_REROUTE (SOUND_MIXER_LINE1, SOUND_MIXER_CD); + AD1848_REROUTE (SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); + AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_LINE); + } +} + + +void +attach_opl3sa_mpu (struct address_info *hw_config) +{ +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + hw_config->name = "OPL3-SA (MPU401)"; + attach_uart401 (hw_config); +#endif +} + +int +probe_opl3sa_mpu (struct address_info *hw_config) +{ +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + unsigned char conf; + static char irq_bits[] = + {-1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4}; + + if (!kilroy_was_here) + { + return 0; /* OPL3-SA has not been detected earlier */ + } + + if (mpu_initialized) + { + DDB (printk ("OPL3-SA: MPU mode already initialized\n")); + return 0; + } + + if (check_region (hw_config->io_base, 4)) + { + printk ("OPL3-SA: MPU I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + + if (hw_config->irq > 10) + { + printk ("OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + + if (irq_bits[hw_config->irq] == -1) + { + printk ("OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); + return 0; + } + + switch (hw_config->io_base) + { + case 0x330: + conf = 0x00; + break; + case 0x332: + conf = 0x20; + break; + case 0x334: + conf = 0x40; + break; + case 0x300: + conf = 0x60; + break; + default: + return 0; /* Invalid port */ + } + + conf |= 0x83; /* MPU & OPL3 (synth) & game port enable */ + conf |= irq_bits[hw_config->irq] << 2; + + opl3sa_write (0x03, conf); + + mpu_initialized = 1; + + return probe_uart401 (hw_config); +#else + return 0; +#endif +} + +void +unload_opl3sa_wss (struct address_info *hw_config) +{ + int dma2 = hw_config->dma2; + + if (dma2 == -1) + dma2 = hw_config->dma; + + release_region (0xf86, 2); + release_region (hw_config->io_base, 4); + + ad1848_unload (hw_config->io_base + 4, + hw_config->irq, + hw_config->dma, + dma2, + 0); +} + +void +unload_opl3sa_mpu (struct address_info *hw_config) +{ +#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) + unload_uart401 (hw_config); +#endif +} +#ifdef SB_OK +void +unload_opl3sa_sb (struct address_info *hw_config) +{ +#ifdef CONFIG_SBDSP + sb_dsp_unload (hw_config); +#endif +} +#endif + + +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/os.h linux/drivers/sound/os.h --- v2.1.62/linux/drivers/sound/os.h Wed May 28 10:51:33 1997 +++ linux/drivers/sound/os.h Sun Nov 9 23:01:38 1997 @@ -1,21 +1,13 @@ - -#ifdef __alpha__ -#else -#endif - #define ALLOW_SELECT #undef NO_INLINE_ASM #define SHORT_BANNERS #define MANUAL_PNP -#undef DO_TIMINGS +#undef DO_TIMINGS #ifdef MODULE #define __NO_VERSION__ #include #include -#ifdef MODVERSIONS -#include -#endif #endif #if LINUX_VERSION_CODE > 131328 #define LINUX21X @@ -41,19 +33,14 @@ #include #include #include -#else #endif #include - #include - #define FALSE 0 #define TRUE 1 - - struct snd_wait { volatile int opts; }; @@ -69,8 +56,6 @@ extern caddr_t sound_mem_blocks[1024]; extern int sound_mem_sizes[1024]; extern int sound_nblocks; - - #undef PSEUDO_DMA_AUTOINIT #define ALLOW_BUFFER_MAPPING diff -u --recursive --new-file v2.1.62/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c --- v2.1.62/linux/drivers/sound/pas2_card.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/pas2_card.c Tue Sep 30 08:46:53 1997 @@ -7,7 +7,7 @@ #include #include "sound_config.h" -#if defined(CONFIG_PAS) +#ifdef CONFIG_PAS static unsigned char dma_bits[] = {4, 1, 2, 3, 0, 5, 6, 7}; @@ -23,13 +23,13 @@ * be relative to the given base -register */ -int translate_code; +int translate_code = 0; static int pas_intr_mask = 0; static int pas_irq = 0; static int pas_sb_base = 0; -char pas_model; +char pas_model = 0; static char *pas_model_names[] = {"", "Pro AudioSpectrum+", "CDPC", "Pro AudioSpectrum 16", "Pro AudioSpectrum 16D"}; @@ -227,12 +227,10 @@ pas_sb_base = sb_config->io_base; if (!sb_dma_bits[sb_config->dma]) - printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n", - sb_config->dma); + printk ("\n\nPAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma); if (!sb_irq_bits[sb_config->irq]) - printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n", - sb_config->irq); + printk ("\n\nPAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq); irq_dma = sb_dma_bits[sb_config->dma] | sb_irq_bits[sb_config->irq]; diff -u --recursive --new-file v2.1.62/linux/drivers/sound/pas2_midi.c linux/drivers/sound/pas2_midi.c --- v2.1.62/linux/drivers/sound/pas2_midi.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/pas2_midi.c Tue Sep 30 08:46:53 1997 @@ -15,7 +15,8 @@ #include "sound_config.h" -#if defined(CONFIG_PAS) && defined(CONFIG_MIDI) +#ifdef CONFIG_PAS +#ifdef CONFIG_MIDI static int midi_busy = 0, input_opened = 0; static int my_dev; @@ -53,7 +54,10 @@ cli (); if ((err = pas_set_intr (0x10)) < 0) - return err; + { + restore_flags (flags); + return err; + } /* * Enable input available and output FIFO empty interrupts @@ -286,4 +290,5 @@ pas_write (stat, 0x1B88); /* Acknowledge interrupts */ } +#endif #endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/pas2_mixer.c linux/drivers/sound/pas2_mixer.c --- v2.1.62/linux/drivers/sound/pas2_mixer.c Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/pas2_mixer.c Tue Sep 30 08:46:54 1997 @@ -15,7 +15,7 @@ #include "sound_config.h" -#if defined(CONFIG_PAS) +#ifdef CONFIG_PAS #ifndef DEB #define DEB(what) /* (what) */ diff -u --recursive --new-file v2.1.62/linux/drivers/sound/pas2_pcm.c linux/drivers/sound/pas2_pcm.c --- v2.1.62/linux/drivers/sound/pas2_pcm.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/pas2_pcm.c Tue Sep 30 08:46:54 1997 @@ -12,7 +12,8 @@ #include "sound_config.h" -#if defined(CONFIG_PAS) && defined(CONFIG_AUDIO) +#ifdef CONFIG_PAS +#ifdef CONFIG_AUDIO #ifndef DEB #define DEB(WHAT) @@ -459,4 +460,5 @@ } } +#endif #endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/pss.c linux/drivers/sound/pss.c --- v2.1.62/linux/drivers/sound/pss.c Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/pss.c Tue Sep 30 08:46:54 1997 @@ -15,7 +15,8 @@ #include "sound_config.h" -#if defined(CONFIG_PSS) && defined(CONFIG_AUDIO) +#ifdef CONFIG_PSS +#ifdef CONFIG_AUDIO /* * PSS registers. @@ -123,7 +124,7 @@ id = inw (REG (PSS_ID)); if ((id >> 8) != 'E') { - /* printk ("No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); */ + /* printk( "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); */ return 0; } @@ -285,8 +286,7 @@ break; else { - printk ("\nPSS: Download timeout problems, byte %d=%d\n", - count, size); + printk ("\nPSS: Download timeout problems, byte %d=%d\n", count, size); return 0; } } @@ -323,7 +323,7 @@ return 0; val = inw (REG (PSS_DATA)); - /* printk("", val/16, val % 16); */ + /* printk( "", val/16, val % 16); */ } return 1; @@ -518,7 +518,7 @@ static int pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local) { - /* printk("PSS coproc ioctl %x %x %d\n", cmd, arg, local); */ + /* printk( "PSS coproc ioctl %x %x %d\n", cmd, arg, local); */ switch (cmd) { @@ -890,4 +890,5 @@ unload_ms_sound (hw_config); } +#endif #endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb.h linux/drivers/sound/sb.h --- v2.1.62/linux/drivers/sound/sb.h Wed May 28 10:51:33 1997 +++ linux/drivers/sound/sb.h Tue Sep 30 08:46:45 1997 @@ -1,3 +1,4 @@ +#ifdef CONFIG_SBDSP #define DSP_RESET (devc->base + 0x6) #define DSP_READ (devc->base + 0xA) #define DSP_WRITE (devc->base + 0xC) @@ -124,3 +125,4 @@ void sb_midi_interrupt (sb_devc *devc); int ess_write (sb_devc *devc, unsigned char reg, unsigned char data); int ess_read (sb_devc *devc, unsigned char reg); +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c --- v2.1.62/linux/drivers/sound/sb_audio.c Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/sb_audio.c Tue Sep 30 08:46:54 1997 @@ -15,7 +15,7 @@ #include "sound_config.h" -#if defined(CONFIG_SBDSP) +#ifdef CONFIG_SBDSP #include "sb_mixer.h" #include "sb.h" @@ -34,8 +34,9 @@ if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ) { - printk ("SB: Recording is not possible with this device\n"); - return -EPERM; + printk ("Notice: Recording is not possible with /dev/dsp%d\n", dev); + if (mode == OPEN_READ) + return -EPERM; } save_flags (flags); @@ -50,6 +51,7 @@ { if (sound_open_dma (devc->dma16, "Sound Blaster 16 bit")) { + restore_flags (flags); return -EBUSY; } } @@ -542,8 +544,12 @@ if (channels != devc->channels) { devc->channels = channels; - if (devc->model == MDL_SBPRO) - sbpro_audio_set_speed (dev, devc->speed); + if (devc->model == MDL_SBPRO && devc->channels == 2) + { + if (devc->speed > 22050) + printk ("OSS: Application error. Wrong ioctl call order.\n"); + sbpro_audio_set_speed (dev, devc->speed); + } } return devc->channels; } diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_card.c linux/drivers/sound/sb_card.c --- v2.1.62/linux/drivers/sound/sb_card.c Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/sb_card.c Tue Sep 30 08:46:54 1997 @@ -15,7 +15,7 @@ #include "sound_config.h" -#if defined(CONFIG_SBDSP) +#ifdef CONFIG_SBDSP #include "sb_mixer.h" #include "sb.h" @@ -33,8 +33,7 @@ { if (check_region (hw_config->io_base, 16)) { - printk ("\n\nsb_dsp.c: I/O port %x already in use\n\n", - hw_config->io_base); + printk ("\n\nsb_dsp.c: I/O port %x already in use\n\n", hw_config->io_base); return 0; } diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c --- v2.1.62/linux/drivers/sound/sb_common.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/sb_common.c Tue Sep 30 08:46:54 1997 @@ -15,7 +15,7 @@ #include "sound_config.h" -#if defined(CONFIG_SBDSP) +#ifdef CONFIG_SBDSP #ifndef CONFIG_AUDIO #error You will need to configure the sound driver with CONFIG_AUDIO option. @@ -52,6 +52,7 @@ #endif + int sb_dsp_command (sb_devc * devc, unsigned char val) { @@ -170,7 +171,7 @@ break; default: - /* printk ("Sound Blaster: Unexpected interrupt\n"); */ + /* printk( "Sound Blaster: Unexpected interrupt\n"); */ ; } /* @@ -184,12 +185,13 @@ status = inb (DSP_DATA_AVL16); } + int sb_dsp_reset (sb_devc * devc) { int loopc; - DDB (printk ("Entered sb_dsp_reset()\n")); + DEB (printk ("Entered sb_dsp_reset()\n")); if (devc->model == MDL_ESS) outb ((3), DSP_RESET); /* Reset FIFO too */ @@ -213,7 +215,7 @@ if (devc->model == MDL_ESS) sb_dsp_command (devc, 0xc6); /* Enable extended mode */ - DDB (printk ("sb_dsp_reset() OK\n")); + DEB (printk ("sb_dsp_reset() OK\n")); return 1; } @@ -435,6 +437,66 @@ return 1; } +static void +relocate_ess1688 (sb_devc * devc) +{ + unsigned char bits; + + switch (devc->base) + { + case 0x220: + bits = 0x04; + break; + case 0x230: + bits = 0x05; + break; + case 0x240: + bits = 0x06; + break; + case 0x250: + bits = 0x07; + break; + default: + return; /* Wrong port */ + } + + DDB (printk ("Doing ESS1688 address selection\n")); + +/* + * ES1688 supports two alternative ways for software address config. + * First try the so called Read-Sequence-Key method. + */ + + /* Reset the sequence logic */ + inb (0x229); + inb (0x229); + inb (0x229); + + /* Perform the read sequence */ + inb (0x22b); + inb (0x229); + inb (0x22b); + inb (0x229); + inb (0x229); + inb (0x22b); + inb (0x229); + + /* Select the base address by reading from it. Then probe using the port. */ + inb (devc->base); + if (sb_dsp_reset (devc)) /* Bingo */ + return; + +#if 0 /* This causes system lockups (Nokia 386/25 at least) */ +/* + * The last resort is the system control register method. + */ + + outb ((0x00), 0xfb); /* 0xFB is the unlock register */ + outb ((0x00), 0xe0); /* Select index 0 */ + outb ((bits), 0xe1); /* Write the config bits */ + outb ((0x00), 0xf9); /* 0xFB is the lock register */ +#endif +} static int ess_init (sb_devc * devc, struct address_info *hw_config) @@ -608,6 +670,8 @@ if (devc->major == 0 || (devc->major == 3 && devc->minor == 1)) relocate_Jazz16 (devc, hw_config); + if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0)) + relocate_ess1688 (devc); if (!sb_dsp_reset (devc)) { @@ -654,8 +718,7 @@ memcpy ((char *) detected_devc, (char *) devc, sizeof (sb_devc)); - DDB (printk ("SB %d.%d detected OK (%x)\n", devc->major, devc->minor, - hw_config->io_base)); + DDB (printk ("SB %d.%d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base)); return 1; } @@ -663,7 +726,6 @@ sb_dsp_init (struct address_info *hw_config) { sb_devc *devc; - int n; char name[100]; extern int sb_be_quiet; @@ -679,6 +741,7 @@ return; } + devc = detected_devc; detected_devc = NULL; @@ -727,7 +790,7 @@ } } -#ifdef __SMP__ +#if defined(__SMP__) || defined(__FreeBSD__) /* Skip IRQ detection if SMP (doesn't work) */ devc->irq_ok = 1; #else @@ -735,6 +798,8 @@ devc->irq_ok = 1; else { + int n; + for (n = 0; n < 3 && devc->irq_ok == 0; n++) if (sb_dsp_command (devc, 0xf2)) /* Cause interrupt immediately */ { @@ -822,16 +887,16 @@ * properly. */ if (devc->model <= MDL_SBPRO) - if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1. */ + if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1 (rare ones may have 3.2). */ { - printk ("This soundcard doesn't seem to be fully Sound Blaster Pro compatible.\n"); - printk ("Almost certainly there is another way to configure OSS so that\n"); + printk ("This soundcard may not be fully Sound Blaster Pro compatible.\n"); + printk ("In many cases there is another way to configure OSS so that\n"); printk ("it works properly with OSS (for example in 16 bit mode).\n"); + printk ("Please ignore this message if you _really_ have a SB Pro.\n"); } else if (!sb_be_quiet && devc->model == MDL_SBPRO) { - printk ("SB DSP version is just %d.%d which means that your card is\n", - devc->major, devc->minor); + printk ("SB DSP version is just %d.%d which means that your card is\n", devc->major, devc->minor); printk ("several years old (8 bit only device)\n"); printk ("or alternatively the sound driver is incorrectly configured.\n"); } @@ -899,6 +964,7 @@ } else release_region (hw_config->io_base, 16); + } /* @@ -1010,8 +1076,7 @@ if (smw_getmem (devc, mp_base, 0) != 0x00 || smw_getmem (devc, mp_base, 1) != 0xff) { - DDB (printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n", - smw_getmem (devc, mp_base, 0), smw_getmem (devc, mp_base, 1))); + DDB (printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem (devc, mp_base, 0), smw_getmem (devc, mp_base, 1))); return 0; /* No RAM */ } @@ -1250,8 +1315,10 @@ } hw_config->name = "Sound Blaster 16"; hw_config->irq = -devc->irq; +#if defined(CONFIG_MIDI) && defined(CONFIG_UART401) if (devc->minor > 12) /* What is Vibra's version??? */ sb16_set_mpu_port (devc, hw_config); +#endif break; case MDL_ESS: diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_midi.c linux/drivers/sound/sb_midi.c --- v2.1.62/linux/drivers/sound/sb_midi.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/sb_midi.c Tue Sep 30 08:46:55 1997 @@ -15,7 +15,8 @@ #include "sound_config.h" -#if defined(CONFIG_SBDSP) && defined(CONFIG_MIDI) +#ifdef CONFIG_SBDSP +#ifdef CONFIG_MIDI #include "sb.h" #undef SB_TEST_IRQ @@ -231,4 +232,5 @@ sequencer_init (); } +#endif #endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c --- v2.1.62/linux/drivers/sound/sb_mixer.c Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/sb_mixer.c Tue Sep 30 08:46:55 1997 @@ -16,7 +16,7 @@ #include "sound_config.h" -#if defined(CONFIG_SBDSP) +#ifdef CONFIG_SBDSP #define __SB_MIXER_C__ #include "sb.h" @@ -36,19 +36,7 @@ static int detect_mixer (sb_devc * devc) { - /* - * Detect the mixer by changing parameters of two volume channels. If the - * values read back match with the values written, the mixer is there (is - * it?) - */ - sb_setmixer (devc, FM_VOL, 0xff); - sb_setmixer (devc, VOC_VOL, 0x33); - - if (sb_getmixer (devc, FM_VOL) != 0xff) - return 0; - if (sb_getmixer (devc, VOC_VOL) != 0x33) - return 0; - + /* Just trust the mixer is there */ return 1; } @@ -406,6 +394,7 @@ switch (devc->model) { case MDL_SBPRO: + case MDL_AZTECH: case MDL_JAZZ: devc->mixer_caps = SOUND_CAP_EXCL_INPUT; devc->supported_devices = SBPRO_MIXER_DEVICES; diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sb_mixer.h linux/drivers/sound/sb_mixer.h --- v2.1.62/linux/drivers/sound/sb_mixer.h Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/sb_mixer.h Tue Sep 30 08:46:45 1997 @@ -17,6 +17,7 @@ * Added defines for the Sound Galaxy NX Pro mixer. * */ +#ifdef CONFIG_SBDSP #define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) @@ -282,4 +283,5 @@ #define SRC__CD 3 /* Select CD recording source */ #define SRC__LINE 7 /* Use Line-in for recording source */ +#endif #endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c --- v2.1.62/linux/drivers/sound/sequencer.c Wed Sep 24 20:05:47 1997 +++ linux/drivers/sound/sequencer.c Tue Sep 30 08:46:55 1997 @@ -17,6 +17,8 @@ #include "sound_config.h" #ifdef CONFIG_SEQUENCER +#include "softoss.h" +int (*softsynthp) (int cmd, int parm1, int parm2, unsigned long parm3) = NULL; #include "midi_ctrl.h" @@ -57,8 +59,8 @@ static int midi_written[MAX_MIDI_DEV] = {0}; -unsigned long prev_input_time = 0; -int prev_event_time; +static unsigned long prev_input_time = 0; +static int prev_event_time; #include "tuning.h" @@ -200,7 +202,10 @@ if (data == 0xfe) /* Ignore active sensing */ return; - tstamp = jiffies - seq_time; + if (softsynthp != NULL) + tstamp = softsynthp (SSYN_GETTIME, 0, 0, 0); + else + tstamp = jiffies - seq_time; if (tstamp != prev_input_time) { @@ -225,6 +230,8 @@ if (seq_mode == SEQ_2) this_time = tmr->get_time (tmr_no); + else if (softsynthp != NULL) + this_time = softsynthp (SSYN_GETTIME, 0, 0, 0); else this_time = jiffies - seq_time; @@ -732,7 +739,10 @@ prev_event_time = time; seq_playing = 1; - request_sound_timer (time); + if (softsynthp != NULL) + softsynthp (SSYN_REQUEST, time, 0, 0); + else + request_sound_timer (time); if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) { @@ -755,7 +765,13 @@ break; case TMR_START: - seq_time = jiffies; + if (softsynthp != NULL) + { + softsynthp (SSYN_START, 0, 0, 0); + seq_time = 0; + } + else + seq_time = jiffies; prev_input_time = 0; prev_event_time = 0; break; @@ -868,7 +884,10 @@ time = *delay; prev_event_time = time; - request_sound_timer (time); + if (softsynthp != NULL) + softsynthp (SSYN_REQUEST, time, 0, 0); + else + request_sound_timer (time); if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) { @@ -902,9 +921,14 @@ case SEQ_SYNCTIMER: /* * Reset timer */ - seq_time = jiffies; + if (softsynthp != NULL) + seq_time = 0; + else + seq_time = jiffies; prev_input_time = 0; prev_event_time = 0; + if (softsynthp != NULL) + softsynthp (SSYN_START, 0, 0, 0); break; case SEQ_MIDIPUTC: /* @@ -926,7 +950,10 @@ */ seq_playing = 1; - request_sound_timer (-1); + if (softsynthp != NULL) + softsynthp (SSYN_REQUEST, -1, 0, 0); + else + request_sound_timer (-1); return 2; } else @@ -1186,10 +1213,15 @@ } } - seq_time = jiffies; + if (softsynthp != NULL) + seq_time = 0; + else + seq_time = jiffies; prev_input_time = 0; prev_event_time = 0; + if (softsynthp != NULL) + softsynthp (SSYN_START, 0, 0, 0); if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) { /* @@ -1227,7 +1259,7 @@ n = 1; - while (!signal_pending(current) && n) + while (!(current->signal & ~current->blocked) && n) { n = 0; @@ -1279,7 +1311,7 @@ if (mode != OPEN_READ && !(file->flags & (O_NONBLOCK) ? 1 : 0)) - while (!signal_pending(current) && qlen > 0) + while (!(current->signal & ~current->blocked) && qlen > 0) { seq_sync (); @@ -1344,7 +1376,7 @@ { unsigned long flags; - if (qlen && !seq_playing && !signal_pending(current)) + if (qlen && !seq_playing && !(current->signal & ~current->blocked)) seq_startplay (); save_flags (flags); @@ -1429,7 +1461,10 @@ int chn; unsigned long flags; - sound_stop_timer (); + if (softsynthp != NULL) + softsynthp (SSYN_STOP, 0, 0, 0); + else + sound_stop_timer (); seq_time = jiffies; prev_input_time = 0; @@ -1493,7 +1528,7 @@ cli (); if ((seq_sleep_flag.opts & WK_SLEEP)) { - /* printk ("Sequencer Warning: Unexpected sleeping process - Waking up\n"); */ + /* printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */ { seq_sleep_flag.opts = WK_WAKEUP; wake_up (&seq_sleeper); @@ -1572,7 +1607,7 @@ if (mode == OPEN_READ) return 0; - while (qlen > 0 && !signal_pending(current)) + while (qlen > 0 && !(current->signal & ~current->blocked)) seq_sync (); if (qlen) return -EINTR; @@ -1624,7 +1659,10 @@ if (seq_mode == SEQ_2) return tmr->ioctl (tmr_no, cmd, arg); - return (*(int *) arg = jiffies - seq_time); + if (softsynthp != NULL) + return (*(int *) arg = softsynthp (SSYN_GETTIME, 0, 0, 0)); + else + return (*(int *) arg = jiffies - seq_time); break; case SNDCTL_SEQ_CTRLRATE: @@ -1740,6 +1778,7 @@ memcpy ((char *) &inf, (char *) synth_devs[dev]->info, sizeof (inf)); strcpy (inf.name, synth_devs[dev]->id); + inf.device = dev; memcpy ((&((char *) arg)[0]), (char *) &inf, sizeof (inf)); return 0; } @@ -1773,6 +1812,7 @@ if (dev < 0 || dev >= max_mididev) return -ENXIO; + midi_devs[dev]->info.device = dev; pp = (char *) &midi_devs[dev]->info; memcpy ((&((char *) arg)[0]), pp, sizeof (inf)); return 0; diff -u --recursive --new-file v2.1.62/linux/drivers/sound/softoss.c linux/drivers/sound/softoss.c --- v2.1.62/linux/drivers/sound/softoss.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/softoss.c Tue Sep 30 08:49:30 1997 @@ -0,0 +1,1593 @@ +/* + * sound/softoss.c + * + * Software based MIDI synthsesizer driver. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +#include + + +/* + * When POLLED_MODE is defined, the resampling loop is run using a timer + * callback routine. Normally the resampling loop is executed inside + * audio buffer interrupt handler which doesn't work with single mode DMA. + */ +#define SOFTSYN_MAIN +#undef POLLED_MODE +#define HANDLE_LFO + +#define ENVELOPE_SCALE 8 +#define NO_SAMPLE 0xffff + +#include "sound_config.h" + +#ifdef CONFIG_SOFTOSS +#include "softoss.h" +#include + +int softsynth_disabled = 0; + +static volatile int intr_pending = 0; + +#ifdef POLLED_MODE + +static struct timer_list poll_timer = +{NULL, NULL, 0, 0, softsyn_poll}; + +#else +#endif + +#ifdef HANDLE_LFO +/* + * LFO table. Playback at 128 Hz gives 1 Hz LFO frequency. + */ +static int tremolo_table[128] = +{ + 0, 39, 158, 355, 630, 982, 1411, 1915, + 2494, 3146, 3869, 4662, 5522, 6448, 7438, 8489, + 9598, 10762, 11980, 13248, 14563, 15922, 17321, 18758, + 20228, 21729, 23256, 24806, 26375, 27960, 29556, 31160, + 32768, 34376, 35980, 37576, 39161, 40730, 42280, 43807, + 45308, 46778, 48215, 49614, 50973, 52288, 53556, 54774, + 55938, 57047, 58098, 59088, 60014, 60874, 61667, 62390, + 63042, 63621, 64125, 64554, 64906, 65181, 65378, 65497, + 65536, 65497, 65378, 65181, 64906, 64554, 64125, 63621, + 63042, 62390, 61667, 60874, 60014, 59087, 58098, 57047, + 55938, 54774, 53556, 52288, 50973, 49614, 48215, 46778, + 45308, 43807, 42280, 40730, 39161, 37576, 35980, 34376, + 32768, 31160, 29556, 27960, 26375, 24806, 23256, 21729, + 20228, 18758, 17321, 15922, 14563, 13248, 11980, 10762, + 9598, 8489, 7438, 6448, 5522, 4662, 3869, 3146, + 2494, 1915, 1411, 982, 630, 355, 158, 39 +}; + +static int vibrato_table[128] = +{ + 0, 1608, 3212, 4808, 6393, 7962, 9512, 11039, + 12540, 14010, 15447, 16846, 18205, 19520, 20788, 22006, + 23170, 24279, 25330, 26320, 27246, 28106, 28899, 29622, + 30274, 30853, 31357, 31786, 32138, 32413, 32610, 32729, + 32768, 32729, 32610, 32413, 32138, 31786, 31357, 30853, + 30274, 29622, 28899, 28106, 27246, 26320, 25330, 24279, + 23170, 22006, 20788, 19520, 18205, 16846, 15447, 14010, + 12540, 11039, 9512, 7962, 6393, 4808, 3212, 1608, + 0, -1608, -3212, -4808, -6393, -7962, -9512, -11039, + -12540, -14010, -15447, -16846, -18205, -19520, -20788, -22006, + -23170, -24279, -25330, -26320, -27246, -28106, -28899, -29622, + -30274, -30853, -31357, -31786, -32138, -32413, -32610, -32729, + -32768, -32729, -32610, -32413, -32138, -31786, -31357, -30853, + -30274, -29622, -28899, -28106, -27246, -26320, -25330, -24279, + -23170, -22006, -20788, -19520, -18205, -16846, -15447, -14010, + -12540, -11039, -9512, -7962, -6393, -4808, -3212, -1608 +}; + +#endif + +static unsigned long last_resample_jiffies; +static unsigned long resample_counter; + +extern int *sound_osp; + +static volatile int is_running = 0; +static int softsynth_loaded = 0; + +static struct synth_info softsyn_info = +{"SoftOSS", 0, SYNTH_TYPE_SAMPLE, SAMPLE_TYPE_GUS, 0, 16, 0, MAX_PATCH}; + +static struct softsyn_devc sdev_info = +{0}; +softsyn_devc *devc = &sdev_info; + +static struct voice_alloc_info *voice_alloc; + +static int softsyn_open (int synthdev, int mode); +static void init_voice (softsyn_devc * devc, int voice); +static void compute_step (int voice); + +static volatile int tmr_running = 0; +static int voice_limit = 24; + +static void +set_max_voices (int nr) +{ + int i; + + if (nr < 4) + nr = 4; + + if (nr > voice_limit) + nr = voice_limit; + + voice_alloc->max_voice = devc->maxvoice = nr; + devc->afterscale = 5; + + for (i = 31; i > 0; i--) + if (nr & (1 << i)) + { + devc->afterscale = i + 1; + return; + } +} + +static void +update_vibrato (int voice) +{ + voice_info *v = &softoss_voices[voice]; + +#ifdef HANDLE_LFO + int x; + + x = vibrato_table[v->vibrato_phase >> 8]; + v->vibrato_phase = (v->vibrato_phase + v->vibrato_step) & 0x7fff; + + x = (x * v->vibrato_depth) >> 15; + v->vibrato_level = (x * 600) >> 8; + + compute_step (voice); +#else + v->vibrato_level = 0; +#endif +} + +#ifdef HANDLE_LFO +static void +update_tremolo (int voice) +{ + voice_info *v = &softoss_voices[voice]; + int x; + + x = tremolo_table[v->tremolo_phase >> 8]; + v->tremolo_phase = (v->tremolo_phase + v->tremolo_step) & 0x7fff; + + v->tremolo_level = (x * v->tremolo_depth) >> 20; +} +#endif + +static void +start_vibrato (int voice) +{ + voice_info *v = &softoss_voices[voice]; + int rate; + + if (!v->vibrato_depth) + return; + + rate = v->vibrato_rate * 6 * 128; + v->vibrato_step = (rate * devc->control_rate) / devc->speed; + + devc->vibratomap |= (1 << voice); /* Enable vibrato */ +} + +static void +start_tremolo (int voice) +{ + voice_info *v = &softoss_voices[voice]; + int rate; + + if (!v->tremolo_depth) + return; + + rate = v->tremolo_rate * 6 * 128; + v->tremolo_step = (rate * devc->control_rate) / devc->speed; + + devc->tremolomap |= (1 << voice); /* Enable tremolo */ +} + +static void +update_volume (int voice) +{ + voice_info *v = &softoss_voices[voice]; + unsigned int vol; + +/* + * Compute plain volume + */ + + vol = (v->velocity * v->expression_vol * v->main_vol) >> 12; + +#ifdef HANDLE_LFO +/* + * Handle LFO + */ + + if (devc->tremolomap & (1 << voice)) + { + int t; + + t = 32768 - v->tremolo_level; + vol = (vol * t) >> 15; + update_tremolo (voice); + } +#endif +/* + * Envelope + */ + if (v->mode & WAVE_ENVELOPES && !v->percussive_voice) + vol = (vol * (v->envelope_vol >> 16)) >> 19; + else + vol >>= 4; + +/* + * Handle panning + */ + + if (v->panning < 0) /* Pan left */ + v->rightvol = (vol * (128 + v->panning)) / 128; + else + v->rightvol = vol; + + if (v->panning > 0) /* Pan right */ + v->leftvol = (vol * (128 - v->panning)) / 128; + else + v->leftvol = vol; +} + +static void +step_envelope (int voice, int do_release, int velocity) +{ + voice_info *v = &softoss_voices[voice]; + int r, rate, time, dif; + unsigned int vol; + unsigned long flags; + + save_flags (flags); + cli (); + + if (!voice_active[voice] || v->sample == NULL) + { + restore_flags (flags); + return; + } + + if (!do_release) + if (v->mode & WAVE_SUSTAIN_ON && v->envelope_phase == 2) + { /* Stop envelope until note off */ + v->envelope_volstep = 0; + v->envelope_time = 0x7fffffff; + if (v->mode & WAVE_VIBRATO) + start_vibrato (voice); + if (v->mode & WAVE_TREMOLO) + start_tremolo (voice); + restore_flags (flags); + return; + } + + if (do_release) + v->envelope_phase = 3; + else + v->envelope_phase++; + + if (v->envelope_phase >= 5) /* Finished */ + { + init_voice (devc, voice); + restore_flags (flags); + return; + } + + vol = v->envelope_target = v->sample->env_offset[v->envelope_phase] << 22; + + + rate = v->sample->env_rate[v->envelope_phase]; + r = 3 - ((rate >> 6) & 0x3); + r *= 3; + r = (int) (rate & 0x3f) << r; + rate = (((r * 44100) / devc->speed) * devc->control_rate) << 8; + + if (rate < (1 << 20)) /* Avoid infinitely "releasing" voices */ + rate = 1 << 20; + + dif = (v->envelope_vol - vol); + if (dif < 0) + dif *= -1; + if (dif < rate * 2) /* Too close */ + { + step_envelope (voice, 0, 60); + restore_flags (flags); + return; + } + + if (vol > v->envelope_vol) + { + v->envelope_volstep = rate; + time = (vol - v->envelope_vol) / rate; + } + else + { + v->envelope_volstep = -rate; + time = (v->envelope_vol - vol) / rate; + } + + time--; + if (time <= 0) + time = 1; + + v->envelope_time = time; + + + restore_flags (flags); +} + +static void +step_envelope_lfo (int voice) +{ + voice_info *v = &softoss_voices[voice]; + +/* + * Update pitch (vibrato) LFO + */ + + if (devc->vibratomap & (1 << voice)) + update_vibrato (voice); + +/* + * Update envelope + */ + + if (v->mode & WAVE_ENVELOPES) + { + v->envelope_vol += v->envelope_volstep; + /* Overshoot protection */ + if (v->envelope_vol < 0) + { + v->envelope_vol = v->envelope_target; + v->envelope_volstep = 0; + } + + if (v->envelope_time-- <= 0) + { + v->envelope_vol = v->envelope_target; + step_envelope (voice, 0, 60); + } + } +} + +static void +compute_step (int voice) +{ + voice_info *v = &softoss_voices[voice]; + + /* + * Since the pitch bender may have been set before playing the note, we + * have to calculate the bending now. + */ + + v->current_freq = compute_finetune (v->orig_freq, + v->bender, + v->bender_range, + v->vibrato_level); + v->step = (((v->current_freq << 9) + (devc->speed >> 1)) / devc->speed); + + if (v->mode & WAVE_LOOP_BACK) + v->step *= -1; /* Reversed playback */ +} + +static void +init_voice (softsyn_devc * devc, int voice) +{ + voice_info *v = &softoss_voices[voice]; + unsigned long flags; + + save_flags (flags); + cli (); + voice_active[voice] = 0; + devc->vibratomap &= ~(1 << voice); + devc->tremolomap &= ~(1 << voice); + v->mode = 0; + v->wave = NULL; + v->sample = NULL; + v->ptr = 0; + v->step = 0; + v->startloop = 0; + v->startbackloop = 0; + v->endloop = 0; + v->looplen = 0; + v->bender = 0; + v->bender_range = 200; + v->panning = 0; + v->main_vol = 127; + v->expression_vol = 127; + v->patch_vol = 127; + v->percussive_voice = 0; + v->sustain_mode = 0; + v->envelope_phase = 1; + v->envelope_vol = 1 << 24; + v->envelope_volstep = 256; + v->envelope_time = 0; + v->vibrato_phase = 0; + v->vibrato_step = 0; + v->vibrato_level = 0; + v->vibrato_rate = 0; + v->vibrato_depth = 0; + v->tremolo_phase = 0; + v->tremolo_step = 0; + v->tremolo_level = 0; + v->tremolo_rate = 0; + v->tremolo_depth = 0; + voice_alloc->map[voice] = 0; + voice_alloc->alloc_times[voice] = 0; + restore_flags (flags); +} + +static void +reset_samples (softsyn_devc * devc) +{ + int i; + + for (i = 0; i < MAX_VOICE; i++) + voice_active[i] = 0; + for (i = 0; i < devc->maxvoice; i++) + { + init_voice (devc, i); + softoss_voices[i].instr = 0; + } + + devc->ram_used = 0; + + for (i = 0; i < MAX_PATCH; i++) + devc->programs[i] = NO_SAMPLE; + + for (i = 0; i < devc->nrsamples; i++) + { + vfree (devc->samples[i]); + vfree (devc->wave[i]); + devc->samples[i] = NULL; + devc->wave[i] = NULL; + } + + devc->nrsamples = 0; +} + +static void +init_engine (softsyn_devc * devc) +{ + int i, fz, srate, sz = devc->channels; + + set_max_voices (devc->default_max_voices); + voice_alloc->timestamp = 0; + + if (devc->bits == 16) + sz *= 2; + + fz = devc->fragsize / sz; /* Samples per fragment */ + devc->samples_per_fragment = fz; + + devc->usecs = 0; + devc->usecs_per_frag = (1000000 * fz) / devc->speed; + + for (i = 0; i < devc->maxvoice; i++) + { + init_voice (devc, i); + softoss_voices[i].instr = 0; + } + + devc->engine_state = ES_STOPPED; + +/* + * Initialize delay + */ + + for (i = 0; i < DELAY_SIZE; i++) + left_delay[i] = right_delay[i] = 0; + delayp = 0; + srate = (devc->speed / 10000); /* 1 to 4 */ + if (srate <= 0) + srate = 1; + devc->delay_size = (DELAY_SIZE * srate) / 4; + if (devc->delay_size == 0 || devc->delay_size > DELAY_SIZE) + devc->delay_size = DELAY_SIZE; +} + +void +softsyn_control_loop (void) +{ + int voice; + +/* + * Recompute envlope, LFO, etc. + */ + for (voice = 0; voice < devc->maxvoice; voice++) + if (voice_active[voice]) + { + update_volume (voice); + step_envelope_lfo (voice); + } + else + voice_alloc->map[voice] = 0; +} + +static void start_engine (softsyn_devc * devc); + +static void +do_resample (int dummy) +{ + struct dma_buffparms *dmap = audio_devs[devc->audiodev]->dmap_out; + struct voice_info *vinfo; + unsigned long flags, jif; + + int voice, loops; + short *buf; + + if (softsynth_disabled) + return; + + save_flags (flags); + cli (); + + if (is_running) + { + printk ("SoftOSS: Playback overrun\n"); + restore_flags (flags); + return; + } + + jif = jiffies; + if (jif == last_resample_jiffies) + { + if (resample_counter++ > 50) + { + for (voice = 0; voice < devc->maxvoice; voice++) + init_voice (devc, voice); + voice_limit--; + resample_counter = 0; + printk ("SoftOSS: CPU overload. Limiting # of voices to %d\n", voice_limit); + + if (voice_limit < 10) + { + voice_limit = 10; + devc->speed = (devc->speed * 2) / 3; + + printk ("SoftOSS: Dropping sampling rate and stopping the device.\n"); + softsynth_disabled = 1; + } + } + } + else + { + last_resample_jiffies = jif; + resample_counter = 0; + } + + /* is_running = 1; */ + + if (dmap->qlen > devc->max_playahead) + { + printk ("SoftOSS: audio buffers full\n"); + is_running = 0; + restore_flags (flags); + return; + } + +/* + * First verify that all active voices are valid (do this just once per block). + */ + for (voice = 0; voice < devc->maxvoice; voice++) + if (voice_active[voice]) + { + int ptr; + + vinfo = &softoss_voices[voice]; + ptr = vinfo->ptr >> 9; + + if (vinfo->wave == NULL || + ptr < 0 || + ptr > vinfo->sample->len) + init_voice (devc, voice); + else if (!(vinfo->mode & WAVE_LOOPING) && + (vinfo->ptr + vinfo->step) > vinfo->endloop) + voice_active[voice] = 0; + } + +/* + * Start the resampling process + */ + + loops = devc->samples_per_fragment; + buf = (short *) (dmap->raw_buf + (dmap->qtail * dmap->fragment_size)); + + softsynth_resample_loop (buf, loops); /* In Xsoftsynth_rs.c */ + + dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; + dmap->qlen++; + dmap->user_counter += dmap->fragment_size; + + devc->usecs += devc->usecs_per_frag; + + if (tmr_running) + { + sound_timer_interrupt (); + } + +/* + * Execute timer + */ + + if (!tmr_running) + if (devc->usecs >= devc->next_event_usecs) + { + devc->next_event_usecs = ~0; + sequencer_timer (0); + } + is_running = 0; + restore_flags (flags); +} + +static void +delayed_resample (int dummy) +{ + struct dma_buffparms *dmap = audio_devs[devc->audiodev]->dmap_out; + int n = 0; + + if (is_running) + return; + + while (devc->engine_state != ES_STOPPED && + dmap->qlen < devc->max_playahead && n++ < 2) + do_resample (0); + intr_pending = 0; +} + +#ifdef POLLED_MODE +static void +softsyn_poll (unsigned long dummy) +{ + delayed_resample (0); + + if (devc->engine_state != ES_STOPPED) + + { + poll_timer.expires = (1) + jiffies; + add_timer (&poll_timer); + }; +} +#else +static void +softsyn_callback (int dev, int parm) +{ + delayed_resample (0); +} +#endif + +static void +start_engine (softsyn_devc * devc) +{ + struct dma_buffparms *dmap; + + if (!devc->audio_opened) + if (softsyn_open (devc->synthdev, 0) < 0) + return; + + if (devc->audiodev >= num_audiodevs) + return; + + dmap = audio_devs[devc->audiodev]->dmap_out; + + devc->usecs = 0; + devc->next_event_usecs = ~0; + devc->control_rate = 64; + devc->control_counter = 0; + + if (devc->engine_state == ES_STOPPED) + { + int trig, n = 0; + + trig = 0; + dma_ioctl (devc->audiodev, SNDCTL_DSP_SETTRIGGER, (caddr_t) & trig); +#ifdef POLLED_MODE + ; + + { + poll_timer.expires = (1) + jiffies; + add_timer (&poll_timer); + }; /* Start polling */ +#else + dmap->audio_callback = softsyn_callback; + dmap->qhead = dmap->qtail = dmap->qlen = 0; +#endif + + while (dmap->qlen < devc->max_playahead && n++ < 2) + do_resample (0); + + devc->engine_state = ES_STARTED; + last_resample_jiffies = jiffies; + resample_counter = 0; + + trig = PCM_ENABLE_OUTPUT; + if (dma_ioctl (devc->audiodev, SNDCTL_DSP_SETTRIGGER, + (caddr_t) & trig) < 0) + { + printk ("SoftOSS: Trigger failed\n"); + } + + + } +} + +static void +stop_engine (softsyn_devc * devc) +{ +} + +static void +request_engine (softsyn_devc * devc, int ticks) +{ + if (ticks < 0) /* Relative time */ + devc->next_event_usecs = devc->usecs - ticks * (1000000 / HZ); + else + devc->next_event_usecs = ticks * (1000000 / HZ); +} + +/* + * Softsync hook serves mode1 (timing) calls made by sequencer.c + */ +static int +softsynth_hook (int cmd, int parm1, int parm2, unsigned long parm3) +{ + switch (cmd) + { + case SSYN_START: + start_engine (devc); + break; + + case SSYN_STOP: + stop_engine (devc); + break; + + case SSYN_REQUEST: + request_engine (devc, parm1); + break; + + case SSYN_GETTIME: + return devc->usecs / (1000000 / HZ); + break; + + default: + printk ("SoftOSS: Unknown request %d\n", cmd); + } + + return 0; +} + +static int +softsyn_ioctl (int dev, + unsigned int cmd, caddr_t arg) +{ + switch (cmd) + { + + case SNDCTL_SYNTH_INFO: + softsyn_info.nr_voices = devc->maxvoice; + + memcpy ((&((char *) arg)[0]), (char *) &softsyn_info, sizeof (softsyn_info)); + return 0; + break; + + case SNDCTL_SEQ_RESETSAMPLES: + stop_engine (devc); + reset_samples (devc); + return 0; + break; + + case SNDCTL_SYNTH_MEMAVL: + return devc->ram_size - devc->ram_used; + break; + + default: + return -EINVAL; + } + +} + +static int +softsyn_kill_note (int devno, int voice, int note, int velocity) +{ + if (voice < 0 || voice > devc->maxvoice) + return 0; + voice_alloc->map[voice] = 0xffff; /* Releasing */ + + if (softoss_voices[voice].sustain_mode & 1) /* Sustain controller on */ + { + softoss_voices[voice].sustain_mode = 3; /* Note off pending */ + return 0; + } + + if (velocity > 127 || softoss_voices[voice].mode & WAVE_FAST_RELEASE) + { + init_voice (devc, voice); /* Mark it inactive */ + return 0; + } + + if (softoss_voices[voice].mode & WAVE_ENVELOPES) + step_envelope (voice, 1, velocity); /* Enter sustain phase */ + else + init_voice (devc, voice); /* Mark it inactive */ + return 0; +} + +static int +softsyn_set_instr (int dev, int voice, int instr) +{ + if (voice < 0 || voice > devc->maxvoice) + return 0; + + if (instr < 0 || instr > MAX_PATCH) + { + printk ("SoftOSS: Invalid instrument number %d\n", instr); + return 0; + } + + softoss_voices[voice].instr = instr; + + return 0; +} + +static int +softsyn_start_note (int dev, int voice, int note, int volume) +{ + int instr = 0; + int best_sample, best_delta, delta_freq, selected; + unsigned long note_freq, freq, base_note, flags; + voice_info *v = &softoss_voices[voice]; + + struct patch_info *sample; + + if (voice < 0 || voice > devc->maxvoice) + return 0; + + if (volume == 0) /* Actually note off */ + softsyn_kill_note (dev, voice, note, volume); + + save_flags (flags); + cli (); + + if (note == 255) + { /* Just volume update */ + v->velocity = volume; + if (voice_active[voice]) + update_volume (voice); + restore_flags (flags); + return 0; + } + + voice_active[voice] = 0; /* Stop the voice for a while */ + devc->vibratomap &= ~(1 << voice); + devc->tremolomap &= ~(1 << voice); + + instr = v->instr; + if (instr < 0 || instr > MAX_PATCH || devc->programs[instr] == NO_SAMPLE) + { + printk ("SoftOSS: Undefined MIDI instrument %d\n", instr); + restore_flags (flags); + return 0; + } + + instr = devc->programs[instr]; + + if (instr < 0 || instr >= devc->nrsamples) + { + printk ("SoftOSS: Corrupted MIDI instrument %d (%d)\n", v->instr, instr); + restore_flags (flags); + return 0; + } + + note_freq = note_to_freq (note); + + selected = -1; + + best_sample = instr; + best_delta = 1000000; + + while (instr != NO_SAMPLE && instr >= 0 && selected == -1) + { + delta_freq = note_freq - devc->samples[instr]->base_note; + + if (delta_freq < 0) + delta_freq = -delta_freq; + if (delta_freq < best_delta) + { + best_sample = instr; + best_delta = delta_freq; + } + if (devc->samples[instr]->low_note <= note_freq && + note_freq <= devc->samples[instr]->high_note) + selected = instr; + else + instr = devc->samples[instr]->key; /* Link to next sample */ + + if (instr < 0 || instr >= devc->nrsamples) + instr = NO_SAMPLE; + } + + if (selected == -1) + instr = best_sample; + else + instr = selected; + + if (instr < 0 || instr == NO_SAMPLE || instr > devc->nrsamples) + { + printk ("SoftOSS: Unresolved MIDI instrument %d\n", v->instr); + restore_flags (flags); + return 0; + } + + sample = devc->samples[instr]; + v->sample = sample; + + if (v->percussive_voice) /* No key tracking */ + { + v->orig_freq = sample->base_freq; /* Fixed pitch */ + } + else + { + base_note = sample->base_note / 100; + note_freq /= 100; + + freq = sample->base_freq * note_freq / base_note; + v->orig_freq = freq; + } + + if (!(sample->mode & WAVE_LOOPING)) + { + sample->loop_end = sample->len; + } + + v->wave = devc->wave[instr]; + + if (volume < 0) + volume = 0; + else if (volume > 127) + volume = 127; + + v->ptr = 0; + v->startloop = sample->loop_start * 512; + v->startbackloop = 0; + v->endloop = sample->loop_end * 512; + v->looplen = (sample->loop_end - sample->loop_start) * 512; + v->leftvol = 64; + v->rightvol = 64; + v->patch_vol = sample->volume; + v->velocity = volume; + v->mode = sample->mode; + v->vibrato_phase = 0; + v->vibrato_step = 0; + v->vibrato_level = 0; + v->vibrato_rate = 0; + v->vibrato_depth = 0; + v->tremolo_phase = 0; + v->tremolo_step = 0; + v->tremolo_level = 0; + v->tremolo_rate = 0; + v->tremolo_depth = 0; + + if (!(v->mode & WAVE_LOOPING)) + v->mode &= ~(WAVE_BIDIR_LOOP | WAVE_LOOP_BACK); + else if (v->mode & WAVE_LOOP_BACK) + { + v->ptr = sample->len; + v->startbackloop = v->startloop; + } + + if (v->mode & WAVE_VIBRATO) + { + v->vibrato_rate = sample->vibrato_rate; + v->vibrato_depth = sample->vibrato_depth; + } + + if (v->mode & WAVE_TREMOLO) + { + v->tremolo_rate = sample->tremolo_rate; + v->tremolo_depth = sample->tremolo_depth; + } + + if (v->mode & WAVE_ENVELOPES) + { + v->envelope_phase = -1; + v->envelope_vol = 0; + step_envelope (voice, 0, 60); + } + update_volume (voice); + compute_step (voice); + + voice_active[voice] = 1; /* Mark it active */ + + restore_flags (flags); + return 0; +} + +static int +softsyn_open (int synthdev, int mode) +{ + int err; + extern int softoss_dev; + int frags = 0x7fff0007; /* fragment size of 128 bytes */ + + if (devc->audio_opened) /* Already opened */ + return 0; + + softsynth_disabled = 0; + devc->finfo.mode = OPEN_WRITE; + devc->finfo.flags = 0; + + if (softoss_dev >= num_audiodevs) + softoss_dev = num_audiodevs - 1; + + if (softoss_dev < 0) + softoss_dev = 0; + if (softoss_dev >= num_audiodevs) + return -ENXIO; + devc->audiodev = softoss_dev; + + if (!(audio_devs[devc->audiodev]->format_mask & AFMT_S16_LE)) + { + printk ("SoftOSS: The audio device doesn't support 16 bits\n"); + return -ENXIO; + } + if ((err = audio_open ((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo)) < 0) + return err; + + devc->speed = audio_devs[devc->audiodev]->d->set_speed ( + devc->audiodev, devc->speed); + devc->channels = audio_devs[devc->audiodev]->d->set_channels ( + devc->audiodev, devc->channels); + devc->bits = audio_devs[devc->audiodev]->d->set_bits ( + devc->audiodev, devc->bits); + + + DDB (printk ("SoftOSS: Using audio dev %d, speed %d, bits %d, channels %d\n", devc->audiodev, devc->speed, devc->bits, devc->channels)); + + dma_ioctl (devc->audiodev, SNDCTL_DSP_SETFRAGMENT, (caddr_t) & frags); + dma_ioctl (devc->audiodev, SNDCTL_DSP_GETBLKSIZE, (caddr_t) & devc->fragsize); + + if (devc->bits != 16 || devc->channels != 2) + { + audio_release ((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo); + printk ("SoftOSS: A 16 bit stereo soundcard is required\n"); + return 0; + } + + if (devc->max_playahead >= audio_devs[devc->audiodev]->dmap_out->nbufs) + devc->max_playahead = audio_devs[devc->audiodev]->dmap_out->nbufs; + + DDB (printk ("SoftOSS: Using %d fragments of %d bytes\n", devc->max_playahead, devc->fragsize)); + + init_engine (devc); + devc->audio_opened = 1; + devc->sequencer_mode = mode; + return 0; +} + +static void +softsyn_close (int synthdev) +{ + devc->engine_state = ES_STOPPED; +#ifdef POLLED_MODE + del_timer (&poll_timer);; +#endif + dma_ioctl (devc->audiodev, SNDCTL_DSP_RESET, 0); + if (devc->audio_opened) + audio_release ((devc->audiodev << 4) | SND_DEV_DSP16, &devc->finfo); + devc->audio_opened = 0; +} + +static void +softsyn_hw_control (int dev, unsigned char *event_rec) +{ + int voice, cmd; + unsigned short p1, p2; + unsigned int plong; + + cmd = event_rec[2]; + voice = event_rec[3]; + p1 = *(unsigned short *) &event_rec[4]; + p2 = *(unsigned short *) &event_rec[6]; + plong = *(unsigned int *) &event_rec[4]; + + switch (cmd) + { + + case _GUS_NUMVOICES: + set_max_voices (p1); + break; + + + default:; + } +} + +static int +softsyn_load_patch (int dev, int format, const char *addr, + int offs, int count, int pmgr_flag) +{ + struct patch_info *patch = NULL; + + int i, p, instr; + long sizeof_patch; + int memlen, adj; + unsigned short data; + short *wave = NULL; + + sizeof_patch = (long) &patch->data[0] - (long) patch; /* Header size */ + + if (format != GUS_PATCH) + { + printk ("SoftOSS: Invalid patch format (key) 0x%x\n", format); + return -EINVAL; + } + + if (count < sizeof_patch) + { + printk ("SoftOSS: Patch header too short\n"); + return -EINVAL; + } + + count -= sizeof_patch; + + if (devc->nrsamples >= MAX_SAMPLE) + { + printk ("SoftOSS: Sample table full\n"); + return -ENOSPC; + } + + /* + * Copy the header from user space but ignore the first bytes which have + * been transferred already. + */ + + patch = vmalloc (sizeof (*patch)); + + if (patch == NULL) + { + printk ("SoftOSS: Out of memory\n"); + return -ENOSPC; + } + + copy_from_user (&((char *) patch)[offs], &(addr)[offs], sizeof_patch - offs); + + if (patch->mode & WAVE_ROM) + { + vfree (patch); + return -EINVAL; + } + + instr = patch->instr_no; + + if (instr < 0 || instr > MAX_PATCH) + { + printk ("SoftOSS: Invalid patch number %d\n", instr); + vfree (patch); + return -EINVAL; + } + + if (count < patch->len) + { + printk ("SoftOSS: Patch record too short (%d<%d)\n", count, (int) patch->len); + patch->len = count; + } + + if (patch->len <= 0 || patch->len > (devc->ram_size - devc->ram_used)) + { + printk ("SoftOSS: Invalid sample length %d\n", (int) patch->len); + vfree (patch); + return -EINVAL; + } + + if (patch->mode & WAVE_LOOPING) + { + if (patch->loop_start < 0 || patch->loop_start >= patch->len) + { + printk ("SoftOSS: Invalid loop start %d\n", patch->loop_start); + vfree (patch); + return -EINVAL; + } + + if (patch->loop_end < patch->loop_start || patch->loop_end > patch->len) + { + printk ("SoftOSS: Invalid loop start or end point (%d, %d)\n", patch->loop_start, patch->loop_end); + vfree (patch); + return -EINVAL; + } + } + +/* + * Next load the wave data to memory + */ + + memlen = patch->len; + adj = 1; + + if (!(patch->mode & WAVE_16_BITS)) + memlen *= 2; + else + adj = 2; + + wave = vmalloc (memlen); + + if (wave == NULL) + { + printk ("SoftOSS: Can't allocate %d bytes of mem for a sample\n", memlen); + vfree (patch); + return -ENOSPC; + } + + p = 0; + for (i = 0; i < memlen / 2; i++) /* Handle words */ + { + unsigned char tmp; + + data = 0; + + if (patch->mode & WAVE_16_BITS) + { + get_user (*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++])); /* Get lsb */ + data = tmp; + get_user (*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++])); /* Get msb */ + if (patch->mode & WAVE_UNSIGNED) + tmp ^= 0x80; /* Convert to signed */ + data |= (tmp << 8); + } + else + { + get_user (*(unsigned char *) &tmp, (unsigned char *) &((addr)[sizeof_patch + p++])); + if (patch->mode & WAVE_UNSIGNED) + tmp ^= 0x80; /* Convert to signed */ + data = (tmp << 8); /* Convert to 16 bits */ + } + + wave[i] = (short) data; + } + + devc->ram_used += patch->len; +/* + * Convert pointers to 16 bit indexes + */ + patch->len /= adj; + patch->loop_start /= adj; + patch->loop_end /= adj; + +/* + * Finally link the loaded patch to the chain + */ + + patch->key = devc->programs[instr]; + devc->programs[instr] = devc->nrsamples; + devc->wave[devc->nrsamples] = (short *) wave; + devc->samples[devc->nrsamples++] = patch; + + return 0; +} + +static void +softsyn_panning (int dev, int voice, int pan) +{ + if (voice < 0 || voice > devc->maxvoice) + return; + + if (pan < -128) + pan = -128; + if (pan > 127) + pan = 127; + + softoss_voices[voice].panning = pan; + if (voice_active[voice]) + update_volume (voice); +} + +static void +softsyn_volume_method (int dev, int mode) +{ +} + +static void +softsyn_aftertouch (int dev, int voice, int pressure) +{ + if (voice < 0 || voice > devc->maxvoice) + return; + + if (voice_active[voice]) + update_volume (voice); +} + +static void +softsyn_controller (int dev, int voice, int ctrl_num, int value) +{ + unsigned long flags; + + if (voice < 0 || voice > devc->maxvoice) + return; + save_flags (flags); + cli (); + + switch (ctrl_num) + { + case CTRL_PITCH_BENDER: + softoss_voices[voice].bender = value; + + if (voice_active[voice]) + compute_step (voice); /* Update pitch */ + break; + + + case CTRL_PITCH_BENDER_RANGE: + softoss_voices[voice].bender_range = value; + break; + case CTL_EXPRESSION: + value /= 128; + case CTRL_EXPRESSION: + softoss_voices[voice].expression_vol = value; + if (voice_active[voice]) + update_volume (voice); + break; + + case CTL_PAN: + softsyn_panning (dev, voice, (value * 2) - 128); + break; + + case CTL_MAIN_VOLUME: + value = (value * 100) / 16383; + + case CTRL_MAIN_VOLUME: + softoss_voices[voice].main_vol = value; + if (voice_active[voice]) + update_volume (voice); + break; + + default: + break; + } + + restore_flags (flags); +} + +static void +softsyn_bender (int dev, int voice, int value) +{ + if (voice < 0 || voice > devc->maxvoice) + return; + + softoss_voices[voice].bender = value - 8192; + if (voice_active[voice]) + compute_step (voice); /* Update pitch */ +} + +static int +softsyn_alloc_voice (int dev, int chn, int note, struct voice_alloc_info *alloc) +{ + int i, p, best = -1, best_time = 0x7fffffff; + + p = alloc->ptr; + /* + * First look for a completely stopped voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0) + { + alloc->ptr = p; + voice_active[p] = 0; + return p; + } + if (alloc->alloc_times[p] < best_time) + { + best = p; + best_time = alloc->alloc_times[p]; + } + p = (p + 1) % alloc->max_voice; + } + + /* + * Then look for a releasing voice + */ + + for (i = 0; i < alloc->max_voice; i++) + { + if (alloc->map[p] == 0xffff) + { + alloc->ptr = p; + voice_active[p] = 0; + return p; + } + p = (p + 1) % alloc->max_voice; + } + + if (best >= 0) + p = best; + + alloc->ptr = p; + voice_active[p] = 0; + return p; +} + +static void +softsyn_setup_voice (int dev, int voice, int chn) +{ + unsigned long flags; + + struct channel_info *info = + &synth_devs[dev]->chn_info[chn]; + + save_flags (flags); + cli (); + /* init_voice(devc, voice); */ + softsyn_set_instr (dev, voice, info->pgm_num); + + softoss_voices[voice].expression_vol = + info->controllers[CTL_EXPRESSION]; /* Just MSB */ + softoss_voices[voice].main_vol = + (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128; + softsyn_panning (dev, voice, (info->controllers[CTL_PAN] * 2) - 128); + softoss_voices[voice].bender = 0; /* info->bender_value; */ + softoss_voices[voice].bender_range = info->bender_range; + + if (chn == 9) + softoss_voices[voice].percussive_voice = 1; + restore_flags (flags); +} + +static void +softsyn_reset (int devno) +{ + int i; + unsigned long flags; + + save_flags (flags); + cli (); + + for (i = 0; i < devc->maxvoice; i++) + init_voice (devc, i); + restore_flags (flags); +} + +static struct synth_operations softsyn_operations = +{ + "SoftOSS", + &softsyn_info, + 0, + SYNTH_TYPE_SAMPLE, + 0, + softsyn_open, + softsyn_close, + softsyn_ioctl, + softsyn_kill_note, + softsyn_start_note, + softsyn_set_instr, + softsyn_reset, + softsyn_hw_control, + softsyn_load_patch, + softsyn_aftertouch, + softsyn_controller, + softsyn_panning, + softsyn_volume_method, + softsyn_bender, + softsyn_alloc_voice, + softsyn_setup_voice +}; + +/* + * Timer stuff (for /dev/music). + */ + +static unsigned int +soft_tmr_start (int dev, unsigned int usecs) +{ + tmr_running = 1; + start_engine (devc); + return devc->usecs_per_frag; +} + +static void +soft_tmr_disable (int dev) +{ + stop_engine (devc); + tmr_running = 0; +} + +static void +soft_tmr_restart (int dev) +{ + tmr_running = 1; +} + +static struct sound_lowlev_timer soft_tmr = +{ + 0, + 9999, + soft_tmr_start, + soft_tmr_disable, + soft_tmr_restart +}; + +int +probe_softsyn (struct address_info *hw_config) +{ + int i; + + if (softsynth_loaded) + return 0; + + devc->ram_size = 8 * 1024 * 1024; + devc->ram_used = 0; + devc->nrsamples = 0; + for (i = 0; i < MAX_PATCH; i++) + { + devc->programs[i] = NO_SAMPLE; + devc->wave[i] = NULL; + } + + devc->maxvoice = DEFAULT_VOICES; + + devc->audiodev = 0; + devc->audio_opened = 0; + devc->channels = 2; + devc->bits = 16; + devc->max_playahead = 32; + +#ifdef SOFTOSS_RATE + devc->speed = SOFTOSS_RATE; +#else + devc->speed = 32000; +#endif + +#ifdef SOFTOSS_VOICES + devc->default_max_voices = SOFTOSS_VOICES; +#else + devc->default_max_voices = 32; +#endif + + softsynth_loaded = 1; + return 1; +} + +void +attach_softsyn_card (struct address_info *hw_config) +{ + + voice_alloc = &softsyn_operations.alloc; + synth_devs[num_synths++] = &softsyn_operations; + sequencer_init (); + sound_timer_init (&soft_tmr, "SoftOSS"); + devc->synthdev = num_synths; + softsynthp = softsynth_hook; + +#ifndef POLLED_MODE +#endif +} + +void +unload_softsyn (struct address_info *hw_config) +{ + if (!softsynth_loaded) + return; +#ifndef POLLED_MODE +#endif + + softsynthp = NULL; + reset_samples (devc); +} + +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/softoss.h linux/drivers/sound/softoss.h --- v2.1.62/linux/drivers/sound/softoss.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/softoss.h Tue Sep 30 08:46:45 1997 @@ -0,0 +1,160 @@ +/* + * softoss.h - Definitions for Software MIDI Synthesizer. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ + + +/* + * Sequencer mode1 timer calls made by sequencer.c + */ +extern int (*softsynthp) (int cmd, int parm1, int parm2, unsigned long parm3); + +#define SSYN_START 1 +#define SSYN_REQUEST 2 /* parm1 = time */ +#define SSYN_STOP 3 +#define SSYN_GETTIME 4 /* Returns number of ticks since reset */ + +#define MAX_PATCH 256 +#define MAX_SAMPLE 512 +#define MAX_VOICE 32 +#define DEFAULT_VOICES 16 + +typedef struct voice_info +{ +/* + * Don't change anything in the beginning of this struct. These fields are used + * by the resampling loop which may have been written in assembly for some + * architectures. Any change may make the resampling code incompatible + */ + int instr; + short *wave; + struct patch_info *sample; + + unsigned int ptr; int step; /* Pointer to the wave data and pointer increment */ + + int mode; + int startloop, startbackloop, endloop, looplen; + + unsigned int leftvol, rightvol; +/***** Don't change anything above this */ + + volatile unsigned long orig_freq, current_freq; + volatile int bender, bender_range, panning; + volatile int main_vol, expression_vol, patch_vol, velocity; + +/* Envelope parameters */ + + int envelope_phase; + volatile int envelope_vol; + volatile int envelope_volstep; + int envelope_time; /* Number of remaining envelope steps */ + unsigned int envelope_target; + int percussive_voice; + int sustain_mode; /* 0=off, 1=sustain on, 2=sustain on+key released */ + +/* Vibrato */ + int vibrato_rate; + int vibrato_depth; + int vibrato_phase; + int vibrato_step; + int vibrato_level; + +/* Tremolo */ + int tremolo_rate; + int tremolo_depth; + int tremolo_phase; + int tremolo_step; + int tremolo_level; +} voice_info; + +extern voice_info softoss_voices[MAX_VOICE]; /* Voice spesific info */ + +typedef struct softsyn_devc +{ +/* + * Don't change anything in the beginning of this struct. These fields are used + * by the resampling loop which may have been written in assembly for some + * architectures. Any change may make the resampling code incompatible + */ + int maxvoice; /* # of voices to be processed */ + int afterscale; + int delay_size; + int control_rate, control_counter; +/***** Don't change anything above this */ + + int ram_size; + int ram_used; + + int synthdev; + int sequencer_mode; +/* + * Audio parameters + */ + + int audiodev; + int audio_opened; + int speed; + int channels; + int bits; + int default_max_voices; + int max_playahead; + struct fileinfo finfo; + int fragsize; + int samples_per_fragment; + +/* + * Sample storage + */ + int nrsamples; + struct patch_info *samples[MAX_SAMPLE]; + short *wave[MAX_SAMPLE]; + +/* + * Programs + */ + int programs[MAX_SAMPLE]; + +/* + * Timer parameters + */ + volatile unsigned long usecs; + volatile unsigned long usecs_per_frag; + volatile unsigned long next_event_usecs; + +/* + * Engine state + */ + + volatile int engine_state; +#define ES_STOPPED 0 +#define ES_STARTED 1 + + /* Voice spesific bitmaps */ + volatile int tremolomap; + volatile int vibratomap; + +} softsyn_devc; + +void softsynth_resample_loop(short *buf, int loops); +extern void softsyn_control_loop(void); + +#define DELAY_SIZE 4096 + +#ifdef SOFTSYN_MAIN + short voice_active[MAX_VOICE] = {0}; + voice_info softoss_voices[MAX_VOICE] = {{0}}; /* Voice spesific info */ + int left_delay[DELAY_SIZE]={0}, right_delay[DELAY_SIZE]={0}; + int delayp=0; +#else + extern softsyn_devc *devc; + + extern int left_delay[DELAY_SIZE], right_delay[DELAY_SIZE]; + extern int delayp; + extern short voice_active[MAX_VOICE]; +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/softoss_rs.c linux/drivers/sound/softoss_rs.c --- v2.1.62/linux/drivers/sound/softoss_rs.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/sound/softoss_rs.c Tue Sep 30 08:46:55 1997 @@ -0,0 +1,139 @@ +/* + * sound/softoss_rs.c + * + * Software based MIDI synthsesizer driver, the actual mixing loop. + * Keep the loop as simple as possible to make it easier to rewrite this + * routine in assembly. + */ +/* + * Copyright (C) by Hannu Savolainen 1993-1997 + * + * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) + * Version 2 (June 1991). See the "COPYING" file distributed with this software + * for more info. + */ +#include + + +#include "sound_config.h" + +#ifdef CONFIG_SOFTOSS +#include "softoss.h" + +void +softsynth_resample_loop (short *buf, int loops) +{ + int iloop, voice; + volatile voice_info *v; + +#ifdef OSS_BIG_ENDIAN + unsigned char *cbuf = (unsigned char *) buf; + +#endif + + for (iloop = 0; iloop < loops; iloop++) + { /* Mix one sample */ + + int accum, left = 0, right = 0; + int ix, position; + + for (voice = 0; voice < devc->maxvoice; voice++) + if (voice_active[voice]) + { /* Compute voice */ + + v = &softoss_voices[voice]; +#ifdef SOFTOSS_TEST + ix = iloop << 3; + position = v->ptr; +#else + ix = (position = v->ptr) >> 9; +#endif + /* Interpolation (resolution of 512 steps) */ + { + int fract = v->ptr & 0x1f; /* 9 bits */ + + /* This method works with less arithmetic operations */ + register int v1 = v->wave[ix]; + + accum = v1 + ((((v->wave[ix + 1] - v1)) * (fract)) >> 9); + } + + left += (accum * v->leftvol); + right += (accum * v->rightvol); + + /* Update sample pointer */ + + position += v->step; + if (position <= v->endloop) + v->ptr = position; + else if (v->mode & WAVE_LOOPING) + { + if (v->mode & WAVE_BIDIR_LOOP) + { + v->mode ^= WAVE_LOOP_BACK; /* Turn around */ + v->step *= -1; + } + else + { + position -= v->looplen; + v->ptr = position; + } + } + /* else leave the voice looping the current sample */ + + if (v->mode & WAVE_LOOP_BACK && position < v->startloop) + { + if (v->mode & WAVE_BIDIR_LOOP) + { + v->mode ^= WAVE_LOOP_BACK; /* Turn around */ + v->step *= -1; + } + else + { + position += v->looplen; + v->ptr = position; + } + } + + } /* Compute voice */ + +#if 1 /* Delay */ + left += left_delay[delayp]; + right += right_delay[delayp]; + + left_delay[delayp] = right >> 2; + right_delay[delayp] = left >> 2; + delayp = (delayp + 1) % devc->delay_size; +#endif + +#define AFTERSCALE devc->afterscale; + + left >>= AFTERSCALE; + right >>= AFTERSCALE; + + if (left > 32767) + left = 32767; + if (left < -32768) + left = -32768; + if (right > 32767) + right = 32767; + if (right < -32768) + right = -32768; + +#ifdef OSS_BIG_ENDIAN + *cbuf++ = left & 0xff; + *cbuf++ = (left >> 8) & 0xff; + *cbuf++ = right & 0xff; + *cbuf++ = (right >> 8) & 0xff; +#else + *buf++ = left; + *buf++ = right; +#endif + if (devc->control_counter++ >= devc->control_rate) + { + devc->control_counter = 0; + softsyn_control_loop (); + } + } /* Mix one sample */ +} +#endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h --- v2.1.62/linux/drivers/sound/sound_calls.h Wed May 28 10:51:33 1997 +++ linux/drivers/sound/sound_calls.h Tue Sep 30 08:46:45 1997 @@ -41,6 +41,7 @@ int audio_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait); void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording); +int dma_ioctl (int dev, unsigned int cmd, caddr_t arg); /* * System calls for the /dev/sequencer @@ -83,7 +84,7 @@ */ /* From soundcard.c */ -#ifndef __bsdi__ +#if !defined(__bsdi__) && !defined(__NjetBSD__) void tenmicrosec(int *osp); #endif void request_sound_timer (int count); diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sound_config.h linux/drivers/sound/sound_config.h --- v2.1.62/linux/drivers/sound/sound_config.h Tue Mar 4 10:25:25 1997 +++ linux/drivers/sound/sound_config.h Sun Nov 9 22:14:51 1997 @@ -12,6 +12,7 @@ #include "local.h" + #include "os.h" #include "soundvers.h" @@ -145,6 +146,7 @@ #define WK_SIGNAL 0x04 #define WK_SLEEP 0x08 #define WK_SELECT 0x10 +#define WK_ABORT 0x20 #define OPEN_READ PCM_ENABLE_INPUT #define OPEN_WRITE PCM_ENABLE_OUTPUT diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sound_switch.c linux/drivers/sound/sound_switch.c --- v2.1.62/linux/drivers/sound/sound_switch.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/sound_switch.c Tue Sep 30 08:46:56 1997 @@ -195,9 +195,6 @@ put_status (" "); put_status (system_utsname.machine); put_status ("\n"); -#ifdef MODULE - put_status ("Driver loaded as a module\n"); -#endif if (!put_status ("Config options: ")) @@ -511,7 +508,10 @@ return -EBUSY; status_busy = 1; if ((status_buf = (char *) vmalloc (4000)) == NULL) - return -EIO; + { + status_busy = 0; + return -EIO; + } status_len = status_ptr = 0; init_status (); break; @@ -605,12 +605,15 @@ get_mixer_info (int dev, caddr_t arg) { mixer_info info; + int i; if (dev < 0 || dev >= num_mixers) return -ENXIO; strcpy (info.id, mixer_devs[dev]->id); - strcpy (info.name, mixer_devs[dev]->name); + for (i = 0; i < 32 && mixer_devs[dev]->name; i++) + info.name[i] = mixer_devs[dev]->name[i]; + info.name[i] = 0; info.modify_counter = mixer_devs[dev]->modify_counter; memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info)); @@ -621,12 +624,15 @@ get_old_mixer_info (int dev, caddr_t arg) { _old_mixer_info info; + int i; if (dev < 0 || dev >= num_mixers) return -ENXIO; strcpy (info.id, mixer_devs[dev]->id); - strcpy (info.name, mixer_devs[dev]->name); + for (i = 0; i < 32 && mixer_devs[dev]->name; i++) + info.name[i] = mixer_devs[dev]->name[i]; + info.name[i] = 0; memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info)); return 0; diff -u --recursive --new-file v2.1.62/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c --- v2.1.62/linux/drivers/sound/soundcard.c Tue Sep 23 16:48:48 1997 +++ linux/drivers/sound/soundcard.c Tue Nov 4 11:54:40 1997 @@ -56,31 +56,34 @@ -static long -sound_read (struct inode *inode, struct file *file, char *buf, unsigned long count) +static ssize_t +sound_read (struct file *file, char *buf, size_t count, loff_t *ppos) { int dev; + struct inode *inode = file->f_dentry->d_inode; dev = MINOR (inode->i_rdev); files[dev].flags = file->f_flags; - return sound_read_sw (dev, &files[dev], buf, count); + return (ssize_t)sound_read_sw (dev, &files[dev], buf, count); } -static long -sound_write (struct inode *inode, struct file *file, const char *buf, unsigned long count) +static ssize_t +sound_write (struct file *file, const char *buf, size_t count, loff_t *ppos) { int dev; + struct inode *inode = file->f_dentry->d_inode; dev = MINOR (inode->i_rdev); files[dev].flags = file->f_flags; - return sound_write_sw (dev, &files[dev], buf, count); + return (ssize_t)sound_write_sw (dev, &files[dev], buf, count); } -static long long sound_lseek (struct file *file, long long offset, int orig) +static long long +sound_lseek (struct file *file, long long offset, int orig) { return -EPERM; } @@ -316,8 +319,7 @@ if (size != dmap->bytes_in_use) { - printk ("Sound: mmap() size = %ld. Should be %d\n", - size, dmap->bytes_in_use); + printk ("Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use); } if (remap_page_range (vma->vm_start, virt_to_phys (dmap->raw_buf), @@ -325,7 +327,7 @@ vma->vm_page_prot)) return -EAGAIN; - vma->vm_dentry = dget(file->f_dentry); + vma->vm_dentry = file->f_dentry; dmap->mapping_flags |= DMA_MAP_MAPPED; @@ -571,7 +573,7 @@ { if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) { - /* printk ("sound_free_dma: Bad access to DMA channel %d\n", chn); */ + /* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */ return; } free_dma (chn); @@ -707,8 +709,7 @@ end_addr = start_addr + dmap->buffsize - 1; if (debugmem) - printk ("sound: start 0x%lx, end 0x%lx\n", - (long) start_addr, (long) end_addr); + printk ("sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr); /* now check if it fits into the same dma-pagesize */ @@ -716,10 +717,7 @@ != ((long) end_addr & ~(dma_pagesize - 1)) || end_addr >= (char *) (MAX_DMA_ADDRESS)) { - printk ( - "sound: Got invalid address 0x%lx for %db DMA-buffer\n", - (long) start_addr, - dmap->buffsize); + printk ("sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize); return -EFAULT; } } @@ -771,7 +769,7 @@ { unsigned long flags; - /* printk("Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */ + /* printk( "Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */ if (autoinit) dma_mode |= DMA_AUTOINIT; save_flags (flags); diff -u --recursive --new-file v2.1.62/linux/drivers/sound/soundvers.h linux/drivers/sound/soundvers.h --- v2.1.62/linux/drivers/sound/soundvers.h Wed May 28 10:51:33 1997 +++ linux/drivers/sound/soundvers.h Sun Nov 9 23:02:06 1997 @@ -1,2 +1,2 @@ -#define SOUND_VERSION_STRING "3.8a" +#define SOUND_VERSION_STRING "3.8s-971110" #define SOUND_INTERNAL_VERSION 0x030804 diff -u --recursive --new-file v2.1.62/linux/drivers/sound/sscape.c linux/drivers/sound/sscape.c --- v2.1.62/linux/drivers/sound/sscape.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/sscape.c Tue Sep 30 08:46:56 1997 @@ -15,7 +15,7 @@ #include "sound_config.h" -#if defined(CONFIG_SSCAPEHW) +#ifdef CONFIG_SSCAPEHW #include "coproc.h" @@ -213,15 +213,6 @@ return data; } -static int -host_command1 (struct sscape_info *devc, int cmd) -{ - unsigned char buf[10]; - - buf[0] = (unsigned char) (cmd & 0xff); - - return host_write (devc, buf, 1); -} static int host_command2 (struct sscape_info *devc, int cmd, int parm1) @@ -254,7 +245,7 @@ value ? 1 : 0); if (host_read (devc) != CMD_ACK) { - /* printk ("SNDSCAPE: Setting MT32 mode failed\n"); */ + /* printk( "SNDSCAPE: Setting MT32 mode failed\n"); */ } host_close (devc); } @@ -266,7 +257,7 @@ host_command3 (devc, CMD_SET_CONTROL, ctrl, value); if (host_read (devc) != CMD_ACK) { - /* printk ("SNDSCAPE: Setting control (%d) failed\n", ctrl); */ + /* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */ } host_close (devc); } diff -u --recursive --new-file v2.1.62/linux/drivers/sound/trix.c linux/drivers/sound/trix.c --- v2.1.62/linux/drivers/sound/trix.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/trix.c Tue Sep 30 08:46:56 1997 @@ -17,7 +17,7 @@ #include "sound_config.h" #include "sb.h" -#if defined(CONFIG_TRIX) +#ifdef CONFIG_TRIX #ifdef INCLUDE_TRIX_BOOT #include "trix_boot.h" @@ -188,13 +188,13 @@ if (hw_config->dma == 0 && inb (hw_config->io_base + 3) & 0x80) { - printk ("AudioTrix: Can't use DMA0 with a 8 bit card\n"); + printk ("AudioTrix: Can't use DMA0 with a 8 bit card slot\n"); return 0; } if (hw_config->irq > 7 && hw_config->irq != 9 && inb (hw_config->io_base + 3) & 0x80) { - printk ("AudioTrix: Can't use IRQ%d with a 8 bit card\n", hw_config->irq); + printk ("AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq); return 0; } diff -u --recursive --new-file v2.1.62/linux/drivers/sound/uart401.c linux/drivers/sound/uart401.c --- v2.1.62/linux/drivers/sound/uart401.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/uart401.c Tue Sep 30 08:46:57 1997 @@ -15,7 +15,8 @@ #include "sound_config.h" -#if defined(CONFIG_UART401) && defined(CONFIG_MIDI) +#ifdef CONFIG_UART401 +#ifdef CONFIG_MIDI typedef struct uart401_devc { @@ -233,7 +234,7 @@ save_flags (flags); cli (); - for (timeout = 30000; timeout < 0 && !output_ready (devc); timeout--); + for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); devc->input_byte = 0; uart401_cmd (devc, UART_MODE_ON); @@ -288,7 +289,7 @@ return; if (!devc->share_irq) - if (snd_set_irq_handler (devc->irq, uart401intr, "uart401", devc->osp) < 0) + if (snd_set_irq_handler (devc->irq, uart401intr, "MPU-401 UART", devc->osp) < 0) { printk ("uart401: Failed to allocate IRQ%d\n", devc->irq); devc->share_irq = 1; @@ -297,7 +298,7 @@ irq2devc[devc->irq] = devc; devc->my_dev = num_midis; - request_region (hw_config->io_base, 4, "SB MIDI"); + request_region (hw_config->io_base, 4, "MPU-401 UART"); enter_uart_mode (devc); if (num_midis >= MAX_MIDI_DEV) @@ -363,7 +364,7 @@ for (n = 0; n < 2 && !ok; n++) { - for (timeout = 30000; timeout < 0 && !output_ready (devc); timeout--); + for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); devc->input_byte = 0; uart401_cmd (devc, MPU_RESET); @@ -385,7 +386,7 @@ if (ok) { - DDB (printk ("Reset UART401 OK\n")); + DEB (printk ("Reset UART401 OK\n")); } else DDB (printk ("Reset UART401 failed - No hardware detected.\n")); @@ -442,7 +443,9 @@ int irq = hw_config->irq; if (irq < 0) - irq *= -1; + { + irq *= -1; + } if (irq < 1 || irq > 15) return; @@ -459,4 +462,5 @@ } +#endif #endif diff -u --recursive --new-file v2.1.62/linux/drivers/sound/uart6850.c linux/drivers/sound/uart6850.c --- v2.1.62/linux/drivers/sound/uart6850.c Wed May 28 10:51:33 1997 +++ linux/drivers/sound/uart6850.c Tue Sep 30 08:46:57 1997 @@ -16,7 +16,8 @@ #include "sound_config.h" -#if defined(CONFIG_UART6850) && defined(CONFIG_MIDI) +#ifdef CONFIG_UART6850 +#ifdef CONFIG_MIDI static int uart6850_base = 0x330; @@ -331,4 +332,5 @@ snd_release_irq (hw_config->irq); } +#endif #endif diff -u --recursive --new-file v2.1.62/linux/fs/buffer.c linux/fs/buffer.c --- v2.1.62/linux/fs/buffer.c Tue Sep 23 16:48:48 1997 +++ linux/fs/buffer.c Sat Nov 8 11:39:12 1997 @@ -553,6 +553,11 @@ return next; } +struct buffer_head *efind_buffer(kdev_t dev, int block, int size) +{ + return find_buffer(dev, block, size); +} + /* * Why like this, I hear you say... The reason is race-conditions. * As we don't lock buffers (unless we are reading them, that is), @@ -817,6 +822,24 @@ goto repeat; } +void init_buffer(struct buffer_head *bh, kdev_t dev, int block, + bh_end_io_t *handler, void *dev_id) +{ + bh->b_count = 1; + bh->b_list = BUF_CLEAN; + bh->b_flushtime = 0; + bh->b_dev = dev; + bh->b_blocknr = block; + bh->b_end_io = handler; + bh->b_dev_id = dev_id; +} + +static void end_buffer_io_sync(struct buffer_head *bh, int uptodate) +{ + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); +} + /* * Ok, this is getblk, and it isn't very clear, again to hinder * race-conditions. Most of the code is seldom used, (ie repeating), @@ -854,13 +877,9 @@ /* OK, FINALLY we know that this buffer is the only one of its kind, * and that it's unused (b_count=0), unlocked, and clean. */ - bh->b_count=1; - bh->b_list = BUF_CLEAN; + init_buffer(bh, dev, block, end_buffer_io_sync, NULL); bh->b_lru_time = jiffies; - bh->b_flushtime=0; bh->b_state=(1<b_dev=dev; - bh->b_blocknr=block; insert_into_queues(bh); return bh; @@ -1254,18 +1273,75 @@ tmp = bh; do { - if (!test_bit(BH_FreeOnIO, &tmp->b_state)) { - printk ("Whoops: unlock_buffer: " - "async IO mismatch on page.\n"); - return; - } tmp->b_next_free = xchg(&reuse_list, NULL); reuse_list = tmp; - clear_bit(BH_FreeOnIO, &tmp->b_state); tmp = tmp->b_this_page; } while (tmp != bh); } +static void end_buffer_io_async(struct buffer_head * bh, int uptodate) +{ + unsigned long flags; + struct buffer_head *tmp; + struct page *page; + + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); + + /* This is a temporary buffer used for page I/O. */ + page = mem_map + MAP_NR(bh->b_data); + if (!PageLocked(page)) + goto not_locked; + if (bh->b_count != 1) + goto bad_count; + + if (!test_bit(BH_Uptodate, &bh->b_state)) + set_bit(PG_error, &page->flags); + + /* + * Be _very_ careful from here on. Bad things can happen if + * two buffer heads end IO at almost the same time and both + * decide that the page is now completely done. + * + * Async buffer_heads are here only as labels for IO, and get + * thrown away once the IO for this page is complete. IO is + * deemed complete once all buffers have been visited + * (b_count==0) and are now unlocked. We must make sure that + * only the _last_ buffer that decrements its count is the one + * that free's the page.. + */ + save_flags(flags); + cli(); + bh->b_count--; + tmp = bh; + do { + if (tmp->b_count) + goto still_busy; + tmp = tmp->b_this_page; + } while (tmp != bh); + + /* OK, the async IO on this page is complete. */ + free_async_buffers(bh); + restore_flags(flags); + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); + after_unlock_page(page); + wake_up(&buffer_wait); + return; + +still_busy: + restore_flags(flags); + return; + +not_locked: + printk ("Whoops: end_buffer_io_async: async io complete on unlocked page\n"); + return; + +bad_count: + printk ("Whoops: end_buffer_io_async: b_count != 1 on async io.\n"); + return; +} + /* * Start I/O on a page. * This function expects the page to be locked and may return before I/O is complete. @@ -1298,12 +1374,7 @@ struct buffer_head * tmp; block = *(b++); - set_bit(BH_FreeOnIO, &next->b_state); - next->b_list = BUF_CLEAN; - next->b_dev = dev; - next->b_blocknr = block; - next->b_count = 1; - next->b_flushtime = 0; + init_buffer(next, dev, block, end_buffer_io_async, NULL); set_bit(BH_Uptodate, &next->b_state); /* @@ -1383,74 +1454,6 @@ } /* - * This is called by end_request() when I/O has completed. - */ -void unlock_buffer(struct buffer_head * bh) -{ - unsigned long flags; - struct buffer_head *tmp; - struct page *page; - - clear_bit(BH_Lock, &bh->b_state); - wake_up(&bh->b_wait); - - if (!test_bit(BH_FreeOnIO, &bh->b_state)) - return; - /* This is a temporary buffer used for page I/O. */ - page = mem_map + MAP_NR(bh->b_data); - if (!PageLocked(page)) - goto not_locked; - if (bh->b_count != 1) - goto bad_count; - - if (!test_bit(BH_Uptodate, &bh->b_state)) - set_bit(PG_error, &page->flags); - - /* - * Be _very_ careful from here on. Bad things can happen if - * two buffer heads end IO at almost the same time and both - * decide that the page is now completely done. - * - * Async buffer_heads are here only as labels for IO, and get - * thrown away once the IO for this page is complete. IO is - * deemed complete once all buffers have been visited - * (b_count==0) and are now unlocked. We must make sure that - * only the _last_ buffer that decrements its count is the one - * that free's the page.. - */ - save_flags(flags); - cli(); - bh->b_count--; - tmp = bh; - do { - if (tmp->b_count) - goto still_busy; - tmp = tmp->b_this_page; - } while (tmp != bh); - - /* OK, the async IO on this page is complete. */ - free_async_buffers(bh); - restore_flags(flags); - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); - after_unlock_page(page); - wake_up(&buffer_wait); - return; - -still_busy: - restore_flags(flags); - return; - -not_locked: - printk ("Whoops: unlock_buffer: async io complete on unlocked page\n"); - return; - -bad_count: - printk ("Whoops: unlock_buffer: b_count != 1 on async io.\n"); - return; -} - -/* * Generic "readpage" function for block devices that have the normal * bmap functionality. This is most of the block device filesystems. * Reads the page asynchronously --- the unlock_buffer() and @@ -1755,7 +1758,7 @@ if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount); printk("Wrote %d/%d buffers\n", nwritten, ndirty); #endif - + run_task_queue(&tq_disk); return 0; } @@ -1916,7 +1919,7 @@ * dirty buffers, then make the next write to a * loop device to be a blocking write. * This lets us block--which we _must_ do! */ - if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0) { + if (ndirty == 0 && nr_buffers_type[BUF_DIRTY] > 0 && wrta_cmd != WRITE) { wrta_cmd = WRITE; continue; } @@ -1925,7 +1928,7 @@ /* If there are still a lot of dirty buffers around, skip the sleep and flush some more */ - if(nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) { + if(ndirty == 0 || nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) { current->signal = 0; interruptible_sleep_on(&bdflush_wait); } diff -u --recursive --new-file v2.1.62/linux/fs/dcache.c linux/fs/dcache.c --- v2.1.62/linux/fs/dcache.c Sat Nov 1 11:04:27 1997 +++ linux/fs/dcache.c Tue Nov 4 10:26:38 1997 @@ -97,12 +97,14 @@ goto out; } - if (!list_empty(&dentry->d_lru)) + if (!list_empty(&dentry->d_lru)) { dentry_stat.nr_unused--; - list_del(&dentry->d_lru); + list_del(&dentry->d_lru); + } if (list_empty(&dentry->d_hash)) { struct inode *inode = dentry->d_inode; struct dentry * parent; + list_del(&dentry->d_child); if (inode) { dentry->d_inode = NULL; iput(inode); @@ -247,6 +249,7 @@ struct dentry * parent; list_del(&dentry->d_hash); + list_del(&dentry->d_child); if (dentry->d_inode) { struct inode * inode = dentry->d_inode; @@ -338,6 +341,69 @@ } /* + * Search the dentry child list for the specified parent, + * and move any unused dentries to the end of the unused + * list for prune_dcache(). We descend to the next level + * whenever the d_subdirs list is non-empty and continue + * searching. + */ +static int select_parent(struct dentry * parent) +{ + struct dentry *this_parent = parent; + struct list_head *next; + int found = 0; + +repeat: + next = this_parent->d_subdirs.next; +resume: + while (next != &this_parent->d_subdirs) { + struct list_head *tmp = next; + struct dentry *dentry = list_entry(tmp, struct dentry, d_child); + next = tmp->next; + if (!dentry->d_count) { + list_del(&dentry->d_lru); + list_add(&dentry->d_lru, dentry_unused.prev); + found++; + } + /* + * Descend a level if the d_subdirs list is non-empty. + */ + if (!list_empty(&dentry->d_subdirs)) { + this_parent = dentry; +#ifdef DCACHE_DEBUG +printk("select_parent: descending to %s/%s, found=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, found); +#endif + goto repeat; + } + } + /* + * All done at this level ... ascend and resume the search. + */ + if (this_parent != parent) { + next = this_parent->d_child.next; + this_parent = this_parent->d_parent; +#ifdef DCACHE_DEBUG +printk("select_parent: ascending to %s/%s, found=%d\n", +this_parent->d_parent->d_name.name, this_parent->d_name.name, found); +#endif + goto resume; + } + return found; +} + +/* + * Prune the dcache to remove unused children of the parent dentry. + */ +void shrink_dcache_parent(struct dentry * parent) +{ + int found; + + while ((found = select_parent(parent)) != 0) + prune_dcache(found); +} + +/* * This is called from do_try_to_free_page() to indicate * that we should reduce the dcache and inode cache memory. */ @@ -415,11 +481,15 @@ if (parent) { dentry->d_parent = dget(parent); dentry->d_sb = parent->d_sb; - } + list_add(&dentry->d_child, &parent->d_subdirs); + } else + INIT_LIST_HEAD(&dentry->d_child); + dentry->d_mounts = dentry; dentry->d_covers = dentry; INIT_LIST_HEAD(&dentry->d_hash); INIT_LIST_HEAD(&dentry->d_lru); + INIT_LIST_HEAD(&dentry->d_subdirs); dentry->d_name.name = str; dentry->d_name.len = name->len; @@ -608,11 +678,16 @@ list_del(&target->d_hash); INIT_LIST_HEAD(&target->d_hash); + list_del(&dentry->d_child); + list_del(&target->d_child); + /* Switch the parents and the names.. */ switch(dentry->d_parent, target->d_parent); switch(dentry->d_name.name, target->d_name.name); switch(dentry->d_name.len, target->d_name.len); switch(dentry->d_name.hash, target->d_name.hash); + list_add(&target->d_child, &target->d_parent->d_subdirs); + list_add(&dentry->d_child, &dentry->d_parent->d_subdirs); } /* diff -u --recursive --new-file v2.1.62/linux/fs/ext2/namei.c linux/fs/ext2/namei.c --- v2.1.62/linux/fs/ext2/namei.c Mon Aug 11 14:47:05 1997 +++ linux/fs/ext2/namei.c Sat Nov 8 11:46:32 1997 @@ -353,8 +353,6 @@ struct ext2_dir_entry * de; int err = -EIO; - if (!dir) - return -ENOENT; /* * N.B. Several error exits in ext2_new_inode don't set err. */ @@ -391,15 +389,13 @@ struct ext2_dir_entry * de; int err = -EIO; - if (!dir) - return -ENOENT; - + err = -ENAMETOOLONG; if (dentry->d_name.len > EXT2_NAME_LEN) - return -ENAMETOOLONG; + goto out; inode = ext2_new_inode (dir, mode, &err); if (!inode) - return err; + goto out; inode->i_uid = current->fsuid; inode->i_mode = mode; @@ -423,12 +419,8 @@ inode->i_rdev = to_kdev_t(rdev); mark_inode_dirty(inode); bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); - if (!bh) { - inode->i_nlink--; - mark_inode_dirty(inode); - iput(inode); - return err; - } + if (!bh) + goto out_no_entry; de->inode = cpu_to_le32(inode->i_ino); dir->i_version = ++event; mark_buffer_dirty(bh, 1); @@ -436,9 +428,17 @@ ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } - brelse(bh); d_instantiate(dentry, inode); - return 0; + brelse(bh); + err = 0; +out: + return err; + +out_no_entry: + inode->i_nlink--; + mark_inode_dirty(inode); + iput(inode); + goto out; } int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode) @@ -446,23 +446,26 @@ struct inode * inode; struct buffer_head * bh, * dir_block; struct ext2_dir_entry * de; - int err = -EIO; + int err; + err = -ENAMETOOLONG; if (dentry->d_name.len > EXT2_NAME_LEN) - return -ENAMETOOLONG; + goto out; + err = -EMLINK; if (dir->i_nlink >= EXT2_LINK_MAX) - return -EMLINK; + goto out; + err = -EIO; inode = ext2_new_inode (dir, S_IFDIR, &err); if (!inode) - return err; + goto out; inode->i_op = &ext2_dir_inode_operations; inode->i_size = inode->i_sb->s_blocksize; dir_block = ext2_bread (inode, 0, 1, &err); if (!dir_block) { - inode->i_nlink--; + inode->i_nlink--; /* is this nlink == 0? */ mark_inode_dirty(inode); iput (inode); return err; @@ -486,12 +489,8 @@ inode->i_mode |= S_ISGID; mark_inode_dirty(inode); bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); - if (!bh) { - inode->i_nlink = 0; - mark_inode_dirty(inode); - iput (inode); - return err; - } + if (!bh) + goto out_no_entry; de->inode = cpu_to_le32(inode->i_ino); dir->i_version = ++event; mark_buffer_dirty(bh, 1); @@ -503,7 +502,15 @@ mark_inode_dirty(dir); d_instantiate(dentry, inode); brelse (bh); - return 0; + err = 0; +out: + return err; + +out_no_entry: + inode->i_nlink = 0; + mark_inode_dirty(inode); + iput (inode); + goto out; } /* @@ -572,25 +579,23 @@ struct buffer_head * bh; struct ext2_dir_entry * de; - if (!dir) - return -ENOENT; - inode = NULL; + retval = -ENAMETOOLONG; if (dentry->d_name.len > EXT2_NAME_LEN) - return -ENAMETOOLONG; + goto out; - bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); retval = -ENOENT; + bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); if (!bh) goto end_rmdir; - retval = -EPERM; - inode = dentry->d_inode; + inode = dentry->d_inode; if (inode->i_sb->dq_op) inode->i_sb->dq_op->initialize (inode, -1); - if ((dir->i_mode & S_ISVTX) && !fsuser() && - current->fsuid != inode->i_uid && - current->fsuid != dir->i_uid) + retval = -EPERM; + if ((dir->i_mode & S_ISVTX) && + current->fsuid != inode->i_uid && + current->fsuid != dir->i_uid && !fsuser()) goto end_rmdir; if (inode == dir) /* we may not delete ".", but "../dir" is ok */ goto end_rmdir; @@ -606,12 +611,19 @@ goto end_rmdir; down(&inode->i_sem); + /* + * Prune any child dentries so that this dentry becomes negative. + */ + if (dentry->d_count > 1) { + printk("ext2_rmdir: d_count=%d, pruning\n", dentry->d_count); + shrink_dcache_parent(dentry); + } if (!empty_dir (inode)) retval = -ENOTEMPTY; else if (le32_to_cpu(de->inode) != inode->i_ino) retval = -ENOENT; else { - if (inode->i_count > 1) { + if (dentry->d_count > 1) { /* * Are we deleting the last instance of a busy directory? * Better clean up if so. @@ -646,6 +658,7 @@ end_rmdir: brelse (bh); +out: return retval; } @@ -656,11 +669,11 @@ struct buffer_head * bh; struct ext2_dir_entry * de; - retval = -ENOENT; - inode = NULL; + retval = -ENAMETOOLONG; if (dentry->d_name.len > EXT2_NAME_LEN) - return -ENAMETOOLONG; + goto out; + retval = -ENOENT; bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de); if (!bh) goto end_unlink; @@ -674,9 +687,9 @@ goto end_unlink; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto end_unlink; - if ((dir->i_mode & S_ISVTX) && !fsuser() && + if ((dir->i_mode & S_ISVTX) && current->fsuid != inode->i_uid && - current->fsuid != dir->i_uid) + current->fsuid != dir->i_uid && !fsuser()) goto end_unlink; retval = -EIO; @@ -708,13 +721,14 @@ end_unlink: brelse (bh); +out: return retval; } int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname) { struct ext2_dir_entry * de; - struct inode * inode = NULL; + struct inode * inode; struct buffer_head * bh = NULL, * name_block = NULL; char * link; int i, l, err = -EIO; @@ -758,12 +772,8 @@ mark_inode_dirty(inode); bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err); - if (!bh) { - inode->i_nlink--; - mark_inode_dirty(inode); - iput (inode); - return err; - } + if (!bh) + goto out_no_entry; de->inode = cpu_to_le32(inode->i_ino); dir->i_version = ++event; mark_buffer_dirty(bh, 1); @@ -773,7 +783,15 @@ } brelse (bh); d_instantiate(dentry, inode); - return 0; + err = 0; +out: + return err; + +out_no_entry: + inode->i_nlink--; + mark_inode_dirty(inode); + iput (inode); + goto out; } int ext2_link (struct inode * inode, struct inode * dir, struct dentry *dentry) @@ -888,10 +906,9 @@ new_inode->i_sb->dq_op->initialize (new_inode, -1); } } - if (new_inode == old_inode) { - retval = 0; + retval = 0; + if (new_inode == old_inode) goto end_rename; - } if (new_inode && S_ISDIR(new_inode->i_mode)) { retval = -EISDIR; if (!S_ISDIR(old_inode->i_mode)) @@ -903,7 +920,7 @@ if (!empty_dir (new_inode)) goto end_rename; retval = -EBUSY; - if (new_inode->i_count > 1) + if (new_dentry->d_count > 1) goto end_rename; } retval = -EPERM; diff -u --recursive --new-file v2.1.62/linux/fs/fat/cache.c linux/fs/fat/cache.c --- v2.1.62/linux/fs/fat/cache.c Sat Oct 25 02:44:17 1997 +++ linux/fs/fat/cache.c Thu Nov 6 17:42:44 1997 @@ -188,7 +188,8 @@ && walk->ino == inode->i_ino && walk->file_cluster == f_clu) { if (walk->disk_cluster != d_clu) { - printk("FAT cache corruption"); + printk("FAT cache corruption inode=%ld\n", + inode->i_ino); fat_cache_inval_inode(inode); return; } diff -u --recursive --new-file v2.1.62/linux/fs/fat/dir.c linux/fs/fat/dir.c --- v2.1.62/linux/fs/fat/dir.c Sat Oct 25 02:44:17 1997 +++ linux/fs/fat/dir.c Sat Nov 8 11:58:11 1997 @@ -97,7 +97,7 @@ return (op - ascii); } - +#if 0 static void dump_de(struct msdos_dir_entry *de) { int i; @@ -109,7 +109,7 @@ } printk("]\n"); } - +#endif int fat_readdirx( struct inode *inode, struct file *filp, diff -u --recursive --new-file v2.1.62/linux/fs/fat/inode.c linux/fs/fat/inode.c --- v2.1.62/linux/fs/fat/inode.c Sat Oct 25 02:44:17 1997 +++ linux/fs/fat/inode.c Thu Nov 6 17:42:44 1997 @@ -5,7 +5,6 @@ * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner */ -#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) #include #define __NO_VERSION__ #include @@ -25,79 +24,111 @@ #include "msbuffer.h" -#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0) #include -#define FAT_COPY_TO_USER(uaddr, kaddr, len) copy_to_user(uaddr, kaddr, len) -#else -#include -#define FAT_COPY_TO_USER(uaddr, kaddr, len) memcpy_tofs(uaddr, kaddr, len) -#endif #include -#if 0 +/* #define FAT_PARANOIA 1 */ +#ifdef FAT_DEBUG # define PRINTK(x) printk x #else # define PRINTK(x) #endif -void fat_put_inode(struct inode *inode) -{ - struct inode *depend, *linked; - struct super_block *sb; - - depend = MSDOS_I(inode)->i_depend; - linked = MSDOS_I(inode)->i_linked; - sb = inode->i_sb; - if (inode->i_nlink) { - if (depend) { - iput(depend); - } - if (linked) { - iput(linked); - MSDOS_I(inode)->i_linked = NULL; - } - if (MSDOS_I(inode)->i_busy) fat_cache_inval_inode(inode); - } -} - -void fat_delete_inode(struct inode *inode) +/* + * Free any dependent inodes at the (effective) last use. + */ +static int fat_free_links(struct inode *inode) { - struct inode *depend, *linked; - struct super_block *sb; + struct inode *depend, *linked, *old_inode; + int success = 0; + /* + * Clear the fields first to avoid races + */ depend = MSDOS_I(inode)->i_depend; + MSDOS_I(inode)->i_depend = NULL; linked = MSDOS_I(inode)->i_linked; - sb = inode->i_sb; + MSDOS_I(inode)->i_linked = NULL; - inode->i_size = 0; - fat_truncate(inode); if (depend) { - if (MSDOS_I(depend)->i_old != inode) { - printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n", - depend, inode, MSDOS_I(depend)->i_old); - fat_fs_panic(sb,"..."); - goto done; +#ifdef FAT_PARANOIA +printk("fat_put_inode: depend inode is %ld, i_count=%d\n", +depend->i_ino, depend->i_count); +#endif + old_inode = MSDOS_I(depend)->i_old; + if (old_inode != inode) { + printk("fat_free_link: Invalid depend for inode %ld: " + "expected 0x%p, got 0x%p\n", + depend->i_ino, inode, old_inode); + goto out; } MSDOS_I(depend)->i_old = NULL; iput(depend); } + if (linked) { - if (MSDOS_I(linked)->i_oldlink != inode) { - printk("Invalid link (0x%p): expected 0x%p, got 0x%p\n", - linked, inode, MSDOS_I(linked)->i_oldlink); - fat_fs_panic(sb,"..."); - goto done; +#ifdef FAT_PARANOIA +printk("fat_put_inode: linked inode is %ld, i_count=%d\n", +linked->i_ino, linked->i_count); +#endif + old_inode = MSDOS_I(linked)->i_oldlink; + if (old_inode != inode) { + printk("fat_free_link: Invalid link for inode %ld: " + "expected 0x%p, got 0x%p\n", + linked->i_ino, inode, old_inode); + goto out; } MSDOS_I(linked)->i_oldlink = NULL; iput(linked); } -done: + success = 1; +out: + return success; +} + +/* + * This is a little tricky, as we may have links and may be linked + * by other inodes. Also, we're subject to race conditions ... + */ +void fat_put_inode(struct inode *inode) +{ + int last_use = 1; + + /* + * Check whether we're a dependent of other inodes ... + */ + if (MSDOS_I(inode)->i_oldlink) + last_use++; + if (MSDOS_I(inode)->i_old) + last_use++; + + if (inode->i_count <= last_use) { +#ifdef FAT_PARANOIA +printk("fat_put_inode: last use for %ld, i_count=%d\n", +inode->i_ino, inode->i_count); +#endif + if (inode->i_nlink) { + if (MSDOS_I(inode)->i_busy) + fat_cache_inval_inode(inode); + fat_free_links(inode); + } + } +} + +void fat_delete_inode(struct inode *inode) +{ + fat_cache_inval_inode(inode); + inode->i_size = 0; + fat_truncate(inode); + if (!fat_free_links(inode)) + fat_fs_panic(inode->i_sb,"..."); /* is this necessary? */ clear_inode(inode); } void fat_put_super(struct super_block *sb) { + lock_super(sb); if (MSDOS_SB(sb)->fat_bits == 32) { fat_clusters_flush(sb); } @@ -111,12 +142,15 @@ if (MSDOS_SB(sb)->nls_io) { unload_nls(MSDOS_SB(sb)->nls_io); MSDOS_SB(sb)->nls_io = NULL; - if (MSDOS_SB(sb)->options.iocharset) { - kfree(MSDOS_SB(sb)->options.iocharset); - MSDOS_SB(sb)->options.iocharset = NULL; - } } - lock_super(sb); + /* + * Note: the iocharset option might have been specified + * without enabling nls_io, so check for it here. + */ + if (MSDOS_SB(sb)->options.iocharset) { + kfree(MSDOS_SB(sb)->options.iocharset); + MSDOS_SB(sb)->options.iocharset = NULL; + } sb->s_dev = 0; unlock_super(sb); MOD_DEC_USE_COUNT; @@ -129,7 +163,7 @@ { char *this_char,*value,save,*savep; char *p; - int ret, len; + int ret = 1, len; opts->name_check = 'n'; opts->conversion = 'b'; @@ -142,11 +176,12 @@ opts->iocharset = NULL; *debug = *fat = 0; - if (!options) return 1; + if (!options) + goto out; save = 0; savep = NULL; - ret = 1; - for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { + for (this_char = strtok(options,","); this_char; + this_char = strtok(NULL,",")) { if ((value = strchr(this_char,'=')) != NULL) { save = *value; savep = value; @@ -155,17 +190,23 @@ if (!strcmp(this_char,"check") && value) { if (value[0] && !value[1] && strchr("rns",*value)) opts->name_check = *value; - else if (!strcmp(value,"relaxed")) opts->name_check = 'r'; - else if (!strcmp(value,"normal")) opts->name_check = 'n'; - else if (!strcmp(value,"strict")) opts->name_check = 's'; + else if (!strcmp(value,"relaxed")) + opts->name_check = 'r'; + else if (!strcmp(value,"normal")) + opts->name_check = 'n'; + else if (!strcmp(value,"strict")) + opts->name_check = 's'; else ret = 0; } else if (!strcmp(this_char,"conv") && value) { if (value[0] && !value[1] && strchr("bta",*value)) opts->conversion = *value; - else if (!strcmp(value,"binary")) opts->conversion = 'b'; - else if (!strcmp(value,"text")) opts->conversion = 't'; - else if (!strcmp(value,"auto")) opts->conversion = 'a'; + else if (!strcmp(value,"binary")) + opts->conversion = 'b'; + else if (!strcmp(value,"text")) + opts->conversion = 't'; + else if (!strcmp(value,"auto")) + opts->conversion = 'a'; else ret = 0; } else if (!strcmp(this_char,"dots")) { @@ -222,8 +263,11 @@ } else if (!strcmp(this_char,"blocksize")) { if (*value) ret = 0; - else if (*blksize != 512 && *blksize != 1024 && *blksize != 2048){ - printk ("MSDOS FS: Invalid blocksize (512, 1024, or 2048)\n"); + else if (*blksize != 512 && + *blksize != 1024 && + *blksize != 2048) { + printk ("MSDOS FS: Invalid blocksize " + "(512, 1024, or 2048)\n"); } } else if (!strcmp(this_char,"sys_immutable")) { @@ -233,46 +277,54 @@ else if (!strcmp(this_char,"codepage")) { opts->codepage = simple_strtoul(value,&value,0); if (*value) ret = 0; - else printk ("MSDOS FS: Using codepage %d\n", opts->codepage); + else printk ("MSDOS FS: Using codepage %d\n", + opts->codepage); } else if (!strcmp(this_char,"iocharset")) { p = value; while (*value && *value != ',') value++; len = value - p; - if (len) { - opts->iocharset = kmalloc(len+1, GFP_KERNEL); - memcpy(opts->iocharset, p, len); - opts->iocharset[len] = 0; - printk ("MSDOS FS: Using IO charset %s\n", - opts->iocharset); - } else { - opts->iocharset = NULL; - ret = 0; + if (len) { + char * buffer = kmalloc(len+1, GFP_KERNEL); + if (buffer) { + opts->iocharset = buffer; + memcpy(buffer, p, len); + buffer[len] = 0; + printk("MSDOS FS: IO charset %s\n", + buffer); + } else + ret = 0; } } if (this_char != options) *(this_char-1) = ','; if (value) *savep = save; - if (ret == 0) return 0; + if (ret == 0) + break; } - return 1; +out: + return ret; } - -/* Read the super block of an MS-DOS FS. */ - -struct super_block *fat_read_super(struct super_block *sb,void *data, int silent) +/* + * Read the super block of an MS-DOS FS. + * + * Note that this may be called from vfat_read_super + * with some fields already initialized. + */ +struct super_block * +fat_read_super(struct super_block *sb, void *data, int silent) { + struct inode *root_inode; struct buffer_head *bh; struct fat_boot_sector *b; + char *p; int data_sectors,logical_sector_size,sector_mult,fat_clusters=0; int debug,error,fat,cp; int blksize = 512; int fat32; struct fat_mount_options opts; char buf[50]; - char *p; - struct inode *root_inode; MOD_INC_USE_COUNT; if (hardsect_size[MAJOR(sb->s_dev)] != NULL){ @@ -281,14 +333,14 @@ printk ("MSDOS: Hardware sector size is %d\n",blksize); } } + opts.isvfat = MSDOS_SB(sb)->options.isvfat; if (!parse_options((char *) data, &fat, &blksize, &debug, &opts) - || (blksize != 512 && blksize != 1024 && blksize != 2048)) - { - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } + || (blksize != 512 && blksize != 1024 && blksize != 2048)) + goto out_fail; + /* N.B. we should parse directly into the sb structure */ + memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options)); + cache_init(); lock_super(sb); if( blksize > 1024 ) @@ -307,10 +359,7 @@ unlock_super(sb); if (bh == NULL || !fat_is_uptodate(sb,bh)) { fat_brelse (sb, bh); - sb->s_dev = 0; - printk("FAT bread failed\n"); - MOD_DEC_USE_COUNT; - return NULL; + goto out_no_bread; } set_blocksize(sb->s_dev, blksize); @@ -351,7 +400,9 @@ fsinfo = (struct fat_boot_fsinfo *) &bh->b_data[MSDOS_SB(sb)->fsinfo_offset]; if (CF_LE_L(fsinfo->signature) != 0x61417272) { - printk("fat_read_super: Did not find valid FSINFO signature. Found 0x%x\n", CF_LE_L(fsinfo->signature)); + printk("fat_read_super: Did not find valid FSINFO " + "signature. Found 0x%x\n", + CF_LE_L(fsinfo->signature)); } else { MSDOS_SB(sb)->free_clusters = CF_LE_L(fsinfo->free_clusters); } @@ -405,8 +456,10 @@ opts.conversion,opts.fs_uid,opts.fs_gid,opts.fs_umask, MSDOS_CAN_BMAP(MSDOS_SB(sb)) ? ",bmap" : ""); printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d," - "se=%d,ts=%ld,ls=%d,rc=%ld,fc=%u]\n",b->media,MSDOS_SB(sb)->cluster_size, - MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->fat_length, + "se=%d,ts=%ld,ls=%d,rc=%ld,fc=%u]\n", + b->media,MSDOS_SB(sb)->cluster_size, + MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start, + MSDOS_SB(sb)->fat_length, MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries, MSDOS_SB(sb)->data_start, CF_LE_W(*(unsigned short *) &b->sectors), @@ -416,67 +469,70 @@ } if (MSDOS_SB(sb)->clusters+2 > fat_clusters) MSDOS_SB(sb)->clusters = fat_clusters-2; - if (error) { - if (!silent) - printk("VFS: Can't find a valid MSDOS filesystem on dev " - "%s.\n", kdevname(sb->s_dev)); - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } + if (error) + goto out_invalid; + sb->s_magic = MSDOS_SUPER_MAGIC; /* set up enough so that it can read an inode */ MSDOS_SB(sb)->fat_wait = NULL; MSDOS_SB(sb)->fat_lock = 0; MSDOS_SB(sb)->prev_free = 0; - memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options)); cp = opts.codepage ? opts.codepage : 437; sprintf(buf, "cp%d", cp); MSDOS_SB(sb)->nls_disk = load_nls(buf); if (! MSDOS_SB(sb)->nls_disk) { /* Fail only if explicit charset specified */ - if (opts.codepage == 0) { - MSDOS_SB(sb)->options.codepage = 0; - MSDOS_SB(sb)->nls_disk = load_nls_default(); - } else { - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } + if (opts.codepage != 0) + goto out_fail; + MSDOS_SB(sb)->options.codepage = 0; /* already 0?? */ + MSDOS_SB(sb)->nls_disk = load_nls_default(); } - p = opts.iocharset ? opts.iocharset : "iso8859-1"; + MSDOS_SB(sb)->nls_io = NULL; if (MSDOS_SB(sb)->options.isvfat && !opts.utf8) { + p = opts.iocharset ? opts.iocharset : "iso8859-1"; MSDOS_SB(sb)->nls_io = load_nls(p); if (! MSDOS_SB(sb)->nls_io) { /* Fail only if explicit charset specified */ - if (opts.iocharset) { - kfree(opts.iocharset); - unload_nls(MSDOS_SB(sb)->nls_disk); - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } else { - MSDOS_SB(sb)->nls_io = load_nls_default(); - } + if (opts.iocharset) + goto out_unload_nls; + MSDOS_SB(sb)->nls_io = load_nls_default(); } } - root_inode = iget(sb,MSDOS_ROOT_INO); + root_inode = iget(sb, MSDOS_ROOT_INO); + if (!root_inode) + goto out_no_root; sb->s_root = d_alloc_root(root_inode, NULL); - if (!sb->s_root) { - sb->s_dev = 0; - printk("get root inode failed\n"); - unload_nls(MSDOS_SB(sb)->nls_disk); - if (MSDOS_SB(sb)->nls_io) unload_nls(MSDOS_SB(sb)->nls_io); - if (opts.iocharset) kfree(opts.iocharset); - MOD_DEC_USE_COUNT; - return NULL; - } + if (!sb->s_root) + goto out_no_root; return sb; -} +out_no_root: + printk("get root inode failed\n"); + iput(root_inode); + if (MSDOS_SB(sb)->nls_io) + unload_nls(MSDOS_SB(sb)->nls_io); +out_unload_nls: + unload_nls(MSDOS_SB(sb)->nls_disk); + goto out_fail; +out_invalid: + if (!silent) + printk("VFS: Can't find a valid MSDOS filesystem on dev %s.\n", + kdevname(sb->s_dev)); + goto out_fail; +out_no_bread: + printk("FAT bread failed\n"); +out_fail: + if (opts.iocharset) { + printk("VFS: freeing iocharset=%s\n", opts.iocharset); + kfree(opts.iocharset); + } + sb->s_dev = 0; + MOD_DEC_USE_COUNT; + return NULL; +} int fat_statfs(struct super_block *sb,struct statfs *buf, int bufsiz) { diff -u --recursive --new-file v2.1.62/linux/fs/fat/mmap.c linux/fs/fat/mmap.c --- v2.1.62/linux/fs/fat/mmap.c Sat Oct 25 02:44:17 1997 +++ linux/fs/fat/mmap.c Sat Nov 8 11:57:40 1997 @@ -21,11 +21,7 @@ #include #include -#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0) #include -#else -#include -#endif #include /* diff -u --recursive --new-file v2.1.62/linux/fs/filesystems.c linux/fs/filesystems.c --- v2.1.62/linux/fs/filesystems.c Sat Oct 25 02:44:17 1997 +++ linux/fs/filesystems.c Fri Nov 7 09:41:54 1997 @@ -33,6 +33,7 @@ #include #include #include +#include extern void device_setup(void); extern void binfmt_setup(void); diff -u --recursive --new-file v2.1.62/linux/fs/msdos/namei.c linux/fs/msdos/namei.c --- v2.1.62/linux/fs/msdos/namei.c Sat Oct 25 02:44:17 1997 +++ linux/fs/msdos/namei.c Thu Nov 6 17:45:06 1997 @@ -21,9 +21,10 @@ #include "../fat/msbuffer.h" +#define MSDOS_PARANOIA 1 +/* #define MSDOS_DEBUG 1 */ #define PRINTK(x) - /* MS-DOS "device special files" */ static const char *reserved_names[] = { @@ -256,7 +257,7 @@ dentry->d_op = &msdos_dentry_operations; - if(!dir) { + if(!dir) { /* N.B. This test is bogus -- should never happen */ d_add(dentry, NULL); return 0; } @@ -283,7 +284,7 @@ } if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */ iput(inode); - d_add(dentry, NULL); + d_add(dentry, NULL); /* N.B. Do we really want a negative? */ return 0; } PRINTK (("msdos_lookup 6\n")); @@ -292,8 +293,7 @@ iput(inode); if (!(inode = iget(next->i_sb,next->i_ino))) { fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen"); - d_add(dentry, NULL); - return -ENOENT; + return -ENOENT; /* N.B. Maybe ENOMEM is better? */ } } PRINTK (("msdos_lookup 7\n")); @@ -382,7 +382,8 @@ res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid, &inode); fat_unlock_creation(); - d_instantiate(dentry, inode); + if (!res) + d_instantiate(dentry, inode); return res; } @@ -409,61 +410,77 @@ /***** See if directory is empty */ static int msdos_empty(struct inode *dir) { - struct super_block *sb = dir->i_sb; loff_t pos; struct buffer_head *bh; struct msdos_dir_entry *de; + int result = 0; - if (dir->i_count > 1) - return -EBUSY; if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */ pos = 0; bh = NULL; - while (fat_get_entry(dir,&pos,&bh,&de) > -1) - if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT, - MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT, - MSDOS_NAME)) { - fat_brelse(sb, bh); - return -ENOTEMPTY; + while (fat_get_entry(dir,&pos,&bh,&de) > -1) { + if (!IS_FREE(de->name) && + strncmp(de->name,MSDOS_DOT , MSDOS_NAME) && + strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) { + result = -ENOTEMPTY; + break; } + } if (bh) - fat_brelse(sb, bh); + fat_brelse(dir->i_sb, bh); } - return 0; + return result; } /***** Remove a directory */ -int msdos_rmdir(struct inode *dir,struct dentry *dentry) +int msdos_rmdir(struct inode *dir, struct dentry *dentry) { + struct inode *inode = dentry->d_inode; struct super_block *sb = dir->i_sb; int res,ino; struct buffer_head *bh; struct msdos_dir_entry *de; - struct inode *inode; bh = NULL; - inode = NULL; - res = -EPERM; - if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len, - &bh,&de,&ino)) < 0) + res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, + &bh, &de, &ino); + if (res < 0) goto rmdir_done; - inode = dentry->d_inode; res = -ENOTDIR; - if (!S_ISDIR(inode->i_mode)) goto rmdir_done; - res = -EBUSY; + if (!S_ISDIR(inode->i_mode)) + goto rmdir_done; if (dir->i_dev != inode->i_dev || dir == inode) - goto rmdir_done; + printk("msdos_rmdir: impossible condition\n"); + /* + * Prune any child dentries, then verify that + * the directory is empty and not in use. + */ + shrink_dcache_parent(dentry); res = msdos_empty(inode); if (res) goto rmdir_done; + res = -EBUSY; + if (dentry->d_count > 1) { +#ifdef MSDOS_DEBUG +printk("rename_diff_dir: %s/%s busy, d_count=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); +#endif + goto rmdir_done; + } + inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_nlink--; mark_inode_dirty(inode); mark_inode_dirty(dir); + /* + * Do the d_delete before any blocking operations. + * We must make a negative dentry, as the FAT code + * apparently relies on the inode being iput(). + */ + d_delete(dentry); de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); - d_delete(dentry); res = 0; rmdir_done: fat_brelse(sb, bh); @@ -489,18 +506,18 @@ fat_lock_creation(); if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) { fat_unlock_creation(); + /* N.B. does this need to be released on the other path? */ fat_brelse(sb, bh); return -EEXIST; } - if ((res = msdos_create_entry(dir,msdos_name,1,is_hid, - &inode)) < 0) { - fat_unlock_creation(); - return res; - } + res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode); + if (res < 0) + goto out_unlock; dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ MSDOS_I(inode)->i_busy = 1; /* prevent lookups */ - if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error; + if ((res = fat_add_cluster(inode)) < 0) + goto mkdir_error; if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0) goto mkdir_error; dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */ @@ -524,6 +541,7 @@ mkdir_error: if (msdos_rmdir(dir,dentry) < 0) fat_fs_panic(dir->i_sb,"rmdir in mkdir failed"); +out_unlock: fat_unlock_creation(); return res; } @@ -535,25 +553,20 @@ int nospc) /* Flag special file ? */ { struct super_block *sb = dir->i_sb; + struct inode *inode = dentry->d_inode; int res,ino; struct buffer_head *bh; struct msdos_dir_entry *de; - struct inode *inode; bh = NULL; - inode = NULL; if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len, &bh,&de,&ino)) < 0) goto unlink_done; - inode = dentry->d_inode; - if (!S_ISREG(inode->i_mode) && nospc){ - res = -EPERM; + res = -EPERM; + if (!S_ISREG(inode->i_mode) && nospc) goto unlink_done; - } - if (IS_IMMUTABLE(inode)){ - res = -EPERM; + if (IS_IMMUTABLE(inode)) goto unlink_done; - } inode->i_nlink = 0; inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; MSDOS_I(inode)->i_busy = 1; @@ -562,6 +575,7 @@ de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, bh, 1); d_delete(dentry); /* This also frees the inode */ + res = 0; unlink_done: fat_brelse(sb, bh); return res; @@ -707,7 +721,10 @@ : (free_de->attr&~ATTR_HIDDEN); if (!(free_inode = iget(new_dir->i_sb,free_ino))) { free_de->name[0] = DELETED_FLAG; -/* Don't mark free_bh as dirty. Both states are supposed to be equivalent. */ + /* + * Don't mark free_bh as dirty. Both states + * are supposed to be equivalent. + */ fat_brelse(sb, free_bh); if (exists) fat_brelse(sb, new_bh); @@ -718,19 +735,53 @@ mark_inode_dirty(new_dir); } msdos_read_inode(free_inode); + /* + * Check whether there's already a linked inode ... + */ + if (MSDOS_I(old_inode)->i_linked) { + struct inode *linked = MSDOS_I(old_inode)->i_linked; +#ifdef MSDOS_PARANOIA +printk("rename_diff_dir: inode %ld already has link %ld, freeing it\n", +old_inode->i_ino, linked->i_ino); +#endif + MSDOS_I(old_inode)->i_linked = NULL; + MSDOS_I(linked)->i_oldlink = NULL; + iput(linked); + } MSDOS_I(old_inode)->i_busy = 1; MSDOS_I(old_inode)->i_linked = free_inode; MSDOS_I(free_inode)->i_oldlink = old_inode; +#ifdef MSDOS_DEBUG +printk("rename_diff_dir: inode %ld added as link of %ld\n", +free_inode->i_ino, old_inode->i_ino); +#endif fat_cache_inval_inode(old_inode); mark_inode_dirty(old_inode); old_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, old_bh, 1); fat_mark_buffer_dirty(sb, free_bh, 1); if (exists) { + /* + * Check whether there's already a depend inode ... + */ + if (MSDOS_I(new_inode)->i_depend) { + struct inode *depend = MSDOS_I(new_inode)->i_depend; +#ifdef MSDOS_PARANOIA +printk("rename_diff_dir: inode %ld already has depend %ld, freeing it\n", +new_inode->i_ino, depend->i_ino); +#endif + MSDOS_I(new_inode)->i_depend = NULL; + MSDOS_I(depend)->i_old = NULL; + iput(depend); + } MSDOS_I(new_inode)->i_depend = free_inode; MSDOS_I(free_inode)->i_old = new_inode; /* Two references now exist to free_inode so increase count */ free_inode->i_count++; +#ifdef MSDOS_DEBUG +printk("rename_diff_dir: inode %ld added as depend of %ld\n", +free_inode->i_ino, new_inode->i_ino); +#endif /* free_inode is put after putting new_inode and old_inode */ fat_brelse(sb, new_bh); } diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c --- v2.1.62/linux/fs/ncpfs/dir.c Mon Oct 20 10:36:52 1997 +++ linux/fs/ncpfs/dir.c Wed Nov 12 11:10:40 1997 @@ -3,6 +3,7 @@ * * Copyright (C) 1995, 1996 by Volker Lendecke * Modified for big endian by J.F. Chadima and David S. Miller + * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache * */ @@ -13,13 +14,18 @@ #include #include #include -#include #include #include #include #include + +#include #include "ncplib_kernel.h" +#ifndef shrink_dcache_parent +#define shrink_dcache_parent(dentry) shrink_dcache_sb((dentry)->d_sb) +#endif + struct ncp_dirent { struct nw_info_struct i; @@ -27,79 +33,30 @@ unsigned long f_pos; }; -static long - ncp_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count); - -static int - ncp_readdir(struct file *filp, - void *dirent, filldir_t filldir); - -static int - ncp_read_volume_list(struct ncp_server *server, int start_with, - int cache_size); - -static int - ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos, - int cache_size, struct ncp_dirent *entry); - -static struct inode * - ncp_iget(struct inode *dir, struct nw_file_info *finfo); - -static struct ncp_inode_info * - ncp_find_dir_inode(struct inode *dir, const char *name); - -static int - ncp_lookup(struct inode *dir, const char *__name, - int len, struct inode **result); - -static int - ncp_create(struct inode *dir, const char *name, int len, int mode, - struct inode **result); - -static int - ncp_mkdir(struct inode *dir, const char *name, int len, int mode); - -static int - ncp_rmdir(struct inode *dir, const char *name, int len); - -static int - ncp_unlink(struct inode *dir, const char *name, int len); - -static int - ncp_rename(struct inode *old_dir, const char *old_name, int old_len, - struct inode *new_dir, const char *new_name, int new_len); - -static inline void str_upper(char *name) -{ - while (*name) { - if (*name >= 'a' && *name <= 'z') { - *name -= ('a' - 'A'); - } - name++; - } -} - -static inline void str_lower(char *name) -{ - while (*name) { - if (*name >= 'A' && *name <= 'Z') { - *name += ('a' - 'A'); - } - name++; - } -} - -static inline int ncp_namespace(struct inode *i) -{ - struct ncp_server *server = NCP_SERVER(i); - struct nw_info_struct *info = NCP_ISTRUCT(i); - return server->name_space[info->volNumber]; -} +static kdev_t c_dev = 0; +static unsigned long c_ino = 0; +static int c_size; +static int c_seen_eof; +static int c_last_returned_index; +static struct ncp_dirent *c_entry = NULL; +static int c_lock = 0; +static struct wait_queue *c_wait = NULL; -static inline int ncp_preserve_case(struct inode *i) -{ - return (ncp_namespace(i) == NW_NS_OS2); -} +static int ncp_read_volume_list(struct ncp_server *, int, int, + struct ncp_dirent *); +static int ncp_do_readdir(struct ncp_server *, struct dentry *, int, int, + struct ncp_dirent *); + +static ssize_t ncp_dir_read(struct file *, char *, size_t, loff_t *); +static int ncp_readdir(struct file *, void *, filldir_t); + +static int ncp_create(struct inode *, struct dentry *, int); +static int ncp_lookup(struct inode *, struct dentry *); +static int ncp_unlink(struct inode *, struct dentry *); +static int ncp_mkdir(struct inode *, struct dentry *, int); +static int ncp_rmdir(struct inode *, struct dentry *); +static int ncp_rename(struct inode *, struct dentry *, + struct inode *, struct dentry *); static struct file_operations ncp_dir_operations = { @@ -128,74 +85,169 @@ NULL, /* mknod */ ncp_rename, /* rename */ NULL, /* readlink */ + NULL, /* follow link */ + NULL, /* readpage */ + NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL, /* revalidate */ }; +static ssize_t +ncp_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos) +{ + return -EISDIR; +} -/* Here we encapsulate the inode number handling that depends upon the - * mount mode: When we mount a complete server, the memory address of - * the ncp_inode_info is used as the inode number. When only a single - * volume is mounted, then the dirEntNum is used as the inode - * number. As this is unique for the complete volume, this should - * enable the NFS exportability of a ncpfs-mounted volume. +/* + * Dentry operations routines */ +static int ncp_lookup_validate(struct dentry *); +static void ncp_delete_dentry(struct dentry *); +static int ncp_hash_dentry(struct dentry *, struct qstr *); +static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *); + +static struct dentry_operations ncp_dentry_operations = +{ + ncp_lookup_validate, /* d_validate(struct dentry *) */ + ncp_hash_dentry, /* d_hash */ + ncp_compare_dentry, /* d_compare */ + ncp_delete_dentry /* d_delete(struct dentry *) */ +}; -static inline int ncp_single_volume(struct ncp_server *server) +static int +ncp_hash_dentry(struct dentry *dentry, struct qstr *this) { - return (server->m.mounted_vol[0] != '\0'); + char al[NCP_MAXPATHLEN]; + unsigned long hash; + int i; + + memcpy(al,this->name,this->len); + al[this->len] = 0; + + if (!ncp_preserve_case(dentry->d_inode)) + str_lower(al); + + hash = init_name_hash(); + for (i=0; ilen ; i++) + hash = partial_name_hash(al[i],hash); + this->hash = end_name_hash(hash); + + return 0; } -inline ino_t - ncp_info_ino(struct ncp_server * server, struct ncp_inode_info * info) +static int +ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) { - return ncp_single_volume(server) - ? info->finfo.i.dirEntNum : (ino_t) info; + char al[NCP_MAXPATHLEN],bl[NCP_MAXPATHLEN]; + + if (a->len != b->len) return 1; + + if (ncp_preserve_case(dentry->d_inode)) + return strncmp(a->name, b->name, a->len); + + memcpy (al,a->name,a->len); + memcpy (bl,b->name,b->len); + al[a->len] = bl[b->len] = 0; + + str_lower(al); + str_lower(bl); + + return strcmp(al,bl); } -static inline int ncp_is_server_root(struct inode *inode) +/* + * This is the callback from dput() when d_count is going to 0. + * We use this to unhash dentries with bad inodes and close files. + */ +static void +ncp_delete_dentry(struct dentry * dentry) { - struct ncp_server *s = NCP_SERVER(inode); + struct inode *inode = dentry->d_inode; - return ((!ncp_single_volume(s)) - && (inode->i_ino == ncp_info_ino(s, &(s->root)))); + if (inode) + { + if (is_bad_inode(inode)) + { + d_drop(dentry); + } + /* + * Lock the superblock, then recheck the dentry count. + * (Somebody might have used it again ...) + */ + if (dentry->d_count == 1 && NCP_FINFO(inode)->opened) { +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_delete_dentry: closing file %s/%s\n", +dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + ncp_make_closed(inode); + } + } else + { + /* N.B. Unhash negative dentries? */ + } } -struct ncp_inode_info * - ncp_find_inode(struct inode *inode) -{ - struct ncp_server *server = NCP_SERVER(inode); - struct ncp_inode_info *root = &(server->root); - struct ncp_inode_info *this = root; +/* Here we encapsulate the inode number handling that depends upon the + * mount mode: When we mount a complete server, the memory address of + * the ncp_inode_info is used as the inode number. When only a single + * volume is mounted, then the dirEntNum is used as the inode + * number. As this is unique for the complete volume, this should + * enable the NFS exportability of a ncpfs-mounted volume. + */ - ino_t ino = inode->i_ino; +/* + * Generate a unique inode number. + */ +ino_t ncp_invent_inos(unsigned long n) +{ + static ino_t ino = 1; - do { - if (ino == ncp_info_ino(server, this)) { - return this; - } - this = this->next; + if (ino + 2*n < ino) + { + /* wrap around */ + ino += n; } - while (this != root); + ino += n; + return ino; +} - return NULL; +/* + * Check whether a dentry already exists for the given name, + * and return the inode number if it has an inode. This is + * needed to keep getcwd() working. + */ +static ino_t +find_inode_number(struct dentry *dir, struct qstr *name) +{ + struct dentry * dentry; + ino_t ino = 0; + + name->hash = full_name_hash(name->name, name->len); + dentry = d_lookup(dir, name); + if (dentry) + { + if (dentry->d_inode) + ino = dentry->d_inode->i_ino; + dput(dentry); + } + return ino; } -static long ncp_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count) +static inline int +ncp_single_volume(struct ncp_server *server) { - return -EISDIR; + return (server->m.mounted_vol[0] != '\0'); } -static kdev_t c_dev = 0; -static unsigned long c_ino = 0; -static int c_size; -static int c_seen_eof; -static int c_last_returned_index; -static struct ncp_dirent *c_entry = NULL; -static int c_lock = 0; -static struct wait_queue *c_wait = NULL; +static inline int ncp_is_server_root(struct inode *inode) +{ + return (!ncp_single_volume(NCP_SERVER(inode)) && + inode == inode->i_sb->s_root->d_inode); +} static inline void ncp_lock_dircache(void) { @@ -210,54 +262,143 @@ wake_up(&c_wait); } -static int ncp_readdir(struct file *filp, - void *dirent, filldir_t filldir) + +/* + * This is the callback when the dcache has a lookup hit. + */ + +static int +ncp_lookup_validate(struct dentry * dentry) { - int result = 0; - int i = 0; - int index = 0; - struct inode *inode = filp->f_dentry->d_inode; - struct ncp_dirent *entry = NULL; + struct ncp_server *server; + struct inode *dir = dentry->d_parent->d_inode; + int down_case = 0; + int val = 0,res; + int len = dentry->d_name.len; + struct ncpfs_inode_info finfo; + __u8 __name[dentry->d_name.len + 1]; + + printk("ncp_lookup_validate called\n"); + + if (!dir || !S_ISDIR(dir->i_mode)) { + printk(KERN_WARNING "ncp_lookup_validate: inode is NULL or not a directory.\n"); + goto finished; + } + server = NCP_SERVER(dir); + + if (!ncp_conn_valid(server)) + goto finished; + + strncpy(__name, dentry->d_name.name, len); + __name[len] = '\0'; +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_lookup_validate: %s, len %d\n", __name, len); +#endif + + if (!ncp_preserve_case(dir)) { + str_lower(__name); + down_case = 1; + } + + /* If the file is in the dir cache, we do not have to ask the + server. */ + +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_lookup_validate: server lookup for %s/%s\n", +dentry->d_parent->d_name.name, __name); +#endif + if (ncp_is_server_root(dir)) + { + str_upper(__name); + down_case = 1; + res = ncp_lookup_volume(server, __name, + &(finfo.nw_info.i)); + } else + { + if (!ncp_preserve_case(dir)) + { + str_upper(__name); + down_case = 1; + } + res = ncp_obtain_info(server, dir, __name, + &(finfo.nw_info.i)); + } +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_lookup_validate: looked for %s/%s, res=%d\n", +dentry->d_parent->d_name.name, __name, res); +#endif + /* + * If we didn't find it, or if it has a different dirEntNum to + * what we remember, it's not valid any more. + */ + if (!res) + if (finfo.nw_info.i.dirEntNum == NCP_FINFO(dentry->d_inode)->dirEntNum) + val=1; +#ifdef NCPFS_PARANOIA + else + printk(KERN_DEBUG "ncp_lookup_validate: found, but dirEntNum changed\n"); +#endif + if (!val) ncp_invalid_dir_cache(dir); + +finished: +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_lookup_validate: result=%d\n", val); +#endif + + return val; +} + + +static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; struct ncp_server *server = NCP_SERVER(inode); - struct ncp_inode_info *dir = NCP_INOP(inode); + struct ncp_dirent *entry = NULL; + int result, i, index = 0; - DDPRINTK("ncp_readdir: filp->f_pos = %d\n", (int) filp->f_pos); - DDPRINTK("ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n", + DDPRINTK(KERN_DEBUG "ncp_readdir: reading %s/%s, pos=%d\n", + dentry->d_parent->d_name.name, dentry->d_name.name, + (int) filp->f_pos); + DDPRINTK(KERN_DEBUG "ncp_readdir: inode->i_ino = %ld, c_ino = %ld\n", inode->i_ino, c_ino); + result = -EBADF; if (!inode || !S_ISDIR(inode->i_mode)) { - printk("ncp_readdir: inode is NULL or not a directory\n"); - return -EBADF; - } - if (!ncp_conn_valid(server)) { - return -EIO; + printk(KERN_WARNING "ncp_readdir: inode is NULL or not a directory\n"); + goto out; } - ncp_lock_dircache(); + result = -EIO; + if (!ncp_conn_valid(server)) + goto out; + ncp_lock_dircache(); + result = -ENOMEM; if (c_entry == NULL) { i = sizeof(struct ncp_dirent) * NCP_READDIR_CACHE_SIZE; c_entry = (struct ncp_dirent *) vmalloc(i); if (c_entry == NULL) { - printk("ncp_readdir: no MEMORY for cache\n"); - result = -ENOMEM; + printk(KERN_WARNING "ncp_readdir: no MEMORY for cache\n"); goto finished; } } + + result = 0; if (filp->f_pos == 0) { ncp_invalid_dir_cache(inode); - if (filldir(dirent, ".", 1, filp->f_pos, - ncp_info_ino(server, dir)) < 0) { + if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0) { goto finished; } - filp->f_pos += 1; + filp->f_pos = 1; } if (filp->f_pos == 1) { - if (filldir(dirent, "..", 2, filp->f_pos, - ncp_info_ino(server, dir->dir)) < 0) { + if (filldir(dirent, "..", 2, 1, + dentry->d_parent->d_inode->i_ino) < 0) { goto finished; } - filp->f_pos += 1; + filp->f_pos = 2; } + if ((inode->i_dev == c_dev) && (inode->i_ino == c_ino)) { for (i = 0; i < c_size; i++) { if (filp->f_pos == c_entry[i].f_pos) { @@ -273,18 +414,17 @@ } if (entry == NULL) { int entries; - DDPRINTK("ncp_readdir: Not found in cache.\n"); + DDPRINTK(KERN_DEBUG "ncp_readdir: Not found in cache.\n"); if (ncp_is_server_root(inode)) { entries = ncp_read_volume_list(server, filp->f_pos, - NCP_READDIR_CACHE_SIZE); - DPRINTK("ncp_read_volume_list returned %d\n", entries); + NCP_READDIR_CACHE_SIZE, c_entry); + DPRINTK(KERN_DEBUG "ncp_read_volume_list returned %d\n", entries); } else { - entries = ncp_do_readdir(server, inode, filp->f_pos, - NCP_READDIR_CACHE_SIZE, - c_entry); - DPRINTK("ncp_readdir returned %d\n", entries); + entries = ncp_do_readdir(server, dentry, filp->f_pos, + NCP_READDIR_CACHE_SIZE, c_entry); + DPRINTK(KERN_DEBUG "ncp_readdir: returned %d\n", entries); } if (entries < 0) { @@ -313,31 +453,24 @@ /* Nothing found, even from a ncp call */ goto finished; } + while (index < c_size) { ino_t ino; + struct qstr qname; - if (ncp_single_volume(server)) { - ino = (ino_t) (entry->i.dirEntNum); - } else { - /* For getwd() we have to return the correct - * inode in d_ino if the inode is currently in - * use. Otherwise the inode number does not - * matter. (You can argue a lot about this..) */ - struct ncp_inode_info *ino_info; - ino_info = ncp_find_dir_inode(inode, - entry->i.entryName); - - /* Some programs seem to be confused about a - * zero inode number, so we set it to one. - * Thanks to Gordon Chaffee for this one. */ - if (ino_info == NULL) { - ino_info = (struct ncp_inode_info *) 1; - } - ino = (ino_t) (ino_info); - } + DDPRINTK(KERN_DEBUG "ncp_readdir: entry->path= %s\n", entry->i.entryName); + DDPRINTK(KERN_DEBUG "ncp_readdir: entry->f_pos = %ld\n", entry->f_pos); - DDPRINTK("ncp_readdir: entry->path= %s\n", entry->i.entryName); - DDPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos); + /* For getwd() we have to return the correct + * inode in d_ino if the inode is currently in + * use. Otherwise the inode number does not + * matter. (You can argue a lot about this..) + */ + qname.name = entry->i.entryName; + qname.len = entry->i.nameLen; + ino = find_inode_number(dentry, &qname); + if (!ino) + ino = ncp_invent_inos(1); if (filldir(dirent, entry->i.entryName, entry->i.nameLen, entry->f_pos, ino) < 0) { @@ -356,103 +489,114 @@ } finished: ncp_unlock_dircache(); +out: return result; } -static int ncp_read_volume_list(struct ncp_server *server, int fpos, int cache_size) +static int +ncp_read_volume_list(struct ncp_server *server, int fpos, + int cache_size, struct ncp_dirent *entry) { - struct ncp_dirent *entry = c_entry; - - int total_count = 2; - int i; + int i, total_count = 2; + struct ncp_volume_info info; + DPRINTK(KERN_DEBUG "ncp_read_volume_list: pos=%d\n", fpos); #if 1 if (fpos < 2) { - printk("OOPS, we expect fpos >= 2"); + printk(KERN_ERR "OOPS, we expect fpos >= 2"); fpos = 2; } #endif for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) { - struct ncp_volume_info info; - if (ncp_get_volume_info_with_number(server, i, &info) != 0) { - return (total_count - fpos); - } - if (strlen(info.volume_name) > 0) { - if (total_count < fpos) { - DPRINTK("ncp_read_volumes: skipped vol: %s\n", - info.volume_name); - } else if (total_count >= fpos + cache_size) { - return (total_count - fpos); - } else { - DPRINTK("ncp_read_volumes: found vol: %s\n", - info.volume_name); + if (ncp_get_volume_info_with_number(server, i, &info) != 0) + goto out; + if (!strlen(info.volume_name)) + continue; - if (ncp_lookup_volume(server, - info.volume_name, - &(entry->i)) != 0) { - DPRINTK("ncpfs: could not lookup vol " - "%s\n", info.volume_name); - continue; - } - entry->f_pos = total_count; - entry += 1; + if (total_count < fpos) { + DPRINTK(KERN_DEBUG "ncp_read_volume_list: skipped vol: %s\n", + info.volume_name); + } else if (total_count >= fpos + cache_size) { + goto out; + } else { + DPRINTK(KERN_DEBUG "ncp_read_volume_list: found vol: %s\n", + info.volume_name); + + if (ncp_lookup_volume(server, info.volume_name, + &(entry->i)) != 0) { + DPRINTK(KERN_DEBUG "ncpfs: could not lookup vol %s\n", + info.volume_name); + continue; } - total_count += 1; + entry->f_pos = total_count; + entry += 1; } + total_count += 1; } +out: return (total_count - fpos); } -static int ncp_do_readdir(struct ncp_server *server, struct inode *dir, int fpos, - int cache_size, struct ncp_dirent *entry) +static int ncp_do_readdir(struct ncp_server *server, struct dentry *dentry, + int fpos, int cache_size, struct ncp_dirent *entry) { - static struct nw_search_sequence seq; + struct inode *dir = dentry->d_inode; static struct inode *last_dir; static int total_count; + static struct nw_search_sequence seq; + int err; #if 1 if (fpos < 2) { - printk("OOPS, we expect fpos >= 2"); + printk(KERN_ERR "OOPS, we expect fpos >= 2"); fpos = 2; } #endif - DPRINTK("ncp_do_readdir: fpos = %d\n", fpos); + DPRINTK(KERN_DEBUG "ncp_do_readdir: %s/%s, fpos=%d\n", + dentry->d_parent->d_name.name, dentry->d_name.name, fpos); if (fpos == 2) { last_dir = NULL; total_count = 2; } - if ((fpos != total_count) || (dir != last_dir)) { + if ((fpos != total_count) || (dir != last_dir)) + { total_count = 2; last_dir = dir; - - DPRINTK("ncp_do_readdir: re-used seq for %s\n", - NCP_ISTRUCT(dir)->entryName); - - if (ncp_initialize_search(server, NCP_ISTRUCT(dir), &seq) != 0) { - DPRINTK("ncp_init_search failed\n"); - return total_count - fpos; + +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_do_readdir: init %s, volnum=%d, dirent=%u\n", +dentry->d_name.name, NCP_FINFO(dir)->volNumber, NCP_FINFO(dir)->dirEntNum); +#endif + err = ncp_initialize_search(server, dir, &seq); + if (err) + { + DPRINTK(KERN_DEBUG "ncp_do_readdir: init failed, err=%d\n", err); + goto out; } } + while (total_count < fpos + cache_size) { - if (ncp_search_for_file_or_subdir(server, &seq, - &(entry->i)) != 0) { - return total_count - fpos; + err = ncp_search_for_file_or_subdir(server, &seq, &(entry->i)); + if (err) { + DPRINTK(KERN_DEBUG "ncp_do_readdir: search failed, err=%d\n", err); + goto out; } if (total_count < fpos) { - DPRINTK("ncp_do_readdir: skipped file: %s\n", - entry->i.entryName); + DPRINTK(KERN_DEBUG "ncp_do_readdir: skipped file: %s/%s\n", + dentry->d_name.name, entry->i.entryName); } else { - DDPRINTK("ncp_do_r: file: %s, f_pos=%d,total_count=%d", - entry->i.entryName, fpos, total_count); + DDPRINTK(KERN_DEBUG "ncp_do_r: file: %s, f_pos=%d,total_count=%d", + entry->i.entryName, fpos, total_count); entry->s = seq; entry->f_pos = total_count; entry += 1; } total_count += 1; } +out: return (total_count - fpos); } @@ -474,7 +618,7 @@ void ncp_free_dir_cache(void) { - DPRINTK("ncp_free_dir_cache: enter\n"); + DPRINTK(KERN_DEBUG "ncp_free_dir_cache: enter\n"); if (c_entry == NULL) { return; @@ -482,540 +626,398 @@ vfree(c_entry); c_entry = NULL; - DPRINTK("ncp_free_dir_cache: exit\n"); -} - - -static struct inode * - ncp_iget(struct inode *dir, struct nw_file_info *finfo) -{ - struct inode *inode; - struct ncp_inode_info *new_inode_info; - struct ncp_inode_info *root; - - if (dir == NULL) { - printk("ncp_iget: dir is NULL\n"); - return NULL; - } - if (finfo == NULL) { - printk("ncp_iget: finfo is NULL\n"); - return NULL; - } - new_inode_info = ncp_kmalloc(sizeof(struct ncp_inode_info), - GFP_KERNEL); - - if (new_inode_info == NULL) { - printk("ncp_iget: could not alloc mem for %s\n", - finfo->i.entryName); - return NULL; - } - new_inode_info->state = NCP_INODE_LOOKED_UP; - new_inode_info->nused = 0; - new_inode_info->dir = NCP_INOP(dir); - new_inode_info->finfo = *finfo; - - NCP_INOP(dir)->nused += 1; - - /* We have to link the new inode_info into the doubly linked - list of inode_infos to make a complete linear search - possible. */ - - root = &(NCP_SERVER(dir)->root); - - new_inode_info->prev = root; - new_inode_info->next = root->next; - root->next->prev = new_inode_info; - root->next = new_inode_info; - - if (!(inode = iget(dir->i_sb, ncp_info_ino(NCP_SERVER(dir), - new_inode_info)))) { - printk("ncp_iget: iget failed!"); - return NULL; - } - return inode; -} - -void ncp_free_inode_info(struct ncp_inode_info *i) -{ - if (i == NULL) { - printk("ncp_free_inode: i == NULL\n"); - return; - } - i->state = NCP_INODE_CACHED; - while ((i->nused == 0) && (i->state == NCP_INODE_CACHED)) { - struct ncp_inode_info *dir = i->dir; - - i->next->prev = i->prev; - i->prev->next = i->next; - - DDPRINTK("ncp_free_inode_info: freeing %s\n", - i->finfo.i.entryName); - - ncp_kfree_s(i, sizeof(struct ncp_inode_info)); - - if (dir == i) - return; - - (dir->nused)--; - i = dir; - } -} - -void ncp_init_root(struct ncp_server *server) -{ - struct ncp_inode_info *root = &(server->root); - struct nw_info_struct *i = &(root->finfo.i); - unsigned short dummy; - - DPRINTK("ncp_init_root: i = %x\n", (int) i); - - root->finfo.opened = 0; - i->attributes = aDIR; - i->dataStreamSize = 1024; - i->dirEntNum = i->DosDirNum = 0; - i->volNumber = NCP_NUMBER_OF_VOLUMES + 1; /* illegal volnum */ - ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate)); - ncp_date_unix2dos(0, &(i->modifyTime), &(i->modifyDate)); - ncp_date_unix2dos(0, &dummy, &(i->lastAccessDate)); - i->creationTime = le16_to_cpu(i->creationTime); - i->creationDate = le16_to_cpu(i->creationDate); - i->modifyTime = le16_to_cpu(i->modifyTime); - i->modifyDate = le16_to_cpu(i->modifyDate); - i->lastAccessDate = le16_to_cpu(i->lastAccessDate); - i->nameLen = 0; - i->entryName[0] = '\0'; - - root->state = NCP_INODE_LOOKED_UP; - root->nused = 1; - root->dir = root; - root->next = root->prev = root; - return; + DPRINTK(KERN_DEBUG "ncp_free_dir_cache: exit\n"); } int ncp_conn_logged_in(struct ncp_server *server) { - if (server->m.mounted_vol[0] == '\0') { - return 0; - } - str_upper(server->m.mounted_vol); - if (ncp_lookup_volume(server, server->m.mounted_vol, - &(server->root.finfo.i)) != 0) { - return -ENOENT; - } - str_lower(server->root.finfo.i.entryName); + int result; - return 0; -} - -void ncp_free_all_inodes(struct ncp_server *server) -{ - /* Here nothing should be to do. I do not know whether it's - better to leave some memory allocated or be stuck in an - endless loop */ -#if 1 - struct ncp_inode_info *root = &(server->root); - - if (root->next != root) { - printk("ncp_free_all_inodes: INODES LEFT!!!\n"); - } - while (root->next != root) { - printk("ncp_free_all_inodes: freeing inode\n"); - ncp_free_inode_info(root->next); - /* In case we have an endless loop.. */ - schedule(); - } + if (ncp_single_volume(server)) { + result = -ENOENT; + str_upper(server->m.mounted_vol); + if (ncp_lookup_volume(server, server->m.mounted_vol, + &(server->root.finfo.i)) != 0) { +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_conn_logged_in: %s not found\n", server->m.mounted_vol); #endif - - return; -} - -/* We will search the inode that belongs to this name, currently by a - complete linear search through the inodes belonging to this - filesystem. This has to be fixed. */ -static struct ncp_inode_info * - ncp_find_dir_inode(struct inode *dir, const char *name) -{ - struct ncp_server *server = NCP_SERVER(dir); - struct nw_info_struct *dir_info = NCP_ISTRUCT(dir); - struct ncp_inode_info *result = &(server->root); - - if (name == NULL) { - return NULL; - } - do { - if ((result->dir->finfo.i.dirEntNum == dir_info->dirEntNum) - && (result->dir->finfo.i.volNumber == dir_info->volNumber) - && (strcmp(result->finfo.i.entryName, name) == 0) - /* The root dir is never looked up using this - * routine. Without the following test a root - * directory 'sys' in a volume named 'sys' could - * never be looked up, because - * server->root->dir==server->root. */ - && (result != &(server->root))) { - return result; + goto out; } - result = result->next; - + str_lower(server->root.finfo.i.entryName); } - while (result != &(server->root)); + result = 0; - return NULL; +out: + return result; } -static int ncp_lookup(struct inode *dir, const char *__name, int len, - struct inode **result) +static int ncp_lookup(struct inode *dir, struct dentry *dentry) { - struct nw_file_info finfo; struct ncp_server *server; - struct ncp_inode_info *result_info; - int found_in_cache; - int down_case = 0; - char name[len + 1]; - - *result = NULL; - + struct inode *inode = NULL; + int found_in_cache, down_case = 0; + int error; + int len = dentry->d_name.len; + struct ncpfs_inode_info finfo; + __u8 __name[dentry->d_name.len + 1]; + + error = -ENOENT; if (!dir || !S_ISDIR(dir->i_mode)) { - printk("ncp_lookup: inode is NULL or not a directory.\n"); - iput(dir); - return -ENOENT; + printk(KERN_WARNING "ncp_lookup: inode is NULL or not a directory.\n"); + goto finished; } server = NCP_SERVER(dir); - if (!ncp_conn_valid(server)) { - iput(dir); - return -EIO; - } - DPRINTK("ncp_lookup: %s, len %d\n", __name, len); - - /* Fast cheat for . */ - if (len == 0 || (len == 1 && __name[0] == '.')) { - *result = dir; - return 0; - } - /* ..and for .. */ - if (len == 2 && __name[0] == '.' && __name[1] == '.') { - struct ncp_inode_info *parent = NCP_INOP(dir)->dir; - - if (parent->state == NCP_INODE_CACHED) { - parent->state = NCP_INODE_LOOKED_UP; - } - *result = iget(dir->i_sb, ncp_info_ino(server, parent)); - iput(dir); - if (*result == 0) { - return -EACCES; - } else { - return 0; - } - } - memcpy(name, __name, len); - name[len] = 0; - lock_super(dir->i_sb); - result_info = ncp_find_dir_inode(dir, name); - - if (result_info != 0) { - if (result_info->state == NCP_INODE_CACHED) { - result_info->state = NCP_INODE_LOOKED_UP; - } - /* Here we convert the inode_info address into an - inode number */ + error = -EIO; + if (!ncp_conn_valid(server)) + goto finished; - *result = iget(dir->i_sb, ncp_info_ino(server, result_info)); - unlock_super(dir->i_sb); - iput(dir); + strncpy(__name, dentry->d_name.name, len); + __name[len] = '\0'; +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_lookup: %s, len %d\n", __name, len); +#endif - if (*result == NULL) { - return -EACCES; - } - return 0; + if (!ncp_preserve_case(dir)) { + str_lower(__name); + down_case = 1; } + /* If the file is in the dir cache, we do not have to ask the server. */ found_in_cache = 0; ncp_lock_dircache(); - if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino)) { + if ((dir->i_dev == c_dev) && (dir->i_ino == c_ino)) + { int first = c_last_returned_index; int i; - + i = first; do { - DDPRINTK("ncp_lookup: trying index: %d, name: %s\n", - i, c_entry[i].i.entryName); - - if (strcmp(c_entry[i].i.entryName, name) == 0) { - DPRINTK("ncp_lookup: found in cache!\n"); - finfo.i = c_entry[i].i; +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_lookup: trying index: %d, name: %s\n", i, c_entry[i].i.entryName); +#endif + if (strcmp(c_entry[i].i.entryName, __name) == 0) { +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_lookup: found in cache!\n"); +#endif + finfo.nw_info.i = c_entry[i].i; found_in_cache = 1; break; } i = (i + 1) % c_size; - } - while (i != first); + } while (i != first); } ncp_unlock_dircache(); - if (found_in_cache == 0) { + if (found_in_cache == 0) + { int res; - - DDPRINTK("ncp_lookup: do_lookup on %s/%s\n", - NCP_ISTRUCT(dir)->entryName, name); - - if (ncp_is_server_root(dir)) { - str_upper(name); + +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_lookup: server lookup for %s/%s\n", +dentry->d_parent->d_name.name, __name); +#endif + if (ncp_is_server_root(dir)) + { + str_upper(__name); down_case = 1; - res = ncp_lookup_volume(server, name, &(finfo.i)); - } else { - if (!ncp_preserve_case(dir)) { - str_upper(name); + res = ncp_lookup_volume(server, __name, + &(finfo.nw_info.i)); + } else + { + if (!ncp_preserve_case(dir)) + { + str_upper(__name); down_case = 1; } - res = ncp_obtain_info(server, - NCP_ISTRUCT(dir)->volNumber, - NCP_ISTRUCT(dir)->dirEntNum, - name, &(finfo.i)); - } - if (res != 0) { - unlock_super(dir->i_sb); - iput(dir); - return -ENOENT; + res = ncp_obtain_info(server, dir, __name, + &(finfo.nw_info.i)); } +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_lookup: looked for %s/%s, res=%d\n", +dentry->d_parent->d_name.name, __name, res); +#endif + /* + * If we didn't find an entry, make a negative dentry. + */ + if (res != 0) + goto add_entry; + } + + /* + * Create an inode for the entry. + */ + finfo.nw_info.opened = 0; + finfo.ino = ncp_invent_inos(1); + error = -EACCES; + inode = ncp_iget(dir->i_sb, &finfo); + if (inode) + { + add_entry: + dentry->d_op = &ncp_dentry_operations; + d_add(dentry, inode); + error = 0; } - finfo.opened = 0; - if (down_case != 0) { - str_lower(finfo.i.entryName); - } - if (!(*result = ncp_iget(dir, &finfo))) { - unlock_super(dir->i_sb); - iput(dir); - return -EACCES; - } - unlock_super(dir->i_sb); - iput(dir); - return 0; +finished: +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_lookup: result=%d\n", error); +#endif + return error; } -static int ncp_create(struct inode *dir, const char *name, int len, int mode, - struct inode **result) +/* + * This code is common to create, mkdir, and mknod. + */ +static int ncp_instantiate(struct inode *dir, struct dentry *dentry, + struct ncpfs_inode_info *finfo) { - struct nw_file_info finfo; - __u8 _name[len + 1]; + struct inode *inode; + int error = -EINVAL; - *result = NULL; + ncp_invalid_dir_cache(dir); + finfo->ino = ncp_invent_inos(1); + inode = ncp_iget(dir->i_sb, finfo); + if (!inode) + goto out_close; + d_instantiate(dentry,inode); + error = 0; +out: + return error; + +out_close: +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_instantiate: %s/%s failed, closing file\n", +dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + ncp_close_file(NCP_SERVER(dir), finfo->nw_info.file_handle); + goto out; +} + +static int ncp_create(struct inode *dir, struct dentry *dentry, int mode) +{ + int error, result; + struct ncpfs_inode_info finfo; + __u8 _name[dentry->d_name.len + 1]; + +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_create: creating %s/%s, mode=%x\n", +dentry->d_parent->d_name.name, dentry->d_name.name, mode); +#endif if (!dir || !S_ISDIR(dir->i_mode)) { - printk("ncp_create: inode is NULL or not a directory\n"); - iput(dir); + printk(KERN_WARNING "ncp_create: inode is NULL or not a directory\n"); return -ENOENT; } - if (!ncp_conn_valid(NCP_SERVER(dir))) { - iput(dir); - return -EIO; - } - strncpy(_name, name, len); - _name[len] = '\0'; + error = -EIO; + if (!ncp_conn_valid(NCP_SERVER(dir))) + goto out; + + strncpy(_name, dentry->d_name.name, dentry->d_name.len); + _name[dentry->d_name.len] = '\0'; if (!ncp_preserve_case(dir)) { str_upper(_name); } - lock_super(dir->i_sb); - if (ncp_open_create_file_or_subdir(NCP_SERVER(dir), - NCP_ISTRUCT(dir), _name, - OC_MODE_CREATE | OC_MODE_OPEN | - OC_MODE_REPLACE, - 0, AR_READ | AR_WRITE, - &finfo) != 0) { - unlock_super(dir->i_sb); - iput(dir); - return -EACCES; - } - ncp_invalid_dir_cache(dir); - if (!ncp_preserve_case(dir)) { - str_lower(finfo.i.entryName); + error = -EACCES; + result = ncp_open_create_file_or_subdir(NCP_SERVER(dir), dir, _name, + OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE, + 0, AR_READ | AR_WRITE, &finfo.nw_info); + if (!result) { + finfo.nw_info.access = O_RDWR; + error = ncp_instantiate(dir, dentry, &finfo); + } else { + DPRINTK(KERN_DEBUG "ncp_create: %s/%s failed\n", + dentry->d_parent->d_name.name, dentry->d_name.name); } - finfo.access = O_RDWR; - if (!(*result = ncp_iget(dir, &finfo)) < 0) { - ncp_close_file(NCP_SERVER(dir), finfo.file_handle); - unlock_super(dir->i_sb); - iput(dir); - return -EINVAL; - } - unlock_super(dir->i_sb); - iput(dir); - return 0; +out: + return error; } -static int ncp_mkdir(struct inode *dir, const char *name, int len, int mode) +static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode) { int error; - struct nw_file_info new_dir; - __u8 _name[len + 1]; + struct ncpfs_inode_info finfo; + __u8 _name[dentry->d_name.len + 1]; - if ((name[0] == '.') - && ((len == 1) - || ((len == 2) - && (name[1] == '.')))) { - iput(dir); - return -EEXIST; + DPRINTK(KERN_DEBUG "ncp_mkdir: making %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + error = -ENOTDIR; + if (!dir || !S_ISDIR(dir->i_mode)) { + printk(KERN_WARNING "ncp_mkdir: inode is NULL or not a directory\n"); + goto out; } - strncpy(_name, name, len); - _name[len] = '\0'; + error = -EIO; + if (!ncp_conn_valid(NCP_SERVER(dir))) + goto out; + strncpy(_name, dentry->d_name.name, dentry->d_name.len); + _name[dentry->d_name.len] = '\0'; if (!ncp_preserve_case(dir)) { str_upper(_name); } - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("ncp_mkdir: inode is NULL or not a directory\n"); - iput(dir); - return -ENOENT; - } - if (!ncp_conn_valid(NCP_SERVER(dir))) { - iput(dir); - return -EIO; - } - if (ncp_open_create_file_or_subdir(NCP_SERVER(dir), - NCP_ISTRUCT(dir), _name, + + error = -EACCES; + if (ncp_open_create_file_or_subdir(NCP_SERVER(dir), dir, _name, OC_MODE_CREATE, aDIR, 0xffff, - &new_dir) != 0) { - error = -EACCES; - } else { - error = 0; - ncp_invalid_dir_cache(dir); + &finfo.nw_info) == 0) + { + error = ncp_instantiate(dir, dentry, &finfo); } - - iput(dir); +out: return error; } -static int ncp_rmdir(struct inode *dir, const char *name, int len) +static int ncp_rmdir(struct inode *dir, struct dentry *dentry) { - int error; - __u8 _name[len + 1]; + int error, result; + __u8 _name[dentry->d_name.len + 1]; - if (!dir || !S_ISDIR(dir->i_mode)) { - printk("ncp_rmdir: inode is NULL or not a directory\n"); - iput(dir); - return -ENOENT; - } - if (!ncp_conn_valid(NCP_SERVER(dir))) { - iput(dir); - return -EIO; - } - if (ncp_find_dir_inode(dir, name) != NULL) { - iput(dir); + DPRINTK(KERN_DEBUG "ncp_rmdir: removing %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + + error = -ENOENT; + if (!dir || !S_ISDIR(dir->i_mode)) + { + printk(KERN_WARNING "ncp_rmdir: inode is NULL or not a directory\n"); + goto out; + } + error = -EIO; + if (!ncp_conn_valid(NCP_SERVER(dir))) + goto out; + + if (dentry->d_count > 1) + { + shrink_dcache_parent(dentry); error = -EBUSY; - } else { - - strncpy(_name, name, len); - _name[len] = '\0'; + if (dentry->d_count > 1) + goto out; + } - if (!ncp_preserve_case(dir)) { - str_upper(_name); - } - if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir), - NCP_ISTRUCT(dir), - _name)) == 0) { - ncp_invalid_dir_cache(dir); - } else { - error = -EACCES; - } + strncpy(_name, dentry->d_name.name, dentry->d_name.len); + _name[dentry->d_name.len] = '\0'; + + if (!ncp_preserve_case(dir)) + { + str_upper(_name); } - iput(dir); + error = -EACCES; + result = ncp_del_file_or_subdir(NCP_SERVER(dir), dir, _name); + if (!result) + { + ncp_invalid_dir_cache(dir); + d_delete(dentry); + error = 0; + } +out: return error; } -static int ncp_unlink(struct inode *dir, const char *name, int len) +static int ncp_unlink(struct inode *dir, struct dentry *dentry) { - int error; - __u8 _name[len + 1]; - + struct inode *inode = dentry->d_inode; + int error, result; + __u8 _name[dentry->d_name.len + 1]; + + DPRINTK(KERN_DEBUG "ncp_unlink: unlinking %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + + error = -ENOTDIR; if (!dir || !S_ISDIR(dir->i_mode)) { - printk("ncp_unlink: inode is NULL or not a directory\n"); - iput(dir); - return -ENOENT; + printk(KERN_WARNING "ncp_unlink: inode is NULL or not a directory\n"); + goto out; } - if (!ncp_conn_valid(NCP_SERVER(dir))) { - iput(dir); - return -EIO; + error = -EIO; + if (!ncp_conn_valid(NCP_SERVER(dir))) + goto out; + + /* + * Check whether to close the file ... + */ + if (inode && NCP_FINFO(inode)->opened) { +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_unlink: closing file\n"); +#endif + ncp_make_closed(inode); } - if (ncp_find_dir_inode(dir, name) != NULL) { - iput(dir); - error = -EBUSY; - } else { - strncpy(_name, name, len); - _name[len] = '\0'; - if (!ncp_preserve_case(dir)) { - str_upper(_name); - } - if ((error = ncp_del_file_or_subdir(NCP_SERVER(dir), - NCP_ISTRUCT(dir), - _name)) == 0) { - ncp_invalid_dir_cache(dir); - } else { - error = -EACCES; - } + strncpy(_name, dentry->d_name.name, dentry->d_name.len); + _name[dentry->d_name.len] = '\0'; + if (!ncp_preserve_case(dir)) + { + str_upper(_name); } - iput(dir); + error = -EACCES; + result = ncp_del_file_or_subdir(NCP_SERVER(dir), dir, _name); + if (!result) { + DPRINTK(KERN_DEBUG "ncp: removed %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + ncp_invalid_dir_cache(dir); + d_delete(dentry); + error = 0; + } +out: return error; } -static int ncp_rename(struct inode *old_dir, const char *old_name, int old_len, - struct inode *new_dir, const char *new_name, int new_len) +static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) { - int res; - char _old_name[old_len + 1]; - char _new_name[new_len + 1]; + int old_len = old_dentry->d_name.len; + int new_len = new_dentry->d_name.len; + int error, result; + char _old_name[old_dentry->d_name.len + 1]; + char _new_name[new_dentry->d_name.len + 1]; + + DPRINTK(KERN_DEBUG "ncp_rename: %s/%s to %s/%s\n", + old_dentry->d_parent->d_name.name, old_dentry->d_name.name, + new_dentry->d_parent->d_name.name, new_dentry->d_name.name); + error = -ENOTDIR; if (!old_dir || !S_ISDIR(old_dir->i_mode)) { - printk("ncp_rename: old inode is NULL or not a directory\n"); - res = -ENOENT; - goto finished; - } - if (!ncp_conn_valid(NCP_SERVER(old_dir))) { - res = -EIO; - goto finished; + printk(KERN_WARNING "ncp_rename: old inode is NULL or not a directory\n"); + goto out; } if (!new_dir || !S_ISDIR(new_dir->i_mode)) { - printk("ncp_rename: new inode is NULL or not a directory\n"); - res = -ENOENT; - goto finished; + printk(KERN_WARNING "ncp_rename: new inode is NULL or not a directory\n"); + goto out; } - if ((ncp_find_dir_inode(old_dir, old_name) != NULL) - || (ncp_find_dir_inode(new_dir, new_name) != NULL)) { - res = -EBUSY; - goto finished; - } - strncpy(_old_name, old_name, old_len); - _old_name[old_len] = '\0'; + error = -EIO; + if (!ncp_conn_valid(NCP_SERVER(old_dir))) + goto out; + strncpy(_old_name, old_dentry->d_name.name, old_len); + _old_name[old_len] = '\0'; if (!ncp_preserve_case(old_dir)) { str_upper(_old_name); } - strncpy(_new_name, new_name, new_len); - _new_name[new_len] = '\0'; + strncpy(_new_name, new_dentry->d_name.name, new_len); + _new_name[new_len] = '\0'; if (!ncp_preserve_case(new_dir)) { str_upper(_new_name); } - res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), - NCP_ISTRUCT(old_dir), _old_name, - NCP_ISTRUCT(new_dir), _new_name); - if (res == 0) { + error = -EACCES; + result = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir), + old_dir, _old_name, + new_dir, _new_name); + if (result == 0) + { + DPRINTK(KERN_DEBUG "ncp renamed %s -> %s.\n", + old_dentry->d_name.name,new_dentry->d_name.name); ncp_invalid_dir_cache(old_dir); ncp_invalid_dir_cache(new_dir); - } else { - res = -EACCES; + d_move(old_dentry,new_dentry); + error = 0; } - - finished: - iput(old_dir); - iput(new_dir); - return res; +out: + return error; } /* The following routines are taken directly from msdos-fs */ @@ -1024,7 +1026,7 @@ static int day_n[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0}; - /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ +/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ extern struct timezone sys_tz; @@ -1042,23 +1044,24 @@ } /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ - -int ncp_date_dos2unix(unsigned short time, unsigned short date) +int +ncp_date_dos2unix(unsigned short time, unsigned short date) { int month, year, secs; month = ((date >> 5) & 15) - 1; year = date >> 9; - secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 * - ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 && - month < 2 ? 1 : 0) + 3653); + secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + + 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) + + year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653); /* days since 1.1.70 plus 80's leap day */ return local2utc(secs); } /* Convert linear UNIX date to a MS-DOS time/date pair. */ -void ncp_date_unix2dos(int unix_date, unsigned short *time, unsigned short *date) +void +ncp_date_unix2dos(int unix_date, unsigned short *time, unsigned short *date) { int day, year, nl_day, month; diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/file.c linux/fs/ncpfs/file.c --- v2.1.62/linux/fs/ncpfs/file.c Tue Sep 23 16:48:49 1997 +++ linux/fs/ncpfs/file.c Wed Nov 12 11:10:40 1997 @@ -2,6 +2,7 @@ * file.c * * Copyright (C) 1995, 1996 by Volker Lendecke + * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache * */ @@ -29,82 +30,113 @@ return 0; } -int ncp_make_open(struct inode *i, int right) +/* + * Open a file with the specified read/write mode. + */ +int ncp_make_open(struct inode *inode, int right) { - struct nw_file_info *finfo; - - if (i == NULL) { - printk("ncp_make_open: got NULL inode\n"); - return -EINVAL; - } - finfo = NCP_FINFO(i); - - DPRINTK("ncp_make_open: dirent->opened = %d\n", finfo->opened); - - lock_super(i->i_sb); - if (finfo->opened == 0) { - finfo->access = -1; + int error, result; + int access; + struct nw_file_info finfo; + + error = -EINVAL; + if (!inode) { + printk(KERN_ERR "ncp_make_open: got NULL inode\n"); + goto out; + } + + DPRINTK(KERN_DEBUG "ncp_make_open: opened=%d, volume # %u, dir entry # %u\n", + NCP_FINFO(inode)->opened, + NCP_FINFO(inode)->volNumber, + NCP_FINFO(inode)->dirEntNum); + error = -EACCES; + lock_super(inode->i_sb); + if (!NCP_FINFO(inode)->opened) { + finfo.i.dirEntNum = NCP_FINFO(inode)->dirEntNum; + finfo.i.volNumber = NCP_FINFO(inode)->volNumber; /* tries max. rights */ - if (ncp_open_create_file_or_subdir(NCP_SERVER(i), - NULL, NULL, - OC_MODE_OPEN, 0, - AR_READ | AR_WRITE, - finfo) == 0) { - finfo->access = O_RDWR; - } else if (ncp_open_create_file_or_subdir(NCP_SERVER(i), - NULL, NULL, - OC_MODE_OPEN, 0, - AR_READ, - finfo) == 0) { - finfo->access = O_RDONLY; + finfo.access = O_RDWR; + result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), + NULL, NULL, OC_MODE_OPEN, + 0, AR_READ | AR_WRITE, &finfo); + if (!result) + goto update; + finfo.access = O_RDONLY; + result = ncp_open_create_file_or_subdir(NCP_SERVER(inode), + NULL, NULL, OC_MODE_OPEN, + 0, AR_READ, &finfo); + if (!result) { +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_make_open: failed, result=%d\n", result); +#endif + goto out_unlock; } - } - unlock_super(i->i_sb); - - if (((right == O_RDONLY) && ((finfo->access == O_RDONLY) - || (finfo->access == O_RDWR))) - || ((right == O_WRONLY) && ((finfo->access == O_WRONLY) - || (finfo->access == O_RDWR))) - || ((right == O_RDWR) && (finfo->access == O_RDWR))) - return 0; - - return -EACCES; + /* + * Update the inode information. + */ + update: + ncp_update_inode(inode, &finfo); + } + + access = NCP_FINFO(inode)->access; +#ifdef NCPFS_PARANOIA +printk(KERN_DEBUG "ncp_make_open: file open, access=%x\n", access); +#endif + if (((right == O_RDONLY) && ((access == O_RDONLY) + || (access == O_RDWR))) + || ((right == O_WRONLY) && ((access == O_WRONLY) + || (access == O_RDWR))) + || ((right == O_RDWR) && (access == O_RDWR))) + error = 0; + +out_unlock: + unlock_super(inode->i_sb); +out: + return error; } -static long ncp_file_read(struct inode *inode, struct file *file, char *buf, unsigned long count) +static ssize_t +ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - int bufsize, already_read; + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + size_t already_read = 0; off_t pos; - int errno; + int bufsize, error; - DPRINTK("ncp_file_read: enter %s\n", NCP_ISTRUCT(inode)->entryName); + DPRINTK(KERN_DEBUG "ncp_file_read: enter %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + error = -EINVAL; if (inode == NULL) { - DPRINTK("ncp_file_read: inode = NULL\n"); - return -EINVAL; - } - if (!ncp_conn_valid(NCP_SERVER(inode))) { - return -EIO; + DPRINTK(KERN_DEBUG "ncp_file_read: inode = NULL\n"); + goto out; } + error = -EIO; + if (!ncp_conn_valid(NCP_SERVER(inode))) + goto out; + error = -EINVAL; if (!S_ISREG(inode->i_mode)) { - DPRINTK("ncp_file_read: read from non-file, mode %07o\n", + DPRINTK(KERN_DEBUG "ncp_file_read: read from non-file, mode %07o\n", inode->i_mode); - return -EINVAL; + goto out; } - pos = file->f_pos; + pos = file->f_pos; if (pos + count > inode->i_size) { count = inode->i_size - pos; } - if (count <= 0) { - return 0; + error = 0; + if (!count) /* size_t is never < 0 */ + goto out; + + error = ncp_make_open(inode, O_RDONLY); + if (error) { + printk(KERN_ERR "ncp_file_read: open failed, error=%d\n", error); + goto out; } - if ((errno = ncp_make_open(inode, O_RDONLY)) != 0) { - return errno; - } - bufsize = NCP_SERVER(inode)->buffer_size; - already_read = 0; + bufsize = NCP_SERVER(inode)->buffer_size; /* First read in as much as possible for each bufsize. */ while (already_read < count) { @@ -112,9 +144,12 @@ int to_read = min(bufsize - (pos % bufsize), count - already_read); - if (ncp_read(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, - pos, to_read, buf, &read_this_time) != 0) { - return -EIO; /* This is not exact, i know.. */ + error = ncp_read(NCP_SERVER(inode), + NCP_FINFO(inode)->file_handle, + pos, to_read, buf, &read_this_time); + if (error) { + error = -EIO; /* This is not exact, i know.. */ + goto out; } pos += read_this_time; buf += read_this_time; @@ -130,38 +165,43 @@ if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; } - mark_inode_dirty(inode); - DPRINTK("ncp_file_read: exit %s\n", NCP_ISTRUCT(inode)->entryName); - - return already_read; + DPRINTK(KERN_DEBUG "ncp_file_read: exit %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); +out: + return already_read ? already_read : error; } -static long ncp_file_write(struct inode *inode, struct file *file, const char *buf, - unsigned long count) +static ssize_t +ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - int bufsize, already_written; + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + size_t already_written = 0; off_t pos; - int errno; + int bufsize, errno; + DPRINTK(KERN_DEBUG "ncp_file_write: enter %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); if (inode == NULL) { - DPRINTK("ncp_file_write: inode = NULL\n"); + DPRINTK(KERN_DEBUG "ncp_file_write: inode = NULL\n"); return -EINVAL; } - if (!ncp_conn_valid(NCP_SERVER(inode))) { - return -EIO; - } + errno = -EIO; + if (!ncp_conn_valid(NCP_SERVER(inode))) + goto out; if (!S_ISREG(inode->i_mode)) { - DPRINTK("ncp_file_write: write to non-file, mode %07o\n", + DPRINTK(KERN_DEBUG "ncp_file_write: write to non-file, mode %07o\n", inode->i_mode); return -EINVAL; } - DPRINTK("ncp_file_write: enter %s\n", NCP_ISTRUCT(inode)->entryName); - if (count <= 0) { - return 0; - } - if ((errno = ncp_make_open(inode, O_RDWR)) != 0) { + errno = 0; + if (!count) + goto out; + errno = ncp_make_open(inode, O_RDWR); + if (errno) { + printk(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno); return errno; } pos = file->f_pos; @@ -192,17 +232,17 @@ } inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); file->f_pos = pos; if (pos > inode->i_size) { inode->i_size = pos; - ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); + ncp_invalid_dir_cache(dentry->d_parent->d_inode); } - DPRINTK("ncp_file_write: exit %s\n", NCP_ISTRUCT(inode)->entryName); - - return already_written; + DPRINTK(KERN_DEBUG "ncp_file_write: exit %s/%s\n", + dentry->d_parent->d_name.name, dentry->d_name.name); +out: + return already_written ? already_written : errno; } static struct file_operations ncp_file_operations = diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c --- v2.1.62/linux/fs/ncpfs/inode.c Mon Aug 18 18:19:46 1997 +++ linux/fs/ncpfs/inode.c Wed Nov 12 11:10:40 1997 @@ -3,6 +3,7 @@ * * Copyright (C) 1995, 1996 by Volker Lendecke * Modified for big endian by J.F. Chadima and David S. Miller + * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache * */ @@ -14,7 +15,6 @@ #include #include -#include #include #include #include @@ -28,84 +28,108 @@ #ifdef CONFIG_KERNELD #include #endif + +#include #include "ncplib_kernel.h" extern int close_fp(struct file *filp); -static void ncp_put_inode(struct inode *); static void ncp_read_inode(struct inode *); +static void ncp_put_inode(struct inode *); +static void ncp_delete_inode(struct inode *); +static int ncp_notify_change(struct inode *, struct iattr *); static void ncp_put_super(struct super_block *); -static void ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); -static int ncp_notify_change(struct inode *inode, struct iattr *attr); +static int ncp_statfs(struct super_block *, struct statfs *, int); static struct super_operations ncp_sops = { ncp_read_inode, /* read inode */ - ncp_notify_change, /* notify change */ NULL, /* write inode */ ncp_put_inode, /* put inode */ + ncp_delete_inode, /* delete inode */ + ncp_notify_change, /* notify change */ ncp_put_super, /* put superblock */ NULL, /* write superblock */ ncp_statfs, /* stat filesystem */ - NULL + NULL /* remount */ }; -/* ncp_read_inode: Called from iget, it only traverses the allocated - ncp_inode_info's and initializes the inode from the data found - there. It does not allocate or deallocate anything. */ +static struct nw_file_info *read_nwinfo = NULL; +static struct semaphore read_sem = MUTEX; -static void ncp_read_inode(struct inode *inode) +/* + * Fill in the ncpfs-specific information in the inode. + */ +void ncp_update_inode(struct inode *inode, struct nw_file_info *nwinfo) { - /* Our task should be extremely simple here. We only have to - look up the information somebody else (ncp_iget) put into - the inode tree. The address of this information is the - inode->i_ino. Just to make sure everything went well, we - check it's there. */ - - struct ncp_inode_info *inode_info = ncp_find_inode(inode); - - if (inode_info == NULL) { - /* Ok, now we're in trouble. The inode info is not there. What - should we do now??? */ - printk("ncp_read_inode: inode info not found\n"); - return; - } - inode_info->state = NCP_INODE_VALID; + NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum; + NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum; + NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber; + + NCP_FINFO(inode)->opened = nwinfo->opened; + NCP_FINFO(inode)->access = nwinfo->access; + NCP_FINFO(inode)->server_file_handle = nwinfo->server_file_handle; + memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle, + sizeof(nwinfo->file_handle)); +#ifdef NCPFS_DEBUG_VERBOSE +printk(KERN_DEBUG "ncp_update_inode: updated %s, volnum=%d, dirent=%u\n", +nwinfo->i.entryName, NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum); +#endif +} - NCP_INOP(inode) = inode_info; - inode_info->inode = inode; +/* + * Fill in the inode based on the nw_file_info structure. + */ +static void ncp_set_attr(struct inode *inode, struct nw_file_info *nwinfo) +{ + struct nw_info_struct *nwi = &nwinfo->i; + struct ncp_server *server = NCP_SERVER(inode); - if (NCP_ISTRUCT(inode)->attributes & aDIR) { - inode->i_mode = NCP_SERVER(inode)->m.dir_mode; + if (nwi->attributes & aDIR) { + inode->i_mode = server->m.dir_mode; /* for directories dataStreamSize seems to be some Object ID ??? */ inode->i_size = 512; } else { - inode->i_mode = NCP_SERVER(inode)->m.file_mode; - inode->i_size = le32_to_cpu(NCP_ISTRUCT(inode)->dataStreamSize); + inode->i_mode = server->m.file_mode; + inode->i_size = le32_to_cpu(nwi->dataStreamSize); } - DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode); + DDPRINTK(KERN_DEBUG "ncp_read_inode: inode->i_mode = %u\n", inode->i_mode); inode->i_nlink = 1; - inode->i_uid = NCP_SERVER(inode)->m.uid; - inode->i_gid = NCP_SERVER(inode)->m.gid; + inode->i_uid = server->m.uid; + inode->i_gid = server->m.gid; inode->i_blksize = 512; inode->i_rdev = 0; + inode->i_blocks = 0; if ((inode->i_blksize != 0) && (inode->i_size != 0)) { inode->i_blocks = (inode->i_size - 1) / inode->i_blksize + 1; - } else { - inode->i_blocks = 0; } - inode->i_mtime = ncp_date_dos2unix(le16_to_cpu(NCP_ISTRUCT(inode)->modifyTime), - le16_to_cpu(NCP_ISTRUCT(inode)->modifyDate)); - inode->i_ctime = ncp_date_dos2unix(le16_to_cpu(NCP_ISTRUCT(inode)->creationTime), - le16_to_cpu(NCP_ISTRUCT(inode)->creationDate)); + inode->i_mtime = ncp_date_dos2unix(le16_to_cpu(nwi->modifyTime), + le16_to_cpu(nwi->modifyDate)); + inode->i_ctime = ncp_date_dos2unix(le16_to_cpu(nwi->creationTime), + le16_to_cpu(nwi->creationDate)); inode->i_atime = ncp_date_dos2unix(0, - le16_to_cpu(NCP_ISTRUCT(inode)->lastAccessDate)); + le16_to_cpu(nwi->lastAccessDate)); + ncp_update_inode(inode, nwinfo); +} + +/* + * This is called from iget() with the read semaphore held. + * The global ncpfs_file_info structure has been set up by ncp_iget. + */ +static void ncp_read_inode(struct inode *inode) +{ + if (read_nwinfo == NULL) { + printk(KERN_ERR "ncp_read_inode: invalid call\n"); + return; + } + + ncp_set_attr(inode, read_nwinfo); if (S_ISREG(inode->i_mode)) { inode->i_op = &ncp_file_inode_operations; @@ -116,73 +140,103 @@ } } -static void ncp_put_inode(struct inode *inode) +/* + * Set up the ncpfs_inode_info pointer and get a new inode. + */ +struct inode * +ncp_iget(struct super_block *sb, struct ncpfs_inode_info *info) { - struct nw_file_info *finfo = NCP_FINFO(inode); - struct super_block *sb = inode->i_sb; + struct inode *inode; - lock_super(sb); - if (finfo->opened != 0) { - if (ncp_close_file(NCP_SERVER(inode), finfo->file_handle) != 0) { - /* We can't do anything but complain. */ - printk("ncp_put_inode: could not close\n"); - } + if (info == NULL) { + printk(KERN_ERR "ncp_iget: info is NULL\n"); + return NULL; } - DDPRINTK("ncp_put_inode: put %s\n", - finfo->i.entryName); - ncp_free_inode_info(NCP_INOP(inode)); + down(&read_sem); + read_nwinfo = &info->nw_info; + inode = iget(sb, info->ino); + read_nwinfo = NULL; + up(&read_sem); + if (!inode) + printk(KERN_ERR "ncp_iget: iget failed!\n"); + return inode; +} + +static void ncp_put_inode(struct inode *inode) +{ + if (inode->i_count == 1) + inode->i_nlink = 0; +} +static void +ncp_delete_inode(struct inode *inode) +{ if (S_ISDIR(inode->i_mode)) { - DDPRINTK("ncp_put_inode: put directory %ld\n", - inode->i_ino); + DDPRINTK(KERN_DEBUG "ncp_delete_inode: put directory %ld\n", inode->i_ino); ncp_invalid_dir_cache(inode); } + + if (NCP_FINFO(inode)->opened && ncp_make_closed(inode) != 0) { + /* We can't do anything but complain. */ + printk(KERN_ERR "ncp_delete_inode: could not close\n"); + } clear_inode(inode); - unlock_super(sb); +} + +static void ncp_init_root(struct ncp_server *server, + struct ncpfs_inode_info *info) +{ + struct ncp_inode_info *root = &(server->root); + struct nw_info_struct *i = &(root->finfo.i); + unsigned short dummy; + + DPRINTK(KERN_DEBUG "ncp_init_root: i = %x\n", (int) i); + + i->attributes = aDIR; + i->dataStreamSize= 1024; + i->dirEntNum = 0; + i->DosDirNum = 0; + i->volNumber = NCP_NUMBER_OF_VOLUMES + 1; /* illegal volnum */ + ncp_date_unix2dos(0, &(i->creationTime), &(i->creationDate)); + ncp_date_unix2dos(0, &(i->modifyTime ), &(i->modifyDate)); + ncp_date_unix2dos(0, &(dummy ), &(i->lastAccessDate)); + i->creationTime = le16_to_cpu(i->creationTime); + i->creationDate = le16_to_cpu(i->creationDate); + i->modifyTime = le16_to_cpu(i->modifyTime); + i->modifyDate = le16_to_cpu(i->modifyDate); + i->lastAccessDate= le16_to_cpu(i->lastAccessDate); + i->nameLen = 0; + i->entryName[0] = '\0'; + + root->finfo.opened= 0; + info->ino = 1; + info->nw_info = root->finfo; } struct super_block * - ncp_read_super(struct super_block *sb, void *raw_data, int silent) +ncp_read_super(struct super_block *sb, void *raw_data, int silent) { struct ncp_mount_data *data = (struct ncp_mount_data *) raw_data; struct ncp_server *server; struct file *ncp_filp; + struct inode *root_inode; kdev_t dev = sb->s_dev; int error; + struct ncpfs_inode_info finfo; - if (data == NULL) { - printk("ncp_read_super: missing data argument\n"); - sb->s_dev = 0; - return NULL; - } - if (data->version != NCP_MOUNT_VERSION) { - printk("ncp warning: mount version %s than kernel\n", - (data->version < NCP_MOUNT_VERSION) ? - "older" : "newer"); - sb->s_dev = 0; - return NULL; - } - if ((data->ncp_fd >= NR_OPEN) - || ((ncp_filp = current->files->fd[data->ncp_fd]) == NULL) - || (!S_ISSOCK(ncp_filp->f_dentry->d_inode->i_mode))) { - printk("ncp_read_super: invalid ncp socket\n"); - sb->s_dev = 0; - return NULL; - } - /* We must malloc our own super-block info */ - server = (struct ncp_server *) ncp_kmalloc(sizeof(struct ncp_server), - GFP_KERNEL); - - if (server == NULL) { - printk("ncp_read_super: could not alloc ncp_server\n"); - return NULL; - } - ncp_filp->f_count++; + MOD_INC_USE_COUNT; + if (data == NULL) + goto out_no_data; + if (data->version != NCP_MOUNT_VERSION) + goto out_bad_mount; + if ((data->ncp_fd >= NR_OPEN) || + ((ncp_filp = current->files->fd[data->ncp_fd]) == NULL) || + !S_ISSOCK(ncp_filp->f_dentry->d_inode->i_mode)) + goto out_bad_file; lock_super(sb); - - NCP_SBP(sb) = server; + ncp_filp->f_count++; sb->s_blocksize = 1024; /* Eh... Is this correct? */ sb->s_blocksize_bits = 10; @@ -190,6 +244,13 @@ sb->s_dev = dev; sb->s_op = &ncp_sops; + /* We must malloc our own super-block info */ + server = (struct ncp_server *) ncp_kmalloc(sizeof(struct ncp_server), + GFP_KERNEL); + if (server == NULL) + goto out_no_server; + NCP_SBP(sb) = server; + server->ncp_filp = ncp_filp; server->lock = 0; server->wait = NULL; @@ -202,7 +263,7 @@ now because of PATH_MAX changes.. */ if (server->m.time_out < 10) { server->m.time_out = 10; - printk("You need to recompile your ncpfs utils..\n"); + printk(KERN_INFO "You need to recompile your ncpfs utils..\n"); } server->m.file_mode = (server->m.file_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG; @@ -211,54 +272,74 @@ server->packet_size = NCP_PACKET_SIZE; server->packet = ncp_kmalloc(NCP_PACKET_SIZE, GFP_KERNEL); + if (server->packet == NULL) + goto out_no_packet; - if (server->packet == NULL) { - printk("ncpfs: could not alloc packet\n"); - error = -ENOMEM; - unlock_super(sb); - goto fail; - } ncp_lock_server(server); error = ncp_connect(server); ncp_unlock_server(server); - unlock_super(sb); + if (error < 0) + goto out_no_connect; + DPRINTK(KERN_DEBUG "ncp_read_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb)); + + error = ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE, + &(server->buffer_size)); + if (error) + goto out_no_bufsize; + DPRINTK(KERN_DEBUG "ncpfs: bufsize = %d\n", server->buffer_size); + + ncp_init_root(server, &finfo); + root_inode = ncp_iget(sb, &finfo); + if (!root_inode) + goto out_no_root; + DPRINTK(KERN_DEBUG "ncp_read_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber); + sb->s_root = d_alloc_root(root_inode, NULL); + if (!sb->s_root) + goto out_no_root; - if (error < 0) { - sb->s_dev = 0; - printk("ncp_read_super: Failed connection, bailing out " - "(error = %d).\n", -error); - ncp_kfree_s(server->packet, server->packet_size); - goto fail; - } - DPRINTK("ncp_read_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb)); - - ncp_init_root(server); - - if (!(sb->s_root = d_alloc_root(iget(sb,ncp_info_ino(server, - &(server->root))),NULL))) { - sb->s_dev = 0; - printk("ncp_read_super: get root inode failed\n"); - goto disconnect; - } - if (ncp_negotiate_buffersize(server, NCP_DEFAULT_BUFSIZE, - &(server->buffer_size)) != 0) { - sb->s_dev = 0; - printk("ncp_read_super: could not get bufsize\n"); - goto disconnect; - } - DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size); - - MOD_INC_USE_COUNT; + unlock_super(sb); return sb; - disconnect: +out_no_root: + printk(KERN_ERR "ncp_read_super: get root inode failed\n"); + iput(root_inode); + goto out_disconnect; +out_no_bufsize: + printk(KERN_ERR "ncp_read_super: could not get bufsize\n"); +out_disconnect: ncp_lock_server(server); ncp_disconnect(server); ncp_unlock_server(server); + goto out_free_packet; +out_no_connect: + printk(KERN_ERR "ncp_read_super: Failed connection, error=%d\n", error); +out_free_packet: ncp_kfree_s(server->packet, server->packet_size); - fail: - put_filp(ncp_filp); + goto out_free_server; +out_no_packet: + printk(KERN_ERR "ncp_read_super: could not alloc packet\n"); +out_free_server: ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server)); + goto out_unlock; +out_no_server: + printk(KERN_ERR "ncp_read_super: could not alloc ncp_server\n"); +out_unlock: + put_filp(ncp_filp); + unlock_super(sb); + goto out; + +out_bad_file: + printk(KERN_ERR "ncp_read_super: invalid ncp socket\n"); + goto out; +out_bad_mount: + printk(KERN_INFO "ncp_read_super: kernel requires mount version %d\n", + NCP_MOUNT_VERSION); + goto out; +out_no_data: + printk(KERN_ERR "ncp_read_super: missing data argument\n"); +out: + sb->s_dev = 0; + MOD_DEC_USE_COUNT; return NULL; } @@ -275,20 +356,17 @@ close_fp(server->ncp_filp); kill_proc(server->m.wdog_pid, SIGTERM, 0); - ncp_free_all_inodes(server); - ncp_kfree_s(server->packet, server->packet_size); - sb->s_dev = 0; ncp_kfree_s(NCP_SBP(sb), sizeof(struct ncp_server)); - NCP_SBP(sb) = NULL; + sb->s_dev = 0; unlock_super(sb); MOD_DEC_USE_COUNT; } -static void ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +static int ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; @@ -306,7 +384,7 @@ tmp.f_files = -1; tmp.f_ffree = -1; tmp.f_namelen = 12; - copy_to_user(buf, &tmp, bufsiz); + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } static int ncp_notify_change(struct inode *inode, struct iattr *attr) @@ -359,11 +437,9 @@ info.lastAccessDate = le16_to_cpu(info.lastAccessDate); } if (info_mask != 0) { - if ((result = - ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), - NCP_ISTRUCT(inode), - info_mask, - &info)) != 0) { + result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), + inode, info_mask, &info); + if (result != 0) { result = -EACCES; if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) { @@ -379,8 +455,7 @@ if ((attr->ia_valid & ATTR_SIZE) != 0) { int written; - DPRINTK("ncpfs: trying to change size of %s to %ld\n", - NCP_ISTRUCT(inode)->entryName, attr->ia_size); + DPRINTK(KERN_DEBUG "ncpfs: trying to change size to %ld\n", attr->ia_size); if ((result = ncp_make_open(inode, O_RDWR)) < 0) { return -EACCES; @@ -390,13 +465,12 @@ /* According to ndir, the changes only take effect after closing the file */ - ncp_close_file(NCP_SERVER(inode), - NCP_FINFO(inode)->file_handle); - NCP_FINFO(inode)->opened = 0; - - result = 0; + result = ncp_make_closed(inode); } - ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); + /* + * We need a dentry here ... + */ + /* ncp_invalid_dir_cache(NCP_INOP(inode)->dir->inode); */ return result; } @@ -423,7 +497,10 @@ int init_module(void) { - DPRINTK("ncpfs: init_module called\n"); + DPRINTK(KERN_DEBUG "ncpfs: init_module called\n"); + + read_sem = MUTEX; + read_nwinfo = NULL; #ifdef DEBUG_NCP_MALLOC ncp_malloced = 0; @@ -436,12 +513,12 @@ void cleanup_module(void) { - DPRINTK("ncpfs: cleanup_module called\n"); + DPRINTK(KERN_DEBUG "ncpfs: cleanup_module called\n"); ncp_free_dir_cache(); unregister_filesystem(&ncp_fs_type); #ifdef DEBUG_NCP_MALLOC - printk("ncp_malloced: %d\n", ncp_malloced); - printk("ncp_current_malloced: %d\n", ncp_current_malloced); + printk(KERN_DEBUG "ncp_malloced: %d\n", ncp_malloced); + printk(KERN_DEBUG "ncp_current_malloced: %d\n", ncp_current_malloced); #endif } diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/ioctl.c linux/fs/ncpfs/ioctl.c --- v2.1.62/linux/fs/ncpfs/ioctl.c Fri Apr 4 08:52:24 1997 +++ linux/fs/ncpfs/ioctl.c Wed Nov 12 11:10:40 1997 @@ -2,25 +2,27 @@ * ioctl.c * * Copyright (C) 1995, 1996 by Volker Lendecke + * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache * */ #include #include #include -#include #include #include #include + #include +#include int ncp_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { + struct ncp_server *server = NCP_SERVER(inode); int result; struct ncp_ioctl_request request; struct ncp_fs_info info; - struct ncp_server *server = NCP_SERVER(inode); switch (cmd) { case NCP_IOC_NCPREQUEST: @@ -56,7 +58,7 @@ ncp_request(server, request.function); - DPRINTK("ncp_ioctl: copy %d bytes\n", + DPRINTK(KERN_DEBUG "ncp_ioctl: copy %d bytes\n", server->reply_size); copy_to_user(request.data, server->packet, server->reply_size); @@ -82,19 +84,18 @@ sizeof(info))) != 0) { return result; } - copy_from_user(&info, (struct ncp_fs_info *) arg, - sizeof(info)); + copy_from_user(&info, (struct ncp_fs_info *) arg, sizeof(info)); if (info.version != NCP_GET_FS_INFO_VERSION) { - DPRINTK("info.version invalid: %d\n", info.version); + DPRINTK(KERN_DEBUG "info.version invalid: %d\n", info.version); return -EINVAL; } /* TODO: info.addr = server->m.serv_addr; */ - info.mounted_uid = server->m.mounted_uid; - info.connection = server->connection; - info.buffer_size = server->buffer_size; - info.volume_number = NCP_ISTRUCT(inode)->volNumber; - info.directory_id = NCP_ISTRUCT(inode)->DosDirNum; + info.mounted_uid = server->m.mounted_uid; + info.connection = server->connection; + info.buffer_size = server->buffer_size; + info.volume_number = NCP_FINFO(inode)->volNumber; + info.directory_id = NCP_FINFO(inode)->DosDirNum; copy_to_user((struct ncp_fs_info *) arg, &info, sizeof(info)); return 0; diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/mmap.c linux/fs/ncpfs/mmap.c --- v2.1.62/linux/fs/ncpfs/mmap.c Tue Sep 23 16:48:49 1997 +++ linux/fs/ncpfs/mmap.c Wed Nov 12 11:10:40 1997 @@ -2,6 +2,7 @@ * mmap.c * * Copyright (C) 1995, 1996 by Volker Lendecke + * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache * */ @@ -32,13 +33,14 @@ static unsigned long ncp_file_mmap_nopage(struct vm_area_struct *area, unsigned long address, int no_share) { - struct inode *inode = area->vm_dentry->d_inode; + struct dentry *dentry = area->vm_dentry; + struct inode *inode = dentry->d_inode; unsigned long page; unsigned int clear; unsigned long tmp; int bufsize; int pos; - unsigned long fs; + mm_segment_t fs; page = __get_free_page(GFP_KERNEL); if (!page) @@ -120,7 +122,7 @@ { struct inode *inode = file->f_dentry->d_inode; - DPRINTK("ncp_mmap: called\n"); + DPRINTK(KERN_DEBUG "ncp_mmap: called\n"); if (!ncp_conn_valid(NCP_SERVER(inode))) { return -EIO; @@ -132,7 +134,6 @@ return -EACCES; if (!IS_RDONLY(inode)) { inode->i_atime = CURRENT_TIME; - mark_inode_dirty(inode); } vma->vm_dentry = dget(file->f_dentry); diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/ncplib_kernel.c linux/fs/ncpfs/ncplib_kernel.c --- v2.1.62/linux/fs/ncpfs/ncplib_kernel.c Fri Apr 4 08:52:24 1997 +++ linux/fs/ncpfs/ncplib_kernel.c Wed Nov 12 11:10:40 1997 @@ -3,9 +3,11 @@ * * Copyright (C) 1995, 1996 by Volker Lendecke * Modified for big endian by J.F. Chadima and David S. Miller + * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache * */ + #include "ncplib_kernel.h" static inline int min(int a, int b) @@ -16,7 +18,7 @@ static void assert_server_locked(struct ncp_server *server) { if (server->lock == 0) { - DPRINTK("ncpfs: server not locked!\n"); + DPRINTK(KERN_DEBUG "ncpfs: server not locked!\n"); } } @@ -65,7 +67,7 @@ int len = strlen(s); assert_server_locked(server); if (len > 255) { - DPRINTK("ncpfs: string too long: %s\n", s); + DPRINTK(KERN_DEBUG "ncpfs: string too long: %s\n", s); len = 255; } ncp_add_byte(server, len); @@ -115,8 +117,8 @@ return get_unaligned((__u32 *) ncp_reply_data(server, offset)); } -int ncp_negotiate_buffersize(struct ncp_server *server, - int size, int *target) +int +ncp_negotiate_buffersize(struct ncp_server *server, int size, int *target) { int result; @@ -133,7 +135,8 @@ return 0; } -int ncp_get_volume_info_with_number(struct ncp_server *server, int n, +int +ncp_get_volume_info_with_number(struct ncp_server *server, int n, struct ncp_volume_info *target) { int result; @@ -143,8 +146,7 @@ ncp_add_byte(server, n); if ((result = ncp_request(server, 22)) != 0) { - ncp_unlock_server(server); - return result; + goto out; } target->total_blocks = ncp_reply_dword(server, 0); target->free_blocks = ncp_reply_dword(server, 4); @@ -156,18 +158,21 @@ memset(&(target->volume_name), 0, sizeof(target->volume_name)); + result = -EIO; len = ncp_reply_byte(server, 29); if (len > NCP_VOLNAME_LEN) { - DPRINTK("ncpfs: volume name too long: %d\n", len); - ncp_unlock_server(server); - return -EIO; + DPRINTK(KERN_DEBUG "ncpfs: volume name too long: %d\n", len); + goto out; } memcpy(&(target->volume_name), ncp_reply_data(server, 30), len); + result = 0; +out: ncp_unlock_server(server); - return 0; + return result; } -int ncp_close_file(struct ncp_server *server, const char *file_id) +int +ncp_close_file(struct ncp_server *server, const char *file_id) { int result; @@ -180,10 +185,25 @@ return result; } -static void ncp_add_handle_path(struct ncp_server *server, - __u8 vol_num, - __u32 dir_base, int have_dir_base, - char *path) +/* + * Called with the superblock locked. + */ +int +ncp_make_closed(struct inode *inode) +{ + int err; + NCP_FINFO(inode)->opened = 0; + err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle); +#ifdef NCPFS_PARANOIA +if (!err) +printk(KERN_DEBUG "ncp_make_closed: volnum=%d, dirent=%u, error=%d\n", +NCP_FINFO(inode)->volNumber, NCP_FINFO(inode)->dirEntNum, err); +#endif + return err; +} + +static void ncp_add_handle_path(struct ncp_server *server, __u8 vol_num, + __u32 dir_base, int have_dir_base, char *path) { ncp_add_byte(server, vol_num); ncp_add_dword(server, dir_base); @@ -213,34 +233,40 @@ return; } -int ncp_obtain_info(struct ncp_server *server, - __u8 vol_num, __u32 dir_base, - char *path, /* At most 1 component */ - struct nw_info_struct *target) +/* + * Returns information for a (one-component) name relative to + * the specified directory. + */ +int ncp_obtain_info(struct ncp_server *server, struct inode *dir, char *path, + struct nw_info_struct *target) { + __u8 volnum = NCP_FINFO(dir)->volNumber; + __u32 dirent = NCP_FINFO(dir)->dirEntNum; int result; if (target == NULL) { + printk(KERN_ERR "ncp_obtain_info: invalid call\n"); return -EINVAL; } ncp_init_request(server); ncp_add_byte(server, 6); /* subfunction */ - ncp_add_byte(server, server->name_space[vol_num]); - ncp_add_byte(server, server->name_space[vol_num]); + ncp_add_byte(server, server->name_space[volnum]); + ncp_add_byte(server, server->name_space[volnum]); /* N.B. twice ?? */ ncp_add_word(server, htons(0xff00)); /* get all */ ncp_add_dword(server, RIM_ALL); - ncp_add_handle_path(server, vol_num, dir_base, 1, path); + ncp_add_handle_path(server, volnum, dirent, 1, path); - if ((result = ncp_request(server, 87)) != 0) { - ncp_unlock_server(server); - return result; - } + if ((result = ncp_request(server, 87)) != 0) + goto out; ncp_extract_file_info(ncp_reply_data(server, 0), target); + +out: ncp_unlock_server(server); - return 0; + return result; } -static inline int ncp_has_os2_namespace(struct ncp_server *server, __u8 volume) +static inline int +ncp_has_os2_namespace(struct ncp_server *server, __u8 volume) { int result; __u8 *namespace; @@ -253,34 +279,36 @@ if ((result = ncp_request(server, 87)) != 0) { ncp_unlock_server(server); - return 0; + return 0; /* not result ?? */ } no_namespaces = ncp_reply_word(server, 0); namespace = ncp_reply_data(server, 2); + result = 1; while (no_namespaces > 0) { - DPRINTK("get_namespaces: found %d on %d\n", *namespace, volume); + DPRINTK(KERN_DEBUG "get_namespaces: found %d on %d\n", *namespace, volume); if (*namespace == 4) { - DPRINTK("get_namespaces: found OS2\n"); - ncp_unlock_server(server); - return 1; + DPRINTK(KERN_DEBUG "get_namespaces: found OS2\n"); + goto out; } namespace += 1; no_namespaces -= 1; } + result = 0; +out: ncp_unlock_server(server); - return 0; + return result; } -int ncp_lookup_volume(struct ncp_server *server, - char *volname, +int +ncp_lookup_volume(struct ncp_server *server, char *volname, struct nw_info_struct *target) { int result; int volnum; - DPRINTK("ncp_lookup_volume: looking up vol %s\n", volname); + DPRINTK(KERN_DEBUG "ncp_lookup_volume: looking up vol %s\n", volname); ncp_init_request(server); ncp_add_byte(server, 22); /* Subfunction: Generate dir handle */ @@ -306,7 +334,7 @@ server->name_space[volnum] = ncp_has_os2_namespace(server, volnum) ? 4 : 0; - DPRINTK("lookup_vol: namespace[%d] = %d\n", + DPRINTK(KERN_DEBUG "lookup_vol: namespace[%d] = %d\n", volnum, server->name_space[volnum]); target->nameLen = strlen(volname); @@ -316,22 +344,22 @@ } int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, - struct nw_info_struct *file, - __u32 info_mask, + struct inode *dir, __u32 info_mask, struct nw_modify_dos_info *info) { + __u8 volnum = NCP_FINFO(dir)->volNumber; + __u32 dirent = NCP_FINFO(dir)->dirEntNum; int result; ncp_init_request(server); ncp_add_byte(server, 7); /* subfunction */ - ncp_add_byte(server, server->name_space[file->volNumber]); + ncp_add_byte(server, server->name_space[volnum]); ncp_add_byte(server, 0); /* reserved */ ncp_add_word(server, htons(0x0680)); /* search attribs: all */ ncp_add_dword(server, info_mask); ncp_add_mem(server, info, sizeof(*info)); - ncp_add_handle_path(server, file->volNumber, - file->dirEntNum, 1, NULL); + ncp_add_handle_path(server, volnum, dirent, 1, NULL); result = ncp_request(server, 87); ncp_unlock_server(server); @@ -339,17 +367,18 @@ } int ncp_del_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *dir, char *name) + struct inode *dir, char *name) { + __u8 volnum = NCP_FINFO(dir)->volNumber; + __u32 dirent = NCP_FINFO(dir)->dirEntNum; int result; ncp_init_request(server); ncp_add_byte(server, 8); /* subfunction */ - ncp_add_byte(server, server->name_space[dir->volNumber]); + ncp_add_byte(server, server->name_space[volnum]); ncp_add_byte(server, 0); /* reserved */ ncp_add_word(server, ntohs(0x0680)); /* search attribs: all */ - ncp_add_handle_path(server, dir->volNumber, - dir->dirEntNum, 1, name); + ncp_add_handle_path(server, volnum, dirent, 1, name); result = ncp_request(server, 87); ncp_unlock_server(server); @@ -367,22 +396,29 @@ /* If both dir and name are NULL, then in target there's already a looked-up entry that wants to be opened. */ int ncp_open_create_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *dir, char *name, + struct inode *dir, char *name, int open_create_mode, __u32 create_attributes, int desired_acc_rights, struct nw_file_info *target) { - int result; __u16 search_attribs = ntohs(0x0600); - __u8 volume = (dir != NULL) ? dir->volNumber : target->i.volNumber; + __u8 volnum = target->i.volNumber; + __u32 dirent = target->i.dirEntNum; + int result; + + if (dir) + { + volnum = NCP_FINFO(dir)->volNumber; + dirent = NCP_FINFO(dir)->dirEntNum; + } if ((create_attributes & aDIR) != 0) { search_attribs |= ntohs(0x0080); } ncp_init_request(server); ncp_add_byte(server, 1); /* subfunction */ - ncp_add_byte(server, server->name_space[volume]); + ncp_add_byte(server, server->name_space[volnum]); ncp_add_byte(server, open_create_mode); ncp_add_word(server, search_attribs); ncp_add_dword(server, RIM_ALL); @@ -390,53 +426,47 @@ /* The desired acc rights seem to be the inherited rights mask for directories */ ncp_add_word(server, desired_acc_rights); + ncp_add_handle_path(server, volnum, dirent, 1, name); - if (dir != NULL) { - ncp_add_handle_path(server, volume, dir->dirEntNum, 1, name); - } else { - ncp_add_handle_path(server, volume, target->i.dirEntNum, - 1, NULL); - } - - if ((result = ncp_request(server, 87)) != 0) { - ncp_unlock_server(server); - return result; - } + if ((result = ncp_request(server, 87)) != 0) + goto out; target->opened = 1; target->server_file_handle = ncp_reply_dword(server, 0); target->open_create_action = ncp_reply_byte(server, 4); if (dir != NULL) { /* in target there's a new finfo to fill */ - ncp_extract_file_info(ncp_reply_data(server, 5), &(target->i)); + ncp_extract_file_info(ncp_reply_data(server, 6), &(target->i)); } ConvertToNWfromDWORD(target->server_file_handle, target->file_handle); +out: ncp_unlock_server(server); - return 0; + return result; } - -int ncp_initialize_search(struct ncp_server *server, - struct nw_info_struct *dir, - struct nw_search_sequence *target) +int +ncp_initialize_search(struct ncp_server *server, struct inode *dir, + struct nw_search_sequence *target) { + __u8 volnum = NCP_FINFO(dir)->volNumber; + __u32 dirent = NCP_FINFO(dir)->dirEntNum; int result; ncp_init_request(server); ncp_add_byte(server, 2); /* subfunction */ - ncp_add_byte(server, server->name_space[dir->volNumber]); + ncp_add_byte(server, server->name_space[volnum]); ncp_add_byte(server, 0); /* reserved */ - ncp_add_handle_path(server, dir->volNumber, dir->dirEntNum, 1, NULL); + ncp_add_handle_path(server, volnum, dirent, 1, NULL); - if ((result = ncp_request(server, 87)) != 0) { - ncp_unlock_server(server); - return result; - } + result = ncp_request(server, 87); + if (result) + goto out; memcpy(target, ncp_reply_data(server, 0), sizeof(*target)); +out: ncp_unlock_server(server); - return 0; + return result; } /* Search for everything */ @@ -457,42 +487,41 @@ ncp_add_byte(server, 0xff); /* following is a wildcard */ ncp_add_byte(server, '*'); - if ((result = ncp_request(server, 87)) != 0) { - ncp_unlock_server(server); - return result; - } + if ((result = ncp_request(server, 87)) != 0) + goto out; memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq)); ncp_extract_file_info(ncp_reply_data(server, 10), target); +out: ncp_unlock_server(server); - return 0; + return result; } int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *old_dir, char *old_name, - struct nw_info_struct *new_dir, char *new_name) + struct inode *old_dir, char *old_name, + struct inode *new_dir, char *new_name) { - int result; + int result = -EINVAL; - if ((old_dir == NULL) || (old_name == NULL) - || (new_dir == NULL) || (new_name == NULL)) - return -EINVAL; + if ((old_dir == NULL) || (old_name == NULL) || + (new_dir == NULL) || (new_name == NULL)) + goto out; ncp_init_request(server); ncp_add_byte(server, 4); /* subfunction */ - ncp_add_byte(server, server->name_space[old_dir->volNumber]); + ncp_add_byte(server, server->name_space[NCP_FINFO(old_dir)->volNumber]); ncp_add_byte(server, 1); /* rename flag */ ncp_add_word(server, ntohs(0x0680)); /* search attributes */ /* source Handle Path */ - ncp_add_byte(server, old_dir->volNumber); - ncp_add_dword(server, old_dir->dirEntNum); + ncp_add_byte(server, NCP_FINFO(old_dir)->volNumber); + ncp_add_dword(server, NCP_FINFO(old_dir)->dirEntNum); ncp_add_byte(server, 1); ncp_add_byte(server, 1); /* 1 source component */ /* dest Handle Path */ - ncp_add_byte(server, new_dir->volNumber); - ncp_add_dword(server, new_dir->dirEntNum); + ncp_add_byte(server, NCP_FINFO(new_dir)->volNumber); + ncp_add_dword(server, NCP_FINFO(new_dir)->dirEntNum); ncp_add_byte(server, 1); ncp_add_byte(server, 1); /* 1 destination component */ @@ -503,15 +532,17 @@ result = ncp_request(server, 87); ncp_unlock_server(server); +out: return result; } /* We have to transfer to/from user space */ -int ncp_read(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_read, - char *target, int *bytes_read) +int +ncp_read(struct ncp_server *server, const char *file_id, + __u32 offset, __u16 to_read, char *target, int *bytes_read) { + char *source; int result; ncp_init_request(server); @@ -521,20 +552,23 @@ ncp_add_word(server, htons(to_read)); if ((result = ncp_request(server, 72)) != 0) { - ncp_unlock_server(server); - return result; + goto out; } *bytes_read = ntohs(ncp_reply_word(server, 0)); + source = ncp_reply_data(server, 2 + (offset & 1)); - copy_to_user(target, ncp_reply_data(server, 2 + (offset & 1)), *bytes_read); - + result = -EFAULT; + if (!copy_to_user(target, source, *bytes_read)) + result = 0; +out: ncp_unlock_server(server); - return 0; + return result; } -int ncp_write(struct ncp_server *server, const char *file_id, +int +ncp_write(struct ncp_server *server, const char *file_id, __u32 offset, __u16 to_write, - const char *source, int *bytes_written) + const char *source, int *bytes_written) { int result; @@ -545,12 +579,11 @@ ncp_add_word(server, htons(to_write)); ncp_add_mem_fromfs(server, source, to_write); - if ((result = ncp_request(server, 73)) != 0) { - ncp_unlock_server(server); - return result; - } + if ((result = ncp_request(server, 73)) != 0) + goto out; *bytes_written = to_write; - + result = 0; +out: ncp_unlock_server(server); - return 0; + return result; } diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/ncplib_kernel.h linux/fs/ncpfs/ncplib_kernel.h --- v2.1.62/linux/fs/ncpfs/ncplib_kernel.h Fri Apr 4 08:52:24 1997 +++ linux/fs/ncpfs/ncplib_kernel.h Wed Nov 12 11:10:40 1997 @@ -3,6 +3,7 @@ * * Copyright (C) 1995, 1996 by Volker Lendecke * Modified for big endian by J.F. Chadima and David S. Miller + * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache * */ @@ -10,9 +11,6 @@ #define _NCPLIB_H #include -#include -#include -#include #include #include #include @@ -24,71 +22,35 @@ #include #include +#include +#include + +int ncp_negotiate_buffersize(struct ncp_server *, int, int *); +int ncp_get_volume_info_with_number(struct ncp_server *, int, + struct ncp_volume_info *); +int ncp_close_file(struct ncp_server *, const char *); +int ncp_read(struct ncp_server *, const char *, __u32, __u16, char *, int *); +int ncp_write(struct ncp_server *, const char *, __u32, __u16, + const char *, int *); -int -ncp_negotiate_buffersize(struct ncp_server *server, int size, - int *target); -int -ncp_get_volume_info_with_number(struct ncp_server *server, int n, - struct ncp_volume_info *target); - -int -ncp_close_file(struct ncp_server *server, const char *file_id); - -int -ncp_read(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_read, - char *target, int *bytes_read); - -int -ncp_write(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_write, - const char *source, int *bytes_written); - -int -ncp_obtain_info(struct ncp_server *server, - __u8 vol_num, __u32 dir_base, - char *path, /* At most 1 component */ +int ncp_obtain_info(struct ncp_server *server, struct inode *, char *, struct nw_info_struct *target); +int ncp_lookup_volume(struct ncp_server *, char *, struct nw_info_struct *); +int ncp_modify_file_or_subdir_dos_info(struct ncp_server *, struct inode *, + __u32, struct nw_modify_dos_info *info); + +int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, char *); +int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, char *, + int, __u32, int, struct nw_file_info *); -int -ncp_lookup_volume(struct ncp_server *server, - char *volname, - struct nw_info_struct *target); - - -int -ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, - struct nw_info_struct *file, - __u32 info_mask, - struct nw_modify_dos_info *info); - -int -ncp_del_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *dir, char *name); - -int -ncp_open_create_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *dir, char *name, - int open_create_mode, - __u32 create_attributes, - int desired_acc_rights, - struct nw_file_info *target); - -int -ncp_initialize_search(struct ncp_server *server, - struct nw_info_struct *dir, +int ncp_initialize_search(struct ncp_server *, struct inode *, struct nw_search_sequence *target); - -int -ncp_search_for_file_or_subdir(struct ncp_server *server, +int ncp_search_for_file_or_subdir(struct ncp_server *server, struct nw_search_sequence *seq, struct nw_info_struct *target); -int -ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, - struct nw_info_struct *old_dir, char *old_name, - struct nw_info_struct *new_dir, char *new_name); +int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server, + struct inode *, char *, struct inode *, char *); #endif /* _NCPLIB_H */ diff -u --recursive --new-file v2.1.62/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c --- v2.1.62/linux/fs/ncpfs/sock.c Wed Sep 24 20:05:48 1997 +++ linux/fs/ncpfs/sock.c Wed Nov 12 11:10:40 1997 @@ -4,11 +4,11 @@ * Copyright (C) 1992, 1993 Rick Sladkey * * Modified 1995, 1996 by Volker Lendecke to be usable for ncp + * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache * */ #include -#include #include #include #include @@ -19,13 +19,13 @@ #include #include #include +#include #include +#include #include #include #include -#include -#include static int _recv(struct socket *sock, unsigned char *ubuf, int size, unsigned flags) @@ -82,7 +82,7 @@ struct file *file; struct inode *inode; struct socket *sock; - unsigned long fs; + mm_segment_t fs; int result; char *start = server->packet; poll_table wait_table; @@ -104,10 +104,12 @@ file = server->ncp_filp; inode = file->f_dentry->d_inode; sock = &inode->u.socket_i; + /* N.B. this isn't needed ... check socket type? */ if (!sock) { - printk("ncp_rpc_call: socki_lookup failed\n"); + printk(KERN_ERR "ncp_rpc_call: socki_lookup failed\n"); return -EBADF; } + init_timeout = server->m.time_out; max_timeout = NCP_MAX_RPC_TIMEOUT; retrans = server->m.retry_count; @@ -127,7 +129,8 @@ fs = get_fs(); set_fs(get_ds()); for (n = 0, timeout = init_timeout;; n++, timeout <<= 1) { - DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n", + /* + DDPRINTK(KERN_DEBUG "ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n", htonl(server->m.serv_addr.sipx_network), server->m.serv_addr.sipx_node[0], server->m.serv_addr.sipx_node[1], @@ -136,17 +139,18 @@ server->m.serv_addr.sipx_node[4], server->m.serv_addr.sipx_node[5], ntohs(server->m.serv_addr.sipx_port)); - DDPRINTK("ncpfs: req.typ: %04X, con: %d, " + */ + DDPRINTK(KERN_DEBUG "ncpfs: req.typ: %04X, con: %d, " "seq: %d", request.type, (request.conn_high << 8) + request.conn_low, request.sequence); - DDPRINTK(" func: %d\n", + DDPRINTK(KERN_DEBUG " func: %d\n", request.function); result = _send(sock, (void *) start, size); if (result < 0) { - printk("ncp_rpc_call: send error = %d\n", result); + printk(KERN_ERR "ncp_rpc_call: send error = %d\n", result); break; } re_select: @@ -159,7 +163,7 @@ * This is useful to see if the system is * hanging */ if (acknowledge_seen == 0) { - printk("NCP max timeout\n"); + printk(KERN_WARNING "NCP max timeout\n"); } timeout = max_timeout; } @@ -176,7 +180,7 @@ if (n < retrans) continue; if (server->m.flags & NCP_MOUNT_SOFT) { - printk("NCP server not responding\n"); + printk(KERN_WARNING "NCP server not responding\n"); result = -EIO; break; } @@ -184,7 +188,7 @@ timeout = init_timeout; init_timeout <<= 1; if (!major_timeout_seen) { - printk("NCP server not responding\n"); + printk(KERN_WARNING "NCP server not responding\n"); } major_timeout_seen = 1; continue; @@ -201,15 +205,15 @@ MSG_PEEK | MSG_DONTWAIT); if (result < 0) { if (result == -EAGAIN) { - DPRINTK("ncp_rpc_call: bad select ready\n"); + DDPRINTK(KERN_DEBUG "ncp_rpc_call: bad select ready\n"); goto re_select; } if (result == -ECONNREFUSED) { - DPRINTK("ncp_rpc_call: server playing coy\n"); + DPRINTK(KERN_WARNING "ncp_rpc_call: server playing coy\n"); goto re_select; } if (result != -ERESTARTSYS) { - printk("ncp_rpc_call: recv error = %d\n", + printk(KERN_ERR "ncp_rpc_call: recv error = %d\n", -result); } break; @@ -217,7 +221,7 @@ if ((result == sizeof(reply)) && (reply.type == NCP_POSITIVE_ACK)) { /* Throw away the packet */ - DPRINTK("ncp_rpc_call: got positive acknowledge\n"); + DPRINTK(KERN_DEBUG "ncp_rpc_call: got positive acknowledge\n"); _recv(sock, (void *) &reply, sizeof(reply), MSG_DONTWAIT); n = 0; @@ -225,7 +229,7 @@ acknowledge_seen = 1; goto re_select; } - DDPRINTK("ncpfs: rep.typ: %04X, con: %d, tsk: %d," + DDPRINTK(KERN_DEBUG "ncpfs: rep.typ: %04X, con: %d, tsk: %d," "seq: %d\n", reply.type, (reply.conn_high << 8) + reply.conn_low, @@ -240,7 +244,7 @@ /* seem to get wrong task from NW311 && (reply.task == request.task) */ && (reply.conn_high == request.conn_high)))) { if (major_timeout_seen) - printk("NCP server OK\n"); + printk(KERN_NOTICE "NCP server OK\n"); break; } /* JEJB/JSP 2/7/94 @@ -249,7 +253,7 @@ * a null buffer yet. */ _recv(sock, (void *) &reply, sizeof(reply), MSG_DONTWAIT); - DPRINTK("ncp_rpc_call: reply mismatch\n"); + DPRINTK(KERN_WARNING "ncp_rpc_call: reply mismatch\n"); goto re_select; } /* @@ -258,11 +262,11 @@ */ result = _recv(sock, (void *) start, server->packet_size, MSG_DONTWAIT); if (result < 0) { - printk("NCP: notice message: result=%d\n", result); + printk(KERN_WARNING "NCP: notice message: result=%d\n", result); } else if (result < sizeof(struct ncp_reply_header)) { - printk("NCP: just caught a too small read memory size..., " + printk(KERN_ERR "NCP: just caught a too small read memory size..., " "email to NET channel\n"); - printk("NCP: result=%d\n", result); + printk(KERN_ERR "NCP: result=%d\n", result); result = -EIO; } current->blocked = old_mask; @@ -279,7 +283,7 @@ int result; if (server->lock == 0) { - printk("ncpfs: Server not locked!\n"); + printk(KERN_ERR "ncpfs: Server not locked!\n"); return -EIO; } if (!ncp_conn_valid(server)) { @@ -287,7 +291,7 @@ } result = do_ncp_rpc_call(server, size); - DDPRINTK("do_ncp_rpc_call returned %d\n", result); + DDPRINTK(KERN_DEBUG "do_ncp_rpc_call returned %d\n", result); if (result < 0) { /* There was a problem with I/O, so the connections is @@ -299,19 +303,17 @@ /* ncp_do_request assures that at least a complete reply header is * received. It assumes that server->current_size contains the ncp - * request size */ + * request size + */ int ncp_request(struct ncp_server *server, int function) { - struct ncp_request_header *h - = (struct ncp_request_header *) (server->packet); - struct ncp_reply_header *reply - = (struct ncp_reply_header *) (server->packet); - + struct ncp_request_header *h; + struct ncp_reply_header *reply; int request_size = server->current_size - - sizeof(struct ncp_request_header); - + - sizeof(struct ncp_request_header); int result; + h = (struct ncp_request_header *) (server->packet); if (server->has_subfunction != 0) { *(__u16 *) & (h->data[0]) = htons(request_size - 2); } @@ -321,13 +323,19 @@ h->sequence = server->sequence; h->conn_low = (server->connection) & 0xff; h->conn_high = ((server->connection) & 0xff00) >> 8; - h->task = (current->pid) & 0xff; + /* + * The server shouldn't know or care what task is making a + * request, so we always use the same task number. + */ + h->task = 2; /* (current->pid) & 0xff; */ h->function = function; - if ((result = ncp_do_request(server, request_size + sizeof(*h))) < 0) { - DPRINTK("ncp_request_error: %d\n", result); - return result; + result = ncp_do_request(server, request_size + sizeof(*h)); + if (result < 0) { + DPRINTK(KERN_WARNING "ncp_request_error: %d\n", result); + goto out; } + reply = (struct ncp_reply_header *) (server->packet); server->completion = reply->completion_code; server->conn_status = reply->connection_state; server->reply_size = result; @@ -335,48 +343,52 @@ result = reply->completion_code; - if (result != 0) { - DPRINTK("ncp_completion_code: %x\n", result); - } +#ifdef NCPFS_PARANOIA +if (result != 0) +printk(KERN_DEBUG "ncp_request: completion code=%x\n", result); +#endif +out: return result; } int ncp_connect(struct ncp_server *server) { - struct ncp_request_header *h - = (struct ncp_request_header *) (server->packet); + struct ncp_request_header *h; int result; + h = (struct ncp_request_header *) (server->packet); h->type = NCP_ALLOC_SLOT_REQUEST; server->sequence = 0; - h->sequence = server->sequence; - h->conn_low = 0xff; - h->conn_high = 0xff; - h->task = (current->pid) & 0xff; - h->function = 0; - - if ((result = ncp_do_request(server, sizeof(*h))) < 0) { - return result; - } + h->sequence = server->sequence; + h->conn_low = 0xff; + h->conn_high = 0xff; + h->task = 2; /* see above */ + h->function = 0; + + result = ncp_do_request(server, sizeof(*h)); + if (result < 0) + goto out; server->sequence = 0; server->connection = h->conn_low + (h->conn_high * 256); - return 0; + result = 0; +out: + return result; } int ncp_disconnect(struct ncp_server *server) { - struct ncp_request_header *h - = (struct ncp_request_header *) (server->packet); + struct ncp_request_header *h; + h = (struct ncp_request_header *) (server->packet); h->type = NCP_DEALLOC_SLOT_REQUEST; server->sequence += 1; - h->sequence = server->sequence; - h->conn_low = (server->connection) & 0xff; - h->conn_high = ((server->connection) & 0xff00) >> 8; - h->task = (current->pid) & 0xff; - h->function = 0; + h->sequence = server->sequence; + h->conn_low = (server->connection) & 0xff; + h->conn_high = ((server->connection) & 0xff00) >> 8; + h->task = 2; /* see above */ + h->function = 0; return ncp_do_request(server, sizeof(*h)); } @@ -386,7 +398,7 @@ #if 0 /* For testing, only 1 process */ if (server->lock != 0) { - DPRINTK("ncpfs: server locked!!!\n"); + DPRINTK(KERN_WARNING "ncpfs: server locked!!!\n"); } #endif while (server->lock) @@ -397,7 +409,7 @@ void ncp_unlock_server(struct ncp_server *server) { if (server->lock != 1) { - printk("ncp_unlock_server: was not locked!\n"); + printk(KERN_WARNING "ncp_unlock_server: was not locked!\n"); } server->lock = 0; wake_up(&server->wait); diff -u --recursive --new-file v2.1.62/linux/fs/nfs/dir.c linux/fs/nfs/dir.c --- v2.1.62/linux/fs/nfs/dir.c Mon Nov 3 13:04:27 1997 +++ linux/fs/nfs/dir.c Sat Nov 8 11:33:05 1997 @@ -31,9 +31,13 @@ #define NFS_MAX_AGE 10*HZ /* max age for dentry validation */ +#ifndef shrink_dcache_parent +#define shrink_dcache_parent(dentry) shrink_dcache_sb((dentry)->d_sb) +#endif + /* needed by smbfs as well ... move to dcache? */ extern void nfs_renew_times(struct dentry *); -extern void nfs_invalidate_dircache_sb(struct super_block *); + #define NFS_PARANOIA 1 /* @@ -626,7 +630,7 @@ error = -EBUSY; if (dentry->d_count > 1) { /* Attempt to shrink child dentries ... */ - shrink_dcache_sb(dentry->d_sb); /* Arghhh */ + shrink_dcache_parent(dentry); if (dentry->d_count > 1) goto out; } @@ -811,6 +815,14 @@ } error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name); +#ifdef NFS_PARANOIA +if (dentry->d_count > 1) +printk("nfs_safe_remove: %s/%s busy after delete?? d_count=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); +if (inode && inode->i_count > 1) +printk("nfs_safe_remove: %s/%s inode busy?? i_count=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count); +#endif if (!error) { nfs_invalidate_dircache(dir); if (inode && inode->i_nlink) @@ -1005,7 +1017,7 @@ * Moving a directory ... prune child dentries if needed. */ else if (old_dentry->d_count > 1) - shrink_dcache_sb(old_dentry->d_sb); /* Arghhh */ + shrink_dcache_parent(old_dentry); /* * Now check the use counts ... we can't safely do the diff -u --recursive --new-file v2.1.62/linux/include/asm-i386/bugs.h linux/include/asm-i386/bugs.h --- v2.1.62/linux/include/asm-i386/bugs.h Thu Sep 11 09:02:24 1997 +++ linux/include/asm-i386/bugs.h Wed Nov 12 11:09:55 1997 @@ -166,6 +166,32 @@ } } +/* + * All current models of Pentium and Pentium with MMX technology CPUs + * have the F0 0F bug, which lets nonpriviledged users lock up the system: + */ + +extern int pentium_f00f_bug; + +__initfunc(static void check_pentium_f00f(void)) +{ + /* + * Pentium and Pentium MMX + */ + printk("checking for F00F bug ..."); + if(x86==5 && !memcmp(x86_vendor_id, "GenuineIntel", 12)) + { + extern void trap_init_f00f_bug(void); + + printk(KERN_INFO "\nIntel Pentium/[MMX] F0 0F bug detected - turning on workaround.\n"); + pentium_f00f_bug = 1; + trap_init_f00f_bug(); + } else { + printk(KERN_INFO " no F0 0F bug in this CPU, great!\n"); + pentium_f00f_bug = 0; + } +} + __initfunc(static void check_bugs(void)) { check_tlb(); @@ -173,5 +199,6 @@ check_hlt(); check_popad(); check_amd_k6(); + check_pentium_f00f(); system_utsname.machine[1] = '0' + x86; } diff -u --recursive --new-file v2.1.62/linux/include/asm-i386/io.h linux/include/asm-i386/io.h --- v2.1.62/linux/include/asm-i386/io.h Mon Jul 7 16:02:04 1997 +++ linux/include/asm-i386/io.h Mon Nov 10 10:45:53 1997 @@ -25,14 +25,18 @@ * Linus */ + /* + * Bit simplified and optimized by Jan Hubicka + */ + #ifdef SLOW_IO_BY_JUMPING -#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:") +#define __SLOW_DOWN_IO "\njmp 1f\n1:\tjmp 1f\n1:" #else -#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80") +#define __SLOW_DOWN_IO "\noutb %%al,$0x80" #endif #ifdef REALLY_SLOW_IO -#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; } +#define SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO __SLOW_DOWN_IO #else #define SLOW_DOWN_IO __SLOW_DOWN_IO #endif @@ -40,30 +44,25 @@ /* * Talk about misusing macros.. */ - #define __OUT1(s,x) \ -extern inline void __out##s(unsigned x value, unsigned short port) { +extern inline void out##s(unsigned x value, unsigned short port) { #define __OUT2(s,s1,s2) \ __asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1" #define __OUT(s,s1,x) \ -__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \ -__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \ -__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \ -__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; } +__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "Nd" (port)); } \ +__OUT1(s##_p,x) __OUT2(s,s1,"w") SLOW_DOWN_IO : : "a" (value), "Nd" (port));} \ #define __IN1(s) \ -extern inline RETURN_TYPE __in##s(unsigned short port) { RETURN_TYPE _v; +extern inline RETURN_TYPE in##s(unsigned short port) { RETURN_TYPE _v; #define __IN2(s,s1,s2) \ __asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0" #define __IN(s,s1,i...) \ -__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \ -__IN1(s##c) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \ -__IN1(s##_p) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \ -__IN1(s##c_p) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; } +__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ +__IN1(s##_p) __IN2(s,s1,"w") SLOW_DOWN_IO : "=a" (_v) : "Nd" (port) ,##i ); return _v; } \ #define __INS(s) \ extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \ @@ -76,11 +75,9 @@ : "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); } #define RETURN_TYPE unsigned char -/* __IN(b,"b","0" (0)) */ __IN(b,"") #undef RETURN_TYPE #define RETURN_TYPE unsigned short -/* __IN(w,"w","0" (0)) */ __IN(w,"") #undef RETURN_TYPE #define RETURN_TYPE unsigned int @@ -98,71 +95,6 @@ __OUTS(b) __OUTS(w) __OUTS(l) - -/* - * Note that due to the way __builtin_constant_p() works, you - * - can't use it inside an inline function (it will never be true) - * - you don't have to worry about side effects within the __builtin.. - */ -#define outb(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outbc((val),(port)) : \ - __outb((val),(port))) - -#define inb(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inbc(port) : \ - __inb(port)) - -#define outb_p(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outbc_p((val),(port)) : \ - __outb_p((val),(port))) - -#define inb_p(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inbc_p(port) : \ - __inb_p(port)) - -#define outw(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outwc((val),(port)) : \ - __outw((val),(port))) - -#define inw(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inwc(port) : \ - __inw(port)) - -#define outw_p(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outwc_p((val),(port)) : \ - __outw_p((val),(port))) - -#define inw_p(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inwc_p(port) : \ - __inw_p(port)) - -#define outl(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outlc((val),(port)) : \ - __outl((val),(port))) - -#define inl(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inlc(port) : \ - __inl(port)) - -#define outl_p(val,port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __outlc_p((val),(port)) : \ - __outl_p((val),(port))) - -#define inl_p(port) \ -((__builtin_constant_p((port)) && (port) < 256) ? \ - __inlc_p(port) : \ - __inl_p(port)) #ifdef __KERNEL__ diff -u --recursive --new-file v2.1.62/linux/include/linux/awe_voice.h linux/include/linux/awe_voice.h --- v2.1.62/linux/include/linux/awe_voice.h Tue Mar 4 10:25:26 1997 +++ linux/include/linux/awe_voice.h Tue Oct 28 02:10:51 1997 @@ -3,7 +3,7 @@ * * Voice information definitions for the low level driver for the * AWE32/Sound Blaster 32 wave table synth. - * version 0.3.1b; Jan. 21, 1997 + * version 0.4.2; Sep. 1, 1997 * * Copyright (C) 1996,1997 Takashi Iwai * @@ -44,14 +44,19 @@ short device_no; /* synthesizer number */ unsigned short sf_id; /* file id (should be zero) */ - short sf_version; /* patch version (not referred) */ - long len; /* data length (without this header) */ + short optarg; /* optional argument */ + int len; /* data length (without this header) */ - short type; /* following data type */ -#define AWE_LOAD_INFO 0 -#define AWE_LOAD_DATA 1 -#define AWE_APPEND_DATA 0x00 -#define AWE_REPLACE_DATA 0x80 + short type; /* patch operation type */ +#define AWE_LOAD_INFO 0 /* awe_voice_rec */ +#define AWE_LOAD_DATA 1 /* awe_sample_info */ +#define AWE_OPEN_PATCH 2 /* awe_open_parm */ +#define AWE_CLOSE_PATCH 3 /* none */ +#define AWE_UNLOAD_PATCH 4 /* none */ +#define AWE_REPLACE_DATA 5 /* awe_sample_info (optarg=#channels)*/ +#define AWE_MAP_PRESET 6 /* awe_voice_map */ +#define AWE_LOAD_CHORUS_FX 0x10 /* awe_chorus_fx_rec (optarg=mode) */ +#define AWE_LOAD_REVERB_FX 0x11 /* awe_reverb_fx_rec (optarg=mode) */ short reserved; /* word alignment data */ @@ -61,7 +66,35 @@ #endif } awe_patch_info; -#define AWE_PATCH_INFO_SIZE 16 +/*#define AWE_PATCH_INFO_SIZE 16*/ +#define AWE_PATCH_INFO_SIZE sizeof(awe_patch_info) + + +/*---------------------------------------------------------------- + * open patch + *----------------------------------------------------------------*/ + +#define AWE_PATCH_NAME_LEN 32 + +typedef struct _awe_open_parm { + unsigned short type; /* sample type */ +#define AWE_PAT_TYPE_MISC 0 +#define AWE_PAT_TYPE_GM 1 +#define AWE_PAT_TYPE_GS 2 +#define AWE_PAT_TYPE_MT32 3 +#define AWE_PAT_TYPE_XG 4 +#define AWE_PAT_TYPE_SFX 5 +#define AWE_PAT_TYPE_GUS 6 +#define AWE_PAT_TYPE_MAP 7 + +#define AWE_PAT_LOCKED 0x100 /* lock the samples */ + + short reserved; + char name[AWE_PATCH_NAME_LEN]; +} awe_open_parm; + +/*#define AWE_OPEN_PARM_SIZE 28*/ +#define AWE_OPEN_PARM_SIZE sizeof(awe_open_parm) /*---------------------------------------------------------------- @@ -100,8 +133,8 @@ typedef struct _awe_voice_info { unsigned short sf_id; /* file id (should be zero) */ unsigned short sample; /* sample id */ - long start, end; /* sample offset correction */ - long loopstart, loopend; /* loop offset correction */ + int start, end; /* sample offset correction */ + int loopstart, loopend; /* loop offset correction */ short rate_offset; /* sample rate pitch offset */ unsigned short mode; /* sample mode */ #define AWE_MODE_ROMSOUND 0x8000 @@ -124,7 +157,8 @@ short index; /* internal index (set by driver) */ } awe_voice_info; -#define AWE_VOICE_INFO_SIZE 92 +/*#define AWE_VOICE_INFO_SIZE 92*/ +#define AWE_VOICE_INFO_SIZE sizeof(awe_voice_info) /*----------------------------------------------------------------*/ @@ -134,24 +168,44 @@ * from older versions. * Use AWE_VOICE_REC_SIZE instead. */ + +/* instrument info header: 4 bytes */ +typedef struct _awe_voice_rec_hdr { + unsigned char bank; /* midi bank number */ + unsigned char instr; /* midi preset number */ + char nvoices; /* number of voices */ + char write_mode; /* write mode; normally 0 */ +#define AWE_WR_APPEND 0 /* append anyway */ +#define AWE_WR_EXCLUSIVE 1 /* skip if already exists */ +#define AWE_WR_REPLACE 2 /* replace if already exists */ +} awe_voice_rec_hdr; + +/*#define AWE_VOICE_REC_SIZE 4*/ +#define AWE_VOICE_REC_SIZE sizeof(awe_voice_rec_hdr) + +/* the standard patch structure for one sample */ +typedef struct _awe_voice_rec_patch { + awe_patch_info patch; + awe_voice_rec_hdr hdr; + awe_voice_info info; +} awe_voice_rec_patch; + + +/* obsolete data type */ #if defined(AWE_COMPAT_030) && AWE_COMPAT_030 #define AWE_INFOARRAY_SIZE 0 #else #define AWE_INFOARRAY_SIZE 1 #endif -/* instrument info header: 4 bytes */ typedef struct _awe_voice_rec { unsigned char bank; /* midi bank number */ unsigned char instr; /* midi preset number */ short nvoices; /* number of voices */ - /* voice information follows here */ awe_voice_info info[AWE_INFOARRAY_SIZE]; } awe_voice_rec; -#define AWE_VOICE_REC_SIZE 4 - /*---------------------------------------------------------------- * sample wave information @@ -161,9 +215,9 @@ typedef struct awe_sample_info { unsigned short sf_id; /* file id (should be zero) */ unsigned short sample; /* sample id */ - long start, end; /* start & end offset */ - long loopstart, loopend; /* loop start & end offset */ - long size; /* size (0 = ROM) */ + int start, end; /* start & end offset */ + int loopstart, loopend; /* loop start & end offset */ + int size; /* size (0 = ROM) */ short checksum_flag; /* use check sum = 1 */ unsigned short mode_flags; /* mode flags */ #define AWE_SAMPLE_8BITS 1 /* wave data is 8bits */ @@ -174,36 +228,31 @@ #define AWE_SAMPLE_STEREO_LEFT 32 /* stereo left sound */ #define AWE_SAMPLE_STEREO_RIGHT 64 /* stereo right sound */ #define AWE_SAMPLE_REVERSE_LOOP 128 /* reverse looping */ - unsigned long checksum; /* check sum */ + unsigned int checksum; /* check sum */ #if defined(AWE_COMPAT_030) && AWE_COMPAT_030 unsigned short data[0]; /* sample data follows here */ #endif } awe_sample_info; -#define AWE_SAMPLE_INFO_SIZE 32 +/*#define AWE_SAMPLE_INFO_SIZE 32*/ +#define AWE_SAMPLE_INFO_SIZE sizeof(awe_sample_info) /*---------------------------------------------------------------- - * awe hardware controls + * voice preset mapping *----------------------------------------------------------------*/ -typedef struct _awe_mode_rec { - int base_addr; - long mem_size; /* word size */ - int max_voices, max_infos, max_samples; - unsigned short current_sf_id; - long free_mem; /* word offset */ - int free_info; - int free_sample; - short reverb_mode; - short chorus_mode; - unsigned short init_atten; - short channel_mode; - short gus_bank; - short exclusive_sound; - unsigned long drum_flags; - int debug_mode; -} awe_mode_rec; +typedef struct awe_voice_map { + int map_bank, map_instr, map_key; /* key = -1 means all keys */ + int src_bank, src_instr, src_key; +} awe_voice_map; + +#define AWE_VOICE_MAP_SIZE sizeof(awe_voice_map) + + +/*---------------------------------------------------------------- + * awe hardware controls + *----------------------------------------------------------------*/ #define _AWE_DEBUG_MODE 0x00 #define _AWE_REVERB_MODE 0x01 @@ -214,56 +263,116 @@ #define _AWE_TERMINATE_CHANNEL 0x06 #define _AWE_TERMINATE_ALL 0x07 #define _AWE_INITIAL_VOLUME 0x08 -#define _AWE_SET_GUS_BANK 0x09 -#define _AWE_CHANNEL_MODE 0x0a /* v0.3 features */ -#define _AWE_DRUM_CHANNELS 0x0b /* v0.3 features */ -#define _AWE_EXCLUSIVE_SOUND 0x0c /* v0.3 features */ #define _AWE_INITIAL_ATTEN _AWE_INITIAL_VOLUME +#define _AWE_RESET_CHANNEL 0x09 +#define _AWE_CHANNEL_MODE 0x0a +#define _AWE_DRUM_CHANNELS 0x0b +#define _AWE_MISC_MODE 0x0c +#define _AWE_RELEASE_ALL 0x0d #define _AWE_NOTEOFF_ALL 0x0e -#define _AWE_GET_CURRENT_MODE 0x10 /* v0.3 features */ +#define _AWE_CHN_PRESSURE 0x0f +/*#define _AWE_GET_CURRENT_MODE 0x10*/ +#define _AWE_EQUALIZER 0x11 +/*#define _AWE_GET_MISC_MODE 0x12*/ +/*#define _AWE_GET_FONTINFO 0x13*/ #define _AWE_MODE_FLAG 0x80 #define _AWE_COOKED_FLAG 0x40 /* not supported */ #define _AWE_MODE_VALUE_MASK 0x3F -#define _AWE_CMD(chn, voice, cmd, p1, p2) \ -{_SEQ_NEEDBUF(8); _seqbuf[_seqbufptr] = SEQ_PRIVATE;\ - _seqbuf[_seqbufptr+1] = chn;\ - _seqbuf[_seqbufptr+2] = _AWE_MODE_FLAG|(cmd);\ - _seqbuf[_seqbufptr+3] = voice;\ - *(unsigned short*)&_seqbuf[_seqbufptr+4] = p1;\ - *(unsigned short*)&_seqbuf[_seqbufptr+6] = p2;\ +/*----------------------------------------------------------------*/ + +#define _AWE_SET_CMD(p,dev,voice,cmd,p1,p2) \ +{((char*)(p))[0] = SEQ_PRIVATE;\ + ((char*)(p))[1] = dev;\ + ((char*)(p))[2] = _AWE_MODE_FLAG|(cmd);\ + ((char*)(p))[3] = voice;\ + ((unsigned short*)(p))[2] = p1;\ + ((unsigned short*)(p))[3] = p2;} + +/* buffered access */ +#define _AWE_CMD(dev, voice, cmd, p1, p2) \ +{_SEQ_NEEDBUF(8);\ + _AWE_SET_CMD(_seqbuf + _seqbufptr, dev, voice, cmd, p1, p2);\ _SEQ_ADVBUF(8);} +/* direct access */ +#define _AWE_CMD_NOW(seqfd,dev,voice,cmd,p1,p2) \ +{struct seq_event_rec tmp;\ + _AWE_SET_CMD(&tmp, dev, voice, cmd, p1, p2);\ + ioctl(seqfd, SNDCTL_SEQ_OUTOFBAND, &tmp);} + +/*----------------------------------------------------------------*/ + +/* set debugging mode */ #define AWE_DEBUG_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0) +/* set reverb mode; from 0 to 7 */ #define AWE_REVERB_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0) +/* set chorus mode; from 0 to 7 */ #define AWE_CHORUS_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_CHORUS_MODE, p1, 0) -#define AWE_REMOVE_LAST_SAMPLES(dev) _AWE_CMD(dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0) -#define AWE_INITIALIZE_CHIP(dev) _AWE_CMD(dev, 0, _AWE_INITIALIZE_CHIP, 0, 0) + +/* reset channel */ +#define AWE_RESET_CHANNEL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 0, 0) +#define AWE_RESET_CONTROL(dev,ch) _AWE_CMD(dev, ch, _AWE_RESET_CHANNEL, 1, 0) + +/* send an effect to all layers */ #define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value) +#define AWE_ADD_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x80),value) +#define AWE_UNSET_EFFECT(dev,voice,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((type)|0x40),0) +/* send an effect to a layer */ +#define AWE_SEND_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)),value) +#define AWE_ADD_LAYER_EFFECT(dev,voice,layer,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x80),value) +#define AWE_UNSET_LAYER_EFFECT(dev,voice,layer,type) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,((layer+1)<<8|(type)|0x40),0) + +/* terminate sound on the channel/voice */ #define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0) +/* terminate all sounds */ #define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0) +/* release all sounds (w/o sustain effect) */ +#define AWE_RELEASE_ALL(dev) _AWE_CMD(dev, 0, _AWE_RELEASE_ALL, 0, 0) +/* note off all sounds (w sustain effect) */ #define AWE_NOTEOFF_ALL(dev) _AWE_CMD(dev, 0, _AWE_NOTEOFF_ALL, 0, 0) + +/* set initial attenuation */ #define AWE_INITIAL_VOLUME(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 0) #define AWE_INITIAL_ATTEN AWE_INITIAL_VOLUME -#define AWE_SET_GUS_BANK(dev,bank) _AWE_CMD(dev, 0, _AWE_SET_GUS_BANK, bank, 0) -#define AWE_SET_CHANNEL_MODE(dev,mode) _AWE_CMD(dev, 0, _AWE_CHANNEL_MODE, mode, 0) -#define AWE_DRUM_CHANNELS(dev,channels) _AWE_CMD(dev, 0, _AWE_DRUM_CHANNELS, channels, 0) -#define AWE_EXCLUSIVE_SOUND(dev,mode) _AWE_CMD(dev, 0, _AWE_EXCLUSIVE_SOUND, mode, 0) +/* relative attenuation */ +#define AWE_SET_ATTEN(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 1) -/* it must be direct access */ -#define AWE_GET_CURRENT_MODE(dev,addr) \ -{char tmpbuf[8];\ - tmpbuf[0] = SEQ_PRIVATE; tmpbuf[1] = dev;\ - tmpbuf[2] = _AWE_MODE_FLAG|_AWE_GET_CURRENT_MODE;\ - tmpbuf[3] = 0; *(awe_mode_rec**)(tmpbuf +4) = (awe_mode_rec*)(addr);\ - write(seqfd, tmpbuf, 8);} +/* set channel playing mode; mode=0/1/2 */ +#define AWE_SET_CHANNEL_MODE(dev,mode) _AWE_CMD(dev, 0, _AWE_CHANNEL_MODE, mode, 0) +#define AWE_PLAY_INDIRECT 0 /* indirect voice mode (default) */ +#define AWE_PLAY_MULTI 1 /* multi note voice mode */ +#define AWE_PLAY_DIRECT 2 /* direct single voice mode */ +#define AWE_PLAY_MULTI2 3 /* sequencer2 mode; used internally */ + +/* set drum channel mask; channels is 32bit long value */ +#define AWE_DRUM_CHANNELS(dev,channels) _AWE_CMD(dev, 0, _AWE_DRUM_CHANNELS, ((channels) & 0xffff), ((channels) >> 16)) + +/* set bass and treble control; values are from 0 to 11 */ +#define AWE_EQUALIZER(dev,bass,treble) _AWE_CMD(dev, 0, _AWE_EQUALIZER, bass, treble) + +/* remove last loaded samples */ +#define AWE_REMOVE_LAST_SAMPLES(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0) +/* initialize emu8000 chip */ +#define AWE_INITIALIZE_CHIP(seqfd,dev) _AWE_CMD_NOW(seqfd, dev, 0, _AWE_INITIALIZE_CHIP, 0, 0) + +/* set miscellaneous modes; meta command */ +#define AWE_MISC_MODE(dev,mode,value) _AWE_CMD(dev, 0, _AWE_MISC_MODE, mode, value) +/* exclusive sound off; 1=off */ +#define AWE_EXCLUSIVE_SOUND(dev,mode) AWE_MISC_MODE(dev,AWE_MD_EXCLUSIVE_SOUND,mode) +/* default GUS bank number */ +#define AWE_SET_GUS_BANK(dev,bank) AWE_MISC_MODE(dev,AWE_MD_GUS_BANK,bank) +/* change panning position in realtime; 0=don't 1=do */ +#define AWE_REALTIME_PAN(dev,mode) AWE_MISC_MODE(dev,AWE_MD_REALTIME_PAN,mode) /* extended pressure controls; not portable with other sound drivers */ #define AWE_KEY_PRESSURE(dev,ch,note,vel) SEQ_START_NOTE(dev,ch,(note)+128,vel) -#define AWE_CHN_PRESSURE(dev,ch,vel) SEQ_START_NOTE(dev,(ch)+128,0,vel) +#define AWE_CHN_PRESSURE(dev,ch,vel) _AWE_CMD(dev,ch,_AWE_CHN_PRESSURE,vel,0) -/* reverb mode */ +/*----------------------------------------------------------------*/ + +/* reverb mode parameters */ #define AWE_REVERB_ROOM1 0 #define AWE_REVERB_ROOM2 1 #define AWE_REVERB_ROOM3 2 @@ -272,8 +381,17 @@ #define AWE_REVERB_PLATE 5 #define AWE_REVERB_DELAY 6 #define AWE_REVERB_PANNINGDELAY 7 +#define AWE_REVERB_PREDEFINED 8 +/* user can define reverb modes up to 32 */ +#define AWE_REVERB_NUMBERS 32 + +typedef struct awe_reverb_fx_rec { + unsigned short parms[28]; +} awe_reverb_fx_rec; + +/*----------------------------------------------------------------*/ -/* chorus mode */ +/* chorus mode parameters */ #define AWE_CHORUS_1 0 #define AWE_CHORUS_2 1 #define AWE_CHORUS_3 2 @@ -282,8 +400,41 @@ #define AWE_CHORUS_FLANGER 5 #define AWE_CHORUS_SHORTDELAY 6 #define AWE_CHORUS_SHORTDELAY2 7 +#define AWE_CHORUS_PREDEFINED 8 +/* user can define chorus modes up to 32 */ +#define AWE_CHORUS_NUMBERS 32 + +typedef struct awe_chorus_fx_rec { + unsigned short feedback; /* feedback level (0xE600-0xE6FF) */ + unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */ + unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */ + unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */ + unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */ +} awe_chorus_fx_rec; -/* effects */ +/*----------------------------------------------------------------*/ + +/* misc mode types */ +enum { +/* 0*/ AWE_MD_EXCLUSIVE_OFF, /* obsolete */ +/* 1*/ AWE_MD_EXCLUSIVE_ON, /* obsolete */ +/* 2*/ AWE_MD_VERSION, /* read only */ +/* 3*/ AWE_MD_EXCLUSIVE_SOUND, /* 0/1: (default=1) */ +/* 4*/ AWE_MD_REALTIME_PAN, /* 0/1: do realtime pan change (default=1) */ +/* 5*/ AWE_MD_GUS_BANK, /* bank number (default=0) */ +/* 6*/ AWE_MD_KEEP_EFFECT, /* 0/1: keep effect values, (default=0) */ +/* 7*/ AWE_MD_ZERO_ATTEN, /* attenuation of max volume (default=32) */ +/* 8*/ AWE_MD_CHN_PRIOR, /* 0/1: set MIDI channel priority mode (default=1) */ +/* 9*/ AWE_MD_MOD_SENSE, /* integer: modwheel sensitivity */ +/*10*/ AWE_MD_DEF_PRESET, /* integer: default preset number */ +/*11*/ AWE_MD_DEF_BANK, /* integer: default bank number */ +/*12*/ AWE_MD_DEF_DRUM, /* integer: default drumset number */ + AWE_MD_END, +}; + +/*----------------------------------------------------------------*/ + +/* effect parameters */ enum { /* modulation envelope parameters */ @@ -330,9 +481,9 @@ /*30*/ AWE_FX_COARSE_SAMPLE_START, /* SHORT: upper word offset */ /*31*/ AWE_FX_COARSE_LOOP_START, /* SHORT: upper word offset */ /*32*/ AWE_FX_COARSE_LOOP_END, /* SHORT: upper word offset */ +/*33*/ AWE_FX_ATTEN, /* BYTE: lo IFATN */ AWE_FX_END, }; - #endif /* AWE_VOICE_H */ diff -u --recursive --new-file v2.1.62/linux/include/linux/blk.h linux/include/linux/blk.h --- v2.1.62/linux/include/linux/blk.h Mon Aug 18 18:19:47 1997 +++ linux/include/linux/blk.h Sat Nov 8 11:39:12 1997 @@ -461,8 +461,7 @@ if ((bh = req->bh) != NULL) { req->bh = bh->b_reqnext; bh->b_reqnext = NULL; - mark_buffer_uptodate(bh, uptodate); - unlock_buffer(bh); + bh->b_end_io(bh, uptodate); if ((bh = req->bh) != NULL) { req->current_nr_sectors = bh->b_size >> 9; if (req->nr_sectors < req->current_nr_sectors) { diff -u --recursive --new-file v2.1.62/linux/include/linux/blkdev.h linux/include/linux/blkdev.h --- v2.1.62/linux/include/linux/blkdev.h Mon Jul 7 16:02:03 1997 +++ linux/include/linux/blkdev.h Sat Nov 8 11:39:12 1997 @@ -55,14 +55,19 @@ extern struct wait_queue * wait_for_request; extern void resetup_one_dev(struct gendisk *dev, int drive); extern void unplug_device(void * data); +extern void make_request(int major,int rw, struct buffer_head * bh); /* md needs this function to remap requests */ extern int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size); +extern int md_make_request (int minor, int rw, struct buffer_head * bh); +extern int md_error (kdev_t mddev, kdev_t rdev); extern int * blk_size[MAX_BLKDEV]; extern int * blksize_size[MAX_BLKDEV]; extern int * hardsect_size[MAX_BLKDEV]; + +extern int * max_readahead[MAX_BLKDEV]; #endif diff -u --recursive --new-file v2.1.62/linux/include/linux/cyclades.h linux/include/linux/cyclades.h --- v2.1.62/linux/include/linux/cyclades.h Thu Jul 17 10:06:08 1997 +++ linux/include/linux/cyclades.h Tue Nov 11 11:04:15 1997 @@ -1,4 +1,4 @@ -/* $Revision: 2.0 $$Date: 1997/06/30 10:30:00 $ +/* $Revision: 2.1 $$Date: 1997/10/24 16:03:00 $ * linux/include/linux/cyclades.h * * This file is maintained by Marcio Saito and @@ -6,6 +6,12 @@ * * This file contains the general definitions for the cyclades.c driver *$Log: cyclades.h,v $ + *Revision 2.1 1997/10/24 16:03:00 ivan + *added rflow (which allows enabling the CD1400 special flow control + *feature) and rtsdtr_inv (which allows DTR/RTS pin inversion) to + *cyclades_port structure; + *added Alpha support + * *Revision 2.0 1997/06/30 10:30:00 ivan *added some new doorbell command constants related to IOCTLW and *UART error signaling @@ -51,6 +57,11 @@ #define CYSETTIMEOUT 0x435907 #define CYGETDEFTIMEOUT 0x435908 #define CYSETDEFTIMEOUT 0x435909 +#define CYSETRFLOW 0x43590a +#define CYRESETRFLOW 0x43590b +#define CYSETRTSDTR_INV 0x43590c +#define CYRESETRTSDTR_INV 0x43590d +#define CYZPOLLCYCLE 0x43590e /*************** CYCLOM-Z ADDITIONS ***************/ @@ -61,6 +72,8 @@ #define CZ_BOOT_END (CZIOC|0xfd) #define CZ_TEST (CZIOC|0xfe) +#define CZ_DEF_POLL (HZ/25) + #define MAX_BOARD 4 /* Max number of boards */ #define MAX_PORT 128 /* Max number of ports per board */ #define MAX_DEV 256 /* Max number of ports total */ @@ -85,7 +98,12 @@ * architectures and compilers. */ +#if defined(__alpha__) +typedef unsigned long ucdouble; /* 64 bits, unsigned */ +typedef unsigned int uclong; /* 32 bits, unsigned */ +#else typedef unsigned long uclong; /* 32 bits, unsigned */ +#endif typedef unsigned short ucshort; /* 16 bits, unsigned */ typedef unsigned char ucchar; /* 8 bits, unsigned */ @@ -96,7 +114,7 @@ #define DP_WINDOW_SIZE (0x00080000) /* window size 512 Kb */ #define ZE_DP_WINDOW_SIZE (0x00100000) /* window size 1 Mb (Ze and 8Zo V.2 */ -#define CTRL_WINDOW_SIZE (0x00000100) /* runtime regs 256 bytes */ +#define CTRL_WINDOW_SIZE (0x00000080) /* runtime regs 128 bytes */ /* * CUSTOM_REG - Cyclom-Z/PCI Custom Registers Set. The driver @@ -194,6 +212,12 @@ #define ID_ADDRESS 0x00000180L /* signature/pointer address */ #define ZFIRM_ID 0x5557465AL /* ZFIRM/U signature */ #define ZFIRM_HLT 0x59505B5CL /* ZFIRM needs external power supply */ +#define ZFIRM_RST 0x56040674L /* RST signal (due to FW reset) */ + +#define ZF_TINACT_DEF 1000 /* default inactivity timeout + (1000 ms) */ +#define ZF_TINACT ZF_TINACT_DEF + struct FIRM_ID { uclong signature; /* ZFIRM/U signature */ uclong zfwctrl_addr; /* pointer to ZFW_CTRL structure */ @@ -334,7 +358,9 @@ uclong rs_status; /* RS-232 inputs */ uclong flow_xon; /* xon char */ uclong flow_xoff; /* xoff char */ - uclong filler[3]; /* filler to align structures */ + uclong hw_overflow; /* hw overflow counter */ + uclong sw_overflow; /* sw overflow counter */ + uclong comm_error; /* frame/parity error counter */ }; @@ -378,11 +404,11 @@ /* host to FW commands */ uclong hcmd_channel; /* channel number */ - uclong *hcmd_param; /* pointer to parameters */ + uclong hcmd_param; /* pointer to parameters */ /* FW to Host commands */ uclong fwcmd_channel; /* channel number */ - uclong *fwcmd_param; /* pointer to parameters */ + uclong fwcmd_param; /* pointer to parameters */ /* filler so the structures are aligned */ uclong filler[7]; @@ -404,8 +430,22 @@ + #ifdef __KERNEL__ +/*************************************** + * Memory access functions/macros * + * (required to support Alpha systems) * + ***************************************/ + +#define cy_writeb(port,val) {writeb((ucchar)(val),(ulong)(port)); mb();} +#define cy_writew(port,val) {writew((ushort)(val),(ulong)(port)); mb();} +#define cy_writel(port,val) {writel((uclong)(val),(ulong)(port)); mb();} + +#define cy_readb(port) readb(port) +#define cy_readw(port) readw(port) +#define cy_readl(port) readl(port) + /* Per card data structure */ struct cyclades_card { @@ -415,6 +455,7 @@ int num_chips; /* 0 if card absent, 1 if Z/PCI, else Y */ int first_line; /* minor number of first channel on card */ int bus_index; /* address shift - 0 for ISA, 1 for PCI */ + int inact_ctrl; /* FW Inactivity control - 0 disabled, 1 enabled */ }; struct cyclades_chip { @@ -443,6 +484,8 @@ int cor1,cor2,cor3,cor4,cor5; int tbpr,tco,rbpr,rco; int baud; + int rflow; + int rtsdtr_inv; int ignore_status_mask; int close_delay; int IER; /* Interrupt Enable Register */ @@ -482,10 +525,14 @@ -#define CyMaxChipsPerCard 8 +#define CyMAX_CHIPS_PER_CARD 8 +#define CyMAX_CHAR_FIFO 12 +#define CyPORTS_PER_CHIP 4 + +#define CyISA_Ywin 0x2000 #define CyPCI_Ywin 0x4000 -#define CyPCI_Zctl 0x100 +#define CyPCI_Zctl CTRL_WINDOW_SIZE #define CyPCI_Zwin 0x80000 #define CyPCI_Ze_win (2 * CyPCI_Zwin) @@ -530,6 +577,9 @@ #define CyPPR (0x7E*2) #define CyCLOCK_20_1MS (0x27) #define CyCLOCK_25_1MS (0x31) +#define CyCLOCK_25_5MS (0xf4) +#define CyCLOCK_60_1MS (0x75) +#define CyCLOCK_60_2MS (0xea) /* Virtual Registers */ @@ -652,12 +702,7 @@ #define CyTBPR (0x72*2) #define CyTCOR (0x76*2) -/* max number of chars in the FIFO */ - -#define CyMAX_CHAR_FIFO 12 - /***************************************************************************/ #endif /* __KERNEL__ */ #endif /* _LINUX_CYCLADES_H */ - diff -u --recursive --new-file v2.1.62/linux/include/linux/dcache.h linux/include/linux/dcache.h --- v2.1.62/linux/include/linux/dcache.h Sat Nov 1 11:04:27 1997 +++ linux/include/linux/dcache.h Tue Nov 4 10:26:38 1997 @@ -57,10 +57,13 @@ struct dentry * d_covers; struct list_head d_hash; /* lookup hash list */ struct list_head d_lru; /* d_count = 0 LRU list */ + struct list_head d_child; /* child of parent list */ + struct list_head d_subdirs; /* our children */ struct qstr d_name; unsigned long d_time; /* used by d_revalidate */ struct dentry_operations *d_op; struct super_block * d_sb; /* The root of the dentry tree */ + unsigned long d_reftime; /* last time referenced */ }; struct dentry_operations { @@ -115,6 +118,7 @@ extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name); extern void prune_dcache(int); extern void shrink_dcache_sb(struct super_block *); +extern void shrink_dcache_parent(struct dentry *); extern int d_invalidate(struct dentry *); #define shrink_dcache() prune_dcache(0) diff -u --recursive --new-file v2.1.62/linux/include/linux/fs.h linux/include/linux/fs.h --- v2.1.62/linux/include/linux/fs.h Sat Oct 25 02:44:18 1997 +++ linux/include/linux/fs.h Sat Nov 8 11:39:13 1997 @@ -164,7 +164,6 @@ #define BH_Touched 4 /* 1 if the buffer has been touched (aging) */ #define BH_Has_aged 5 /* 1 if the buffer has been aged (aging) */ #define BH_Protected 6 /* 1 if the buffer is protected */ -#define BH_FreeOnIO 7 /* 1 to discard the buffer_head after IO */ /* * Try to keep the most commonly used fields in single cache lines (16 @@ -202,8 +201,18 @@ struct buffer_head ** b_pprev; /* doubly linked list of hash-queue */ struct buffer_head * b_prev_free; /* doubly linked list of buffers */ struct buffer_head * b_reqnext; /* request queue */ + + /* + * I/O completion + */ + void (*b_end_io)(struct buffer_head *bh, int uptodate); + void *b_dev_id; }; +typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate); +void init_buffer(struct buffer_head *bh, kdev_t dev, int block, + bh_end_io_t *handler, void *dev_id); + static inline int buffer_uptodate(struct buffer_head * bh) { return test_bit(BH_Uptodate, &bh->b_state); @@ -756,6 +765,7 @@ extern int close_fp(struct file *); extern struct buffer_head * get_hash_table(kdev_t, int, int); extern struct buffer_head * getblk(kdev_t, int, int); +extern struct buffer_head *efind_buffer(kdev_t dev, int block, int size); extern void ll_rw_block(int, int, struct buffer_head * bh[]); extern void ll_rw_page(int, kdev_t, unsigned long, char *); extern void ll_rw_swap_file(int, kdev_t, unsigned int *, int, char *); diff -u --recursive --new-file v2.1.62/linux/include/linux/ipx.h linux/include/linux/ipx.h --- v2.1.62/linux/include/linux/ipx.h Mon Jun 30 15:23:58 1997 +++ linux/include/linux/ipx.h Sun Nov 9 10:13:48 1997 @@ -7,8 +7,8 @@ struct sockaddr_ipx { - sa_family_t sipx_family; - __u16 sipx_port; + sa_family_t sipx_family; + __u16 sipx_port; __u32 sipx_network; unsigned char sipx_node[IPX_NODE_LEN]; __u8 sipx_type; @@ -26,14 +26,14 @@ typedef struct ipx_route_definition { - unsigned long ipx_network; - unsigned long ipx_router_network; + __u32 ipx_network; + __u32 ipx_router_network; unsigned char ipx_router_node[IPX_NODE_LEN]; } ipx_route_definition; typedef struct ipx_interface_definition { - unsigned long ipx_network; + __u32 ipx_network; unsigned char ipx_device[16]; unsigned char ipx_dlink_type; #define IPX_FRAME_NONE 0 @@ -61,8 +61,8 @@ struct ipx_route_def { - unsigned long ipx_network; - unsigned long ipx_router_network; + __u32 ipx_network; + __u32 ipx_router_network; #define IPX_ROUTE_NO_ROUTER 0 unsigned char ipx_router_node[IPX_NODE_LEN]; unsigned char ipx_device[16]; diff -u --recursive --new-file v2.1.62/linux/include/linux/locks.h linux/include/linux/locks.h --- v2.1.62/linux/include/linux/locks.h Mon Jul 7 16:02:11 1997 +++ linux/include/linux/locks.h Sat Nov 8 11:39:13 1997 @@ -26,8 +26,11 @@ __wait_on_buffer(bh); } -void unlock_buffer(struct buffer_head *); - +extern inline void unlock_buffer(struct buffer_head *bh) +{ + clear_bit(BH_Lock, &bh->b_state); + wake_up(&bh->b_wait); +} /* * super-block locking. Again, interrupts may only unlock diff -u --recursive --new-file v2.1.62/linux/include/linux/md.h linux/include/linux/md.h --- v2.1.62/linux/include/linux/md.h Tue Oct 29 17:06:23 1996 +++ linux/include/linux/md.h Sat Nov 8 11:39:13 1997 @@ -1,4 +1,3 @@ - /* md.h : Multiple Devices driver for Linux Copyright (C) 1994-96 Marc ZYNGIER @@ -19,10 +18,19 @@ #define _MD_H #include -#include #include +#include + +/* + * Different major versions are not compatible. + * Different minor versions are only downward compatible. + * Different patchlevel versions are downward and upward compatible. + */ +#define MD_MAJOR_VERSION 0 +#define MD_MINOR_VERSION 36 +#define MD_PATCHLEVEL_VERSION 6 -#define MD_VERSION "0.35" +#define MD_DEFAULT_DISK_READAHEAD (256 * 1024) /* ioctls */ #define REGISTER_DEV _IO (MD_MAJOR, 1) @@ -42,9 +50,9 @@ #define FAULT_SHIFT 8 #define PERSONALITY_SHIFT 16 -#define FACTOR_MASK 0xFFUL -#define FAULT_MASK 0xFF00UL -#define PERSONALITY_MASK 0xFF0000UL +#define FACTOR_MASK 0x000000FFUL +#define FAULT_MASK 0x0000FF00UL +#define PERSONALITY_MASK 0x00FF0000UL #define MD_RESERVED 0 /* Not used by now */ #define LINEAR (1UL << PERSONALITY_SHIFT) @@ -54,15 +62,159 @@ #define RAID5 (4UL << PERSONALITY_SHIFT) #define MAX_PERSONALITY 5 +/* + * MD superblock. + * + * The MD superblock maintains some statistics on each MD configuration. + * Each real device in the MD set contains it near the end of the device. + * Some of the ideas are copied from the ext2fs implementation. + * + * We currently use 4096 bytes as follows: + * + * word offset function + * + * 0 - 31 Constant generic MD device information. + * 32 - 63 Generic state information. + * 64 - 127 Personality specific information. + * 128 - 511 12 32-words descriptors of the disks in the raid set. + * 512 - 911 Reserved. + * 912 - 1023 Disk specific descriptor. + */ + +/* + * If x is the real device size in bytes, we return an apparent size of: + * + * y = (x & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES + * + * and place the 4kB superblock at offset y. + */ +#define MD_RESERVED_BYTES (64 * 1024) +#define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512) +#define MD_RESERVED_BLOCKS (MD_RESERVED_BYTES / BLOCK_SIZE) + +#define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS) +#define MD_NEW_SIZE_BLOCKS(x) ((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS) + +#define MD_SB_BYTES 4096 +#define MD_SB_WORDS (MD_SB_BYTES / 4) +#define MD_SB_BLOCKS (MD_SB_BYTES / BLOCK_SIZE) +#define MD_SB_SECTORS (MD_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define MD_SB_GENERIC_OFFSET 0 +#define MD_SB_PERSONALITY_OFFSET 64 +#define MD_SB_DISKS_OFFSET 128 +#define MD_SB_DESCRIPTOR_OFFSET 992 + +#define MD_SB_GENERIC_CONSTANT_WORDS 32 +#define MD_SB_GENERIC_STATE_WORDS 32 +#define MD_SB_GENERIC_WORDS (MD_SB_GENERIC_CONSTANT_WORDS + MD_SB_GENERIC_STATE_WORDS) +#define MD_SB_PERSONALITY_WORDS 64 +#define MD_SB_DISKS_WORDS 384 +#define MD_SB_DESCRIPTOR_WORDS 32 +#define MD_SB_RESERVED_WORDS (1024 - MD_SB_GENERIC_WORDS - MD_SB_PERSONALITY_WORDS - MD_SB_DISKS_WORDS - MD_SB_DESCRIPTOR_WORDS) +#define MD_SB_EQUAL_WORDS (MD_SB_GENERIC_WORDS + MD_SB_PERSONALITY_WORDS + MD_SB_DISKS_WORDS) +#define MD_SB_DISKS (MD_SB_DISKS_WORDS / MD_SB_DESCRIPTOR_WORDS) + +/* + * Device "operational" state bits + */ +#define MD_FAULTY_DEVICE 0 /* Device is faulty / operational */ +#define MD_ACTIVE_DEVICE 1 /* Device is a part or the raid set / spare disk */ +#define MD_SYNC_DEVICE 2 /* Device is in sync with the raid set */ + +typedef struct md_device_descriptor_s { + __u32 number; /* 0 Device number in the entire set */ + __u32 major; /* 1 Device major number */ + __u32 minor; /* 2 Device minor number */ + __u32 raid_disk; /* 3 The role of the device in the raid set */ + __u32 state; /* 4 Operational state */ + __u32 reserved[MD_SB_DESCRIPTOR_WORDS - 5]; +} md_descriptor_t; + +#define MD_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define MD_SB_CLEAN 0 +#define MD_SB_ERRORS 1 + +typedef struct md_superblock_s { + + /* + * Constant generic information + */ + __u32 md_magic; /* 0 MD identifier */ + __u32 major_version; /* 1 major version to which the set conforms */ + __u32 minor_version; /* 2 minor version to which the set conforms */ + __u32 patch_version; /* 3 patchlevel version to which the set conforms */ + __u32 gvalid_words; /* 4 Number of non-reserved words in this section */ + __u32 set_magic; /* 5 Raid set identifier */ + __u32 ctime; /* 6 Creation time */ + __u32 level; /* 7 Raid personality (mirroring, raid5, ...) */ + __u32 size; /* 8 Apparent size of each individual disk, in kB */ + __u32 nr_disks; /* 9 Number of total disks in the raid set */ + __u32 raid_disks; /* 10 Number of disks in a fully functional raid set */ + __u32 gstate_creserved[MD_SB_GENERIC_CONSTANT_WORDS - 11]; + + /* + * Generic state information + */ + __u32 utime; /* 0 Superblock update time */ + __u32 state; /* 1 State bits (clean, ...) */ + __u32 active_disks; /* 2 Number of currently active disks (some non-faulty disks might not be in sync) */ + __u32 working_disks; /* 3 Number of working disks */ + __u32 failed_disks; /* 4 Number of failed disks */ + __u32 spare_disks; /* 5 Number of spare disks */ + __u32 gstate_sreserved[MD_SB_GENERIC_STATE_WORDS - 6]; + + /* + * Personality information + */ + __u32 parity_algorithm; + __u32 chunk_size; + __u32 pstate_reserved[MD_SB_PERSONALITY_WORDS - 2]; + + /* + * Disks information + */ + md_descriptor_t disks[MD_SB_DISKS]; + + /* + * Reserved + */ + __u32 reserved[MD_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + md_descriptor_t descriptor; +} md_superblock_t; + #ifdef __KERNEL__ -#include +#include #include #include +/* + * Kernel-based reconstruction is mostly working, but still requires + * some additional work. + */ +#define SUPPORT_RECONSTRUCTION 0 + #define MAX_REAL 8 /* Max number of physical dev per md dev */ #define MAX_MD_DEV 4 /* Max number of md dev */ +#if SUPPORT_RECONSTRUCTION +#define MAX_MD_THREADS (MAX_MD_DEV * 3) /* Max number of kernel threads */ +#else +#define MAX_MD_THREADS (MAX_MD_DEV) /* Max number of kernel threads */ +#endif /* SUPPORT_RECONSTRUCTION */ + #define FACTOR(a) ((a)->repartition & FACTOR_MASK) #define MAX_FAULT(a) (((a)->repartition & FAULT_MASK)>>8) #define PERSONALITY(a) ((a)->repartition & PERSONALITY_MASK) @@ -76,40 +228,75 @@ int offset; /* Real device offset (in blocks) in md dev (only used in linear mode) */ struct inode *inode; /* Lock inode */ + md_superblock_t *sb; + u32 sb_offset; }; struct md_dev; +#define SPARE_INACTIVE 0 +#define SPARE_WRITE 1 +#define SPARE_ACTIVE 2 + struct md_personality { char *name; - int (*map)(struct md_dev *md_dev, kdev_t *rdev, - unsigned long *rsector, unsigned long size); - int (*run)(int minor, struct md_dev *md_dev); - int (*stop)(int minor, struct md_dev *md_dev); - int (*status)(char *page, int minor, struct md_dev *md_dev); + int (*map)(struct md_dev *mddev, kdev_t *rdev, + unsigned long *rsector, unsigned long size); + int (*make_request)(struct md_dev *mddev, int rw, struct buffer_head * bh); + void (*end_request)(struct buffer_head * bh, int uptodate); + int (*run)(int minor, struct md_dev *mddev); + int (*stop)(int minor, struct md_dev *mddev); + int (*status)(char *page, int minor, struct md_dev *mddev); int (*ioctl)(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); int max_invalid_dev; + int (*error_handler)(struct md_dev *mddev, kdev_t dev); + +/* + * Some personalities (RAID-1, RAID-5) can get disks hot-added and + * hot-removed. Hot removal is different from failure. (failure marks + * a disk inactive, but the disk is still part of the array) + */ + int (*hot_add_disk) (struct md_dev *mddev, kdev_t dev); + int (*hot_remove_disk) (struct md_dev *mddev, kdev_t dev); + int (*mark_spare) (struct md_dev *mddev, md_descriptor_t *descriptor, int state); }; struct md_dev { - struct real_dev devices[MAX_REAL]; - struct md_personality *pers; - int repartition; - int busy; - int nb_dev; - void *private; + struct real_dev devices[MAX_REAL]; + struct md_personality *pers; + md_superblock_t *sb; + int sb_dirty; + int repartition; + int busy; + int nb_dev; + void *private; +}; + +struct md_thread { + void (*run) (void *data); + void *data; + struct wait_queue *wqueue; + __u32 flags; }; +#define THREAD_WAKEUP 0 + extern struct md_dev md_dev[MAX_MD_DEV]; extern int md_size[MAX_MD_DEV]; +extern int md_maxreadahead[MAX_MD_DEV]; extern char *partition_name (kdev_t dev); extern int register_md_personality (int p_num, struct md_personality *p); extern int unregister_md_personality (int p_num); +extern struct md_thread *md_register_thread (void (*run) (void *data), void *data); +extern void md_unregister_thread (struct md_thread *thread); +extern void md_wakeup_thread(struct md_thread *thread); +extern int md_update_sb (int minor); +extern int md_do_sync(struct md_dev *mddev); #endif __KERNEL__ #endif _MD_H diff -u --recursive --new-file v2.1.62/linux/include/linux/miscdevice.h linux/include/linux/miscdevice.h --- v2.1.62/linux/include/linux/miscdevice.h Mon Aug 4 16:25:40 1997 +++ linux/include/linux/miscdevice.h Tue Nov 4 10:23:25 1997 @@ -9,6 +9,7 @@ #define ATARIMOUSE_MINOR 5 #define SUN_MOUSE_MINOR 6 #define PC110PAD_MINOR 9 +#define RADIO_MINOR 129 #define RTC_MINOR 135 #define SUN_OPENPROM_MINOR 139 #define NVRAM_MINOR 144 diff -u --recursive --new-file v2.1.62/linux/include/linux/ncp_fs.h linux/include/linux/ncp_fs.h --- v2.1.62/linux/include/linux/ncp_fs.h Tue Sep 23 16:48:50 1997 +++ linux/include/linux/ncp_fs.h Wed Nov 12 11:10:40 1997 @@ -55,6 +55,20 @@ #ifdef __KERNEL__ +#undef NCPFS_PARANOIA +#define DEBUG_NCP 0 +#if DEBUG_NCP > 0 +#define DPRINTK(format, args...) printk(format , ## args) +#else +#define DPRINTK(format, args...) +#endif + +#if DEBUG_NCP > 1 +#define DDPRINTK(format, args...) printk(format , ## args) +#else +#define DDPRINTK(format, args...) +#endif + /* The readdir cache size controls how many directory entries are * cached. */ @@ -62,16 +76,40 @@ #define NCP_MAX_RPC_TIMEOUT (6*HZ) +/* + * This is the ncpfs part of the inode structure. This must contain + * all the information we need to work with an inode after creation. + * (Move to ncp_fs_i.h once it stabilizes, and add a union in fs.h) + */ +struct ncpfs_i { + __u32 dirEntNum __attribute__((packed)); + __u32 DosDirNum __attribute__((packed)); + __u32 volNumber __attribute__((packed)); + int opened; + int access; + __u32 server_file_handle __attribute__((packed)); + __u8 open_create_action __attribute__((packed)); + __u8 file_handle[6] __attribute__((packed)); +}; + +/* + * This is an extension of the nw_file_info structure with + * the additional information we need to create an inode. + */ +struct ncpfs_inode_info { + ino_t ino; /* dummy inode number */ + struct nw_file_info nw_info; +}; + /* Guess, what 0x564c is :-) */ #define NCP_SUPER_MAGIC 0x564c #define NCP_SBP(sb) ((struct ncp_server *)((sb)->u.generic_sbp)) -#define NCP_INOP(inode) ((struct ncp_inode_info *)((inode)->u.generic_ip)) #define NCP_SERVER(inode) NCP_SBP((inode)->i_sb) -#define NCP_FINFO(inode) (&(NCP_INOP(inode)->finfo)) -#define NCP_ISTRUCT(inode) (&(NCP_FINFO(inode)->i)) +/* We don't have an ncpfs union yet, so use smbfs ... */ +#define NCP_FINFO(inode) ((struct ncpfs_i *)&((inode)->u.smbfs_i)) #ifdef DEBUG_NCP_MALLOC @@ -101,46 +139,23 @@ #endif /* DEBUG_NCP_MALLOC */ -#if DEBUG_NCP > 0 -#define DPRINTK(format, args...) printk(format , ## args) -#else -#define DPRINTK(format, args...) -#endif - -#if DEBUG_NCP > 1 -#define DDPRINTK(format, args...) printk(format , ## args) -#else -#define DDPRINTK(format, args...) -#endif - - -/* linux/fs/ncpfs/file.c */ -extern struct inode_operations ncp_file_inode_operations; -int ncp_make_open(struct inode *i, int right); +/* linux/fs/ncpfs/inode.c */ +struct super_block *ncp_read_super(struct super_block *, void *, int); +struct inode *ncp_iget(struct super_block *, struct ncpfs_inode_info *); +void ncp_update_inode(struct inode *, struct nw_file_info *); +extern int init_ncp_fs(void); /* linux/fs/ncpfs/dir.c */ extern struct inode_operations ncp_dir_inode_operations; -void ncp_free_inode_info(struct ncp_inode_info *i); -void ncp_free_all_inodes(struct ncp_server *server); -void ncp_init_root(struct ncp_server *server); -int ncp_conn_logged_in(struct ncp_server *server); +int ncp_conn_logged_in(struct ncp_server *); void ncp_init_dir_cache(void); -void ncp_invalid_dir_cache(struct inode *ino); -struct ncp_inode_info *ncp_find_inode(struct inode *inode); -ino_t ncp_info_ino(struct ncp_server *server, struct ncp_inode_info *info); +void ncp_invalid_dir_cache(struct inode *); void ncp_free_dir_cache(void); int ncp_date_dos2unix(__u16 time, __u16 date); void ncp_date_unix2dos(int unix_date, __u16 * time, __u16 * date); - /* linux/fs/ncpfs/ioctl.c */ -int ncp_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); - -/* linux/fs/ncpfs/inode.c */ -struct super_block *ncp_read_super(struct super_block *sb, - void *raw_data, int silent); -extern int init_ncp_fs(void); +int ncp_ioctl(struct inode *, struct file *, unsigned int, unsigned long); /* linux/fs/ncpfs/sock.c */ int ncp_request(struct ncp_server *server, int function); @@ -149,8 +164,50 @@ void ncp_lock_server(struct ncp_server *server); void ncp_unlock_server(struct ncp_server *server); +/* linux/fs/ncpfs/file.c */ +extern struct inode_operations ncp_file_inode_operations; +int ncp_make_open(struct inode *, int); + /* linux/fs/ncpfs/mmap.c */ -int ncp_mmap(struct file *file, struct vm_area_struct *vma); +int ncp_mmap(struct file *, struct vm_area_struct *); + +/* linux/fs/ncpfs/ncplib_kernel.c */ +int ncp_make_closed(struct inode *); + +static inline void str_upper(char *name) +{ + while (*name) { + if (*name >= 'a' && *name <= 'z') { + *name -= ('a' - 'A'); + } + name++; + } +} + +static inline void str_lower(char *name) +{ + while (*name) { + if (*name >= 'A' && *name <= 'Z') { + *name += ('a' - 'A'); + } + name++; + } +} + +static inline int ncp_namespace(struct inode *inode) +{ + struct ncp_server *server = NCP_SERVER(inode); + return server->name_space[NCP_FINFO(inode)->volNumber]; +} + +static inline int ncp_preserve_case(struct inode *i) +{ + /* If we can get case-sensitive server lookups working, then + * + * return (ncp_namespace(i) == NW_NS_OS2); + */ + return 0; +} #endif /* __KERNEL__ */ diff -u --recursive --new-file v2.1.62/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h --- v2.1.62/linux/include/linux/nfs_fs.h Sat Nov 1 11:04:27 1997 +++ linux/include/linux/nfs_fs.h Sat Nov 8 11:33:05 1997 @@ -133,12 +133,11 @@ /* * linux/fs/nfs/inode.c */ -extern struct super_block *nfs_read_super(struct super_block *sb, - void *data,int); +extern struct super_block *nfs_read_super(struct super_block *, void *, int); extern int init_nfs_fs(void); -extern struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, - struct nfs_fattr *fattr); -extern int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr); +extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *, + struct nfs_fattr *); +extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_revalidate(struct inode *); extern int _nfs_revalidate_inode(struct nfs_server *, struct inode *); @@ -151,20 +150,14 @@ * linux/fs/nfs/dir.c */ extern struct inode_operations nfs_dir_inode_operations; -extern void nfs_sillyrename_cleanup(struct inode *); extern void nfs_free_dircache(void); extern void nfs_invalidate_dircache(struct inode *); +extern void nfs_invalidate_dircache_sb(struct super_block *); /* * linux/fs/nfs/symlink.c */ extern struct inode_operations nfs_symlink_inode_operations; - -/* - * linux/fs/nfs/mmap.c - */ -extern int nfs_mmap(struct inode *inode, struct file *file, - struct vm_area_struct * vma); /* * linux/fs/nfs/locks.c diff -u --recursive --new-file v2.1.62/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h --- v2.1.62/linux/include/linux/proc_fs.h Wed Oct 15 16:04:24 1997 +++ linux/include/linux/proc_fs.h Wed Nov 5 13:15:47 1997 @@ -147,6 +147,9 @@ enum scsi_directory_inos { PROC_SCSI_SCSI = 256, PROC_SCSI_ADVANSYS, + PROC_SCSI_PCI2000, + PROC_SCSI_PCI2220I, + PROC_SCSI_PSI240I, PROC_SCSI_EATA, PROC_SCSI_EATA_PIO, PROC_SCSI_AHA152X, @@ -156,6 +159,7 @@ PROC_SCSI_BUSLOGIC, PROC_SCSI_U14_34F, PROC_SCSI_FDOMAIN, + PROC_SCSI_GDTH, PROC_SCSI_GENERIC_NCR5380, PROC_SCSI_IN2000, PROC_SCSI_PAS16, diff -u --recursive --new-file v2.1.62/linux/include/linux/radio.h linux/include/linux/radio.h --- v2.1.62/linux/include/linux/radio.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/radio.h Tue Nov 4 10:23:25 1997 @@ -0,0 +1,134 @@ +/* + * linux/radio.h + * + * Include for radio card support under linux + * Another pointless suid-binary removal utility... :-) + */ + +#ifndef _LINUX_RADIO_H +#define _LINUX_RADIO_H + +#include + +/* + * Constants + */ +/* Various types of Radio card... */ +/* (NB. I've made this a bit-field. It might make the difference one day.) */ +#define RADIO_TYPE_UNSUP 0x0000 +#define RADIO_TYPE_RTRACK 0x0001 /* AIMSlab RadioTrack (RadioReveal) card -- basic, to say the least */ +#define RADIO_TYPE_WINRADIO 0x0002 /* Dunno, but made by someone */ +#define RADIO_TYPE_TYPHOON 0x0004 /* It exists... */ + +/* waveband types */ +#define RADIO_PROTOCOL_AM 0x0010 /* AM "protocol" */ +#define RADIO_PROTOCOL_FM 0x0020 /* FM "protocol" */ +#define RADIO_PROTOCOL_SSB 0x0040 /* SSB */ +/* and no doubt some other stuff, too (Brian?) */ + + +/* the following are _very_ inaccurate; essentially, all that + * they do is provide a "name" for client programs + */ +#define RADIO_BAND_UNKNOWN 0x0000 /* other */ +#define RADIO_BAND_AM_SW 0x0100 /* short wave (?) */ +#define RADIO_BAND_AM_MW 0x0200 /* medium wave (540 - 1600) */ +#define RADIO_BAND_AM_LW 0x0400 /* long wave (150 - 270) */ +#define RADIO_BAND_FM_STD 0x1000 /* "standard" FM band (i.e. 88 - 108 or so) */ + + +/* Since floating-point stuff is illegal in the kernel, we use these + * pairs of macros to convert to, and from userland floats + * (I hope these are general enough!) + */ +/* Remember to make sure that all of these are integral... */ +/* Also remember to pass sensible things in here (MHz for FM, kHz for AM) */ +#define RADIO_FM_RES 100 /* 0.01 MHZ */ +#define RADIO_FM_FRTOINT(fl) ((int)(((float)(fl))*RADIO_FM_RES)) +#define RADIO_FM_INTTOFR(fr) ((float)(((int)(fr))/RADIO_FM_RES)) + +/* Old RadioTrack definitions +#define RADIO_FM_FRTOINT(fl) ((int)(((float)(fl)-88.0)*40)+0xf6c) +#define RADIO_FM_INTTOFR(fr) ((float)(((fr)-0xf6c)/40)+88.0) +*/ + +#define RADIO_AM_RES 1 /* 1 kHz */ +#define RADIO_AM_FRTOINT(fl) ((int)(((float)(fl))*RADIO_AM_RES)) +#define RADIO_AM_INTTOFR(fr) ((float)(((int)(fr))/RADIO_AM_RES)) + + +/* + * Structures + */ +/* query structures */ +struct radio_cap { + int dev_num; /* device index */ + int type; /* device type (see above) */ + int num_bwidths; /* number of "bandwidths" supported */ + int volmin, volmax; /* min/max in steps of one */ +}; + +struct radio_band { + int dev_num; /* device index (IN) */ + int index; /* "bandwidth" index (IN) */ + int proto; /* protocol (AM, FM, SSB, etc) (OUT) */ + int types; /* see RADIO_BAND_* above */ + int freqmin,freqmax; /* encoded according to the macros above */ + int strmin,strmax; /* min/max signal strength (steps of 1) */ +}; + +/* Previously, this was in four separate structures: + * radio_vol, radio_freq, radio_band and radio_sigstr, + * That was foolish, but now it's not so obvious what's going on. + * Be careful. + */ + +struct radio_ctl { + int dev_num; /* device index (IN) */ + int value; /* volume, frequency, band, sigstr */ +}; + + +/* + * ioctl numbers + */ +/* You have _how_ many radio devices? =) */ +#define RADIO_NUMDEVS _IOR(0x8c, 0x00, int) +#define RADIO_GETCAPS _IOR(0x8c, 0x01, struct radio_cap) +#define RADIO_GETBNDCAP _IOR(0x8c, 0x02, struct radio_band) + +#define RADIO_SETVOL _IOW(0x8c, 0x10, struct radio_ctl) +#define RADIO_GETVOL _IOR(0x8c, 0x11, struct radio_ctl) +#define RADIO_SETBAND _IOW(0x8c, 0x12, struct radio_ctl) +#define RADIO_GETBAND _IOR(0x8c, 0x13, struct radio_ctl) +#define RADIO_SETFREQ _IOW(0x8c, 0x14, struct radio_ctl) +#define RADIO_GETFREQ _IOR(0x8c, 0x15, struct radio_ctl) + +#define RADIO_GETSIGSTR _IOR(0x8c, 0x30, struct radio_ctl) + +/* kernel specific stuff... */ +#ifdef __KERNEL__ +/* Try to keep the number of function pointers to a minimum. + * Devices are responsible for updating, or otherwise, the + * variables here, not the outside wrapper. + */ +struct radio_device; + +int radio_add_device(struct radio_device *newdev); + +struct radio_device { + struct radio_cap *cap; + struct radio_band *bands; /* pointer to array of radio_bands */ + int (*setvol)(struct radio_device*,int); + int curvol; + int (*setband)(struct radio_device*,int); + int curband; + int (*setfreq)(struct radio_device*,int); + int curfreq; + int (*getsigstr)(struct radio_device*); + struct radio_device *next; + void *misc; /* device internal storage... (eg i/o addresses, etc */ +}; +#endif /* __KERNEL__ */ + +#endif /* _LINUX_RADIO_H */ diff -u --recursive --new-file v2.1.62/linux/include/linux/raid1.h linux/include/linux/raid1.h --- v2.1.62/linux/include/linux/raid1.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/raid1.h Sat Nov 8 11:39:13 1997 @@ -0,0 +1,49 @@ +#ifndef _RAID1_H +#define _RAID1_H + +#include + +struct mirror_info { + int number; + int raid_disk; + kdev_t dev; + int next; + int sect_limit; + + /* + * State bits: + */ + int operational; + int write_only; + int spare; +}; + +struct raid1_data { + struct md_dev *mddev; + struct mirror_info mirrors[MD_SB_DISKS]; /* RAID1 devices, 2 to MD_SB_DISKS */ + int raid_disks; + int working_disks; /* Number of working disks */ + int last_used; + unsigned long next_sect; + int sect_count; + int resync_running; +}; + +/* + * this is our 'private' 'collective' RAID1 buffer head. + * it contains information about what kind of IO operations were started + * for this RAID5 operation, and about their status: + */ + +struct raid1_bh { + unsigned int remaining; + unsigned int state; + int cmd; + struct md_dev *mddev; + struct buffer_head *master_bh; + struct buffer_head *mirror_bh [MD_SB_DISKS]; + struct buffer_head bh_req; + struct buffer_head *next_retry; +}; + +#endif diff -u --recursive --new-file v2.1.62/linux/include/linux/raid5.h linux/include/linux/raid5.h --- v2.1.62/linux/include/linux/raid5.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/raid5.h Sat Nov 8 11:39:13 1997 @@ -0,0 +1,110 @@ +#ifndef _RAID5_H +#define _RAID5_H + +#ifdef __KERNEL__ +#include +#include + +struct disk_info { + kdev_t dev; + int operational; + int number; + int raid_disk; + int write_only; + int spare; +}; + +struct stripe_head { + struct stripe_head *hash_next, **hash_pprev; /* hash pointers */ + struct stripe_head *free_next; /* pool of free sh's */ + struct buffer_head *buffer_pool; /* pool of free buffers */ + struct buffer_head *bh_pool; /* pool of free bh's */ + struct raid5_data *raid_conf; + struct buffer_head *bh_old[MD_SB_DISKS]; /* disk image */ + struct buffer_head *bh_new[MD_SB_DISKS]; /* buffers of the MD device (present in buffer cache) */ + struct buffer_head *bh_copy[MD_SB_DISKS]; /* copy on write of bh_new (bh_new can change from under us) */ + struct buffer_head *bh_req[MD_SB_DISKS]; /* copy of bh_new (only the buffer heads), queued to the lower levels */ + int cmd_new[MD_SB_DISKS]; /* READ/WRITE for new */ + int new[MD_SB_DISKS]; /* buffer added since the last handle_stripe() */ + unsigned long sector; /* sector of this row */ + int size; /* buffers size */ + int pd_idx; /* parity disk index */ + int nr_pending; /* nr of pending cmds */ + __u32 state; /* state flags */ + int cmd; /* stripe cmd */ + int count; /* nr of waiters */ + int write_method; /* reconstruct-write / read-modify-write */ + int phase; /* PHASE_BEGIN, ..., PHASE_COMPLETE */ + struct wait_queue *wait; /* processes waiting for this stripe */ +}; + +/* + * Phase + */ +#define PHASE_BEGIN 0 +#define PHASE_READ_OLD 1 +#define PHASE_WRITE 2 +#define PHASE_READ 3 +#define PHASE_COMPLETE 4 + +/* + * Write method + */ +#define METHOD_NONE 0 +#define RECONSTRUCT_WRITE 1 +#define READ_MODIFY_WRITE 2 + +/* + * Stripe state + */ +#define STRIPE_LOCKED 0 +#define STRIPE_ERROR 1 + +/* + * Stripe commands + */ +#define STRIPE_NONE 0 +#define STRIPE_WRITE 1 +#define STRIPE_READ 2 + +struct raid5_data { + struct stripe_head **stripe_hashtbl; + struct md_dev *mddev; + struct md_thread *thread, *resync_thread; + struct disk_info disks[MD_SB_DISKS]; + struct disk_info *spare; + int buffer_size; + int chunk_size, level, algorithm; + int raid_disks, working_disks, failed_disks; + int sector_count; + unsigned long next_sector; + atomic_t nr_handle; + struct stripe_head *next_free_stripe; + int nr_stripes; + int resync_parity; + int max_nr_stripes; + int clock; + int nr_hashed_stripes; + int nr_locked_stripes; + int nr_pending_stripes; + int nr_cached_stripes; + + /* + * Free stripes pool + */ + int nr_free_sh; + struct stripe_head *free_sh_list; + struct wait_queue *wait_for_stripe; +}; + +#endif + +/* + * Our supported algorithms + */ +#define ALGORITHM_LEFT_ASYMMETRIC 0 +#define ALGORITHM_RIGHT_ASYMMETRIC 1 +#define ALGORITHM_LEFT_SYMMETRIC 2 +#define ALGORITHM_RIGHT_SYMMETRIC 3 + +#endif diff -u --recursive --new-file v2.1.62/linux/include/linux/soundcard.h linux/include/linux/soundcard.h --- v2.1.62/linux/include/linux/soundcard.h Wed May 28 10:51:33 1997 +++ linux/include/linux/soundcard.h Tue Sep 30 08:46:45 1997 @@ -33,7 +33,7 @@ * Use ioctl(fd, OSS_GETVERSION, &int) to get the version number of * the currently active driver. */ -#define SOUND_VERSION 0x030800 +#define SOUND_VERSION 0x030802 #define OPEN_SOUND_SYSTEM /* In Linux we need to be prepared for cross compiling */ @@ -127,7 +127,7 @@ #define SNDCTL_SEQ_GETOUTCOUNT _SIOR ('Q', 4, int) #define SNDCTL_SEQ_GETINCOUNT _SIOR ('Q', 5, int) #define SNDCTL_SEQ_PERCMODE _SIOW ('Q', 6, int) -#define SNDCTL_FM_LOAD_INSTR _SIOW ('Q', 7, struct sbi_instrument) /* Obsolete. Don't use. */ +#define SNDCTL_FM_LOAD_INSTR _SIOW ('Q', 7, struct sbi_instrument) /* Obsolete. Don't use!!!!!! */ #define SNDCTL_SEQ_TESTMIDI _SIOW ('Q', 8, int) #define SNDCTL_SEQ_RESETSAMPLES _SIOW ('Q', 9, int) #define SNDCTL_SEQ_NRSYNTHS _SIOR ('Q',10, int) @@ -697,7 +697,7 @@ * the devices supported by the particular mixer. */ -#define SOUND_MIXER_NRDEVICES 17 +#define SOUND_MIXER_NRDEVICES 25 #define SOUND_MIXER_VOLUME 0 #define SOUND_MIXER_BASS 1 #define SOUND_MIXER_TREBLE 2 @@ -721,6 +721,14 @@ #define SOUND_MIXER_LINE1 14 /* Input source 1 (aux1) */ #define SOUND_MIXER_LINE2 15 /* Input source 2 (aux2) */ #define SOUND_MIXER_LINE3 16 /* Input source 3 (line) */ +#define SOUND_MIXER_DIGITAL1 17 /* Digital (input) 1 */ +#define SOUND_MIXER_DIGITAL2 18 /* Digital (input) 2 */ +#define SOUND_MIXER_DIGITAL3 19 /* Digital (input) 3 */ +#define SOUND_MIXER_PHONEIN 20 /* Phone input */ +#define SOUND_MIXER_PHONEOUT 21 /* Phone output */ +#define SOUND_MIXER_VIDEO 22 /* Video/TV (audio) in */ +#define SOUND_MIXER_RADIO 23 /* Radio in */ +#define SOUND_MIXER_MONITOR 24 /* Monitor (usually mic) volume */ /* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */ /* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */ @@ -741,11 +749,13 @@ #define SOUND_DEVICE_LABELS {"Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \ "Mic ", "CD ", "Mix ", "Pcm2 ", "Rec ", "IGain", "OGain", \ - "Line1", "Line2", "Line3"} + "Line1", "Line2", "Line3", "Digital1", "Digital2", "Digital3", \ + "PhoneIn", "PhoneOut", "Video", "Radio", "Monitor"} #define SOUND_DEVICE_NAMES {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \ "mic", "cd", "mix", "pcm2", "rec", "igain", "ogain", \ - "line1", "line2", "line3"} + "line1", "line2", "line3", "dig1", "dig2", "dig3", \ + "phin", "phout", "video", "radio", "monitor"} /* Device bitmask identifiers */ @@ -775,6 +785,14 @@ #define SOUND_MASK_LINE1 (1 << SOUND_MIXER_LINE1) #define SOUND_MASK_LINE2 (1 << SOUND_MIXER_LINE2) #define SOUND_MASK_LINE3 (1 << SOUND_MIXER_LINE3) +#define SOUND_MASK_DIGITAL1 (1 << SOUND_MIXER_DIGITAL1) +#define SOUND_MASK_DIGITAL2 (1 << SOUND_MIXER_DIGITAL2) +#define SOUND_MASK_DIGITAL3 (1 << SOUND_MIXER_DIGITAL3) +#define SOUND_MASK_PHONEIN (1 << SOUND_MIXER_PHONEIN) +#define SOUND_MASK_PHONEOUT (1 << SOUND_MIXER_PHONEOUT) +#define SOUND_MASK_RADIO (1 << SOUND_MIXER_RADIO) +#define SOUND_MASK_VIDEO (1 << SOUND_MIXER_VIDEO) +#define SOUND_MASK_MONITOR (1 << SOUND_MIXER_MONITOR) /* Obsolete macros */ #define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE) diff -u --recursive --new-file v2.1.62/linux/include/net/ipx.h linux/include/net/ipx.h --- v2.1.62/linux/include/net/ipx.h Mon Jul 7 16:02:45 1997 +++ linux/include/net/ipx.h Sun Nov 9 10:13:48 1997 @@ -18,9 +18,9 @@ typedef struct { - unsigned long net; - unsigned char node[IPX_NODE_LEN]; - unsigned short sock; + __u32 net; + __u8 node[IPX_NODE_LEN]; + __u16 sock; } ipx_address; #define ipx_broadcast_node "\377\377\377\377\377\377" @@ -28,11 +28,11 @@ struct ipxhdr { - unsigned short ipx_checksum __attribute__ ((packed)); + __u16 ipx_checksum __attribute__ ((packed)); #define IPX_NO_CHECKSUM 0xFFFF - unsigned short ipx_pktsize __attribute__ ((packed)); - unsigned char ipx_tctrl; - unsigned char ipx_type; + __u16 ipx_pktsize __attribute__ ((packed)); + __u8 ipx_tctrl; + __u8 ipx_type; #define IPX_TYPE_UNKNOWN 0x00 #define IPX_TYPE_RIP 0x01 /* may also be 0 */ #define IPX_TYPE_SAP 0x04 /* may also be 0 */ @@ -49,7 +49,7 @@ typedef struct ipx_interface { /* IPX address */ - unsigned long if_netnum; + __u32 if_netnum; unsigned char if_node[IPX_NODE_LEN]; /* physical device info */ @@ -70,7 +70,7 @@ } ipx_interface; typedef struct ipx_route { - unsigned long ir_net; + __u32 ir_net; ipx_interface *ir_intrfc; unsigned char ir_routed; unsigned char ir_router_node[IPX_NODE_LEN]; diff -u --recursive --new-file v2.1.62/linux/include/scsi/scsi.h linux/include/scsi/scsi.h --- v2.1.62/linux/include/scsi/scsi.h Fri Apr 4 08:52:26 1997 +++ linux/include/scsi/scsi.h Fri Nov 7 10:14:49 1997 @@ -180,6 +180,8 @@ /* Used to obtain the host number of a device. */ #define SCSI_IOCTL_PROBE_HOST 0x5385 +/* Used to get the bus number for a device */ +#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386 /* * Overrides for Emacs so that we follow Linus's tabbing style. diff -u --recursive --new-file v2.1.62/linux/init/main.c linux/init/main.c --- v2.1.62/linux/init/main.c Sun Sep 7 13:10:43 1997 +++ linux/init/main.c Tue Nov 4 09:17:30 1997 @@ -117,6 +117,7 @@ extern void generic_NCR53C400_setup(char *str, int *intr); extern void aha152x_setup(char *str, int *ints); extern void aha1542_setup(char *str, int *ints); +extern void gdth_setup(char *str, int *ints); extern void aic7xxx_setup(char *str, int *ints); extern void AM53C974_setup(char *str, int *ints); extern void BusLogic_Setup(char *str, int *ints); @@ -404,6 +405,9 @@ #endif #ifdef CONFIG_SCSI_AHA1542 { "aha1542=", aha1542_setup}, +#endif +#ifdef CONFIG_SCSI_GDTH + { "gdth=", gdth_setup}, #endif #ifdef CONFIG_SCSI_AIC7XXX { "aic7xxx=", aic7xxx_setup}, diff -u --recursive --new-file v2.1.62/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v2.1.62/linux/kernel/ksyms.c Mon Nov 3 13:04:27 1997 +++ linux/kernel/ksyms.c Sat Nov 8 11:39:13 1997 @@ -184,7 +184,6 @@ EXPORT_SYMBOL(ll_rw_block); EXPORT_SYMBOL(__wait_on_buffer); EXPORT_SYMBOL(mark_buffer_uptodate); -EXPORT_SYMBOL(unlock_buffer); EXPORT_SYMBOL(add_blkdev_randomness); EXPORT_SYMBOL(generic_file_read); EXPORT_SYMBOL(generic_file_write); @@ -232,6 +231,10 @@ EXPORT_SYMBOL(gendisk_head); EXPORT_SYMBOL(resetup_one_dev); EXPORT_SYMBOL(unplug_device); +EXPORT_SYMBOL(make_request); +EXPORT_SYMBOL(tq_disk); +EXPORT_SYMBOL(efind_buffer); +EXPORT_SYMBOL(init_buffer); /* tty routines */ EXPORT_SYMBOL(tty_hangup); diff -u --recursive --new-file v2.1.62/linux/mm/filemap.c linux/mm/filemap.c --- v2.1.62/linux/mm/filemap.c Sat Oct 25 02:44:18 1997 +++ linux/mm/filemap.c Sat Nov 8 11:39:13 1997 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -445,12 +446,20 @@ #define MIN_READAHEAD PageAlignSize(4096*3) #endif +static inline int get_max_readahead(struct inode * inode) +{ + if (!inode->i_dev || !max_readahead[MAJOR(inode->i_dev)]) + return MAX_READAHEAD; + return max_readahead[MAJOR(inode->i_dev)][MINOR(inode->i_dev)]; +} + static inline unsigned long generic_file_readahead(int reada_ok, struct file * filp, struct inode * inode, unsigned long ppos, struct page * page, unsigned long page_cache) { unsigned long max_ahead, ahead; unsigned long raend; + int max_readahead = get_max_readahead(inode); raend = filp->f_raend & PAGE_MASK; max_ahead = 0; @@ -534,8 +543,8 @@ filp->f_ramax += filp->f_ramax; - if (filp->f_ramax > MAX_READAHEAD) - filp->f_ramax = MAX_READAHEAD; + if (filp->f_ramax > max_readahead) + filp->f_ramax = max_readahead; #ifdef PROFILE_READAHEAD profile_readahead((reada_ok == 2), filp); @@ -562,6 +571,7 @@ ssize_t error, read; size_t pos, pgpos, page_cache; int reada_ok; + int max_readahead = get_max_readahead(inode); if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; @@ -608,8 +618,8 @@ if (reada_ok && filp->f_ramax < MIN_READAHEAD) filp->f_ramax = MIN_READAHEAD; - if (filp->f_ramax > MAX_READAHEAD) - filp->f_ramax = MAX_READAHEAD; + if (filp->f_ramax > max_readahead) + filp->f_ramax = max_readahead; } for (;;) { diff -u --recursive --new-file v2.1.62/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c --- v2.1.62/linux/net/ipv4/ip_fragment.c Sun Sep 7 13:10:43 1997 +++ linux/net/ipv4/ip_fragment.c Thu Nov 6 15:02:38 1997 @@ -14,6 +14,7 @@ * Alan Cox : Split from ip.c , see ip_input.c for history. * David S. Miller : Begin massive cleanup... * Andi Kleen : Add sysctls. + * xxxx : Overlapfrag bug. */ #include @@ -339,7 +340,7 @@ /* Copy the data portions of all fragments into the new buffer. */ fp = qp->fragments; while(fp) { - if(count+fp->len > skb->len) { + if (fp->len < 0 || count+fp->len > skb->len) { NETDEBUG(printk(KERN_ERR "Invalid fragment list: " "Fragment over size.\n")); ip_free(qp); diff -u --recursive --new-file v2.1.62/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c --- v2.1.62/linux/net/ipx/af_ipx.c Thu Sep 4 17:07:32 1997 +++ linux/net/ipx/af_ipx.c Sun Nov 9 10:13:48 1997 @@ -221,7 +221,7 @@ * IPX interface is defined by a physical device and a frame type. */ -static ipx_route * ipxrtr_lookup(unsigned long); +static ipx_route * ipxrtr_lookup(__u32); static void ipxitf_clear_primary_net(void) { @@ -242,11 +242,11 @@ return i; } -static ipx_interface *ipxitf_find_using_net(unsigned long net) +static ipx_interface *ipxitf_find_using_net(__u32 net) { ipx_interface *i; - if (net == 0L) + if (!net) return ipx_primary_net; for (i=ipx_interfaces; i && (i->if_netnum!=net); i=i->if_next) @@ -699,7 +699,7 @@ return 0; } -static int ipxrtr_add_route(unsigned long, ipx_interface *, unsigned char *); +static int ipxrtr_add_route(__u32, ipx_interface *, unsigned char *); static int ipxitf_add_local_route(ipx_interface *intrfc) { @@ -726,9 +726,9 @@ } /* See if we should update our network number */ - if ((intrfc->if_netnum == 0L) && - (ipx->ipx_source.net == ipx->ipx_dest.net) && - (ipx->ipx_source.net != 0L)) + if ( !intrfc->if_netnum && /* net number of intrfc not known yet (== 0) */ + (ipx->ipx_source.net == ipx->ipx_dest.net) && /* intra-net packet */ + ipx->ipx_source.net) /* source net number of packet != 0 */ { /* NB: NetWare servers lie about their hop count so we * dropped the test based on it. This is the best way @@ -742,7 +742,7 @@ else { printk(KERN_WARNING "IPX: Network number collision %lx\n %s %s and %s %s\n", - htonl(ipx->ipx_source.net), + (long unsigned int) htonl(ipx->ipx_source.net), ipx_device_name(i), ipx_frame_name(i->if_dlink_type), ipx_device_name(intrfc), @@ -756,12 +756,12 @@ int i; ipx_interface *ifcs; struct sk_buff *skb2; - long *l; + __u32 *l; char *c; c = (char *) skb->data; c += sizeof( struct ipxhdr ); - l = (long *) c; + l = (__u32 *) c; i = 0; /* @@ -780,7 +780,7 @@ for ( ifcs = ipx_interfaces; ifcs != NULL ; ifcs = ifcs->if_next) { /* That aren't in the list */ - l = (long *) c; + l = (__u32 *) c; for( i = 0 ; i <= ipx->ipx_tctrl ; i++ ) if( ifcs->if_netnum == *l++ ) break; @@ -804,9 +804,9 @@ } #endif - if (ipx->ipx_dest.net == 0L) + if (!ipx->ipx_dest.net) ipx->ipx_dest.net = intrfc->if_netnum; - if (ipx->ipx_source.net == 0L) + if (!ipx->ipx_source.net) ipx->ipx_source.net = intrfc->if_netnum; if (intrfc->if_netnum != ipx->ipx_dest.net) @@ -873,7 +873,7 @@ return -EEXIST; /* Must have a valid network number */ - if (idef->ipx_network == 0L) + if (!idef->ipx_network) return -EADDRNOTAVAIL; if (ipxitf_find_using_net(idef->ipx_network) != NULL) return -EADDRINUSE; @@ -927,7 +927,7 @@ if ((idef->ipx_special == IPX_PRIMARY) && (ipx_primary_net != NULL)) return -EEXIST; - if ((idef->ipx_network != 0L) && + if (idef->ipx_network && (ipxitf_find_using_net(idef->ipx_network) != NULL)) return -EADDRINUSE; @@ -1001,7 +1001,7 @@ } /* If the network number is known, add a route */ - if (intrfc->if_netnum == 0L) + if (!intrfc->if_netnum) return 0; return ipxitf_add_local_route(intrfc); @@ -1075,7 +1075,7 @@ if (intrfc!=NULL) { intrfc->if_dev=dev; - intrfc->if_netnum=0L; + intrfc->if_netnum=0; intrfc->if_dlink_type = dlink_type; intrfc->if_dlink = datalink; intrfc->if_sklist = NULL; @@ -1178,7 +1178,7 @@ * * \*******************************************************************************************************************/ -static ipx_route *ipxrtr_lookup(unsigned long net) +static ipx_route *ipxrtr_lookup(__u32 net) { ipx_route *r; @@ -1188,7 +1188,7 @@ return r; } -static int ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node) +static int ipxrtr_add_route(__u32 network, ipx_interface *intrfc, unsigned char *node) { ipx_route *rt; @@ -1340,7 +1340,7 @@ int err; /* Find the appropriate interface on which to send packet */ - if ((usipx->sipx_network == 0L) && (ipx_primary_net != NULL)) + if (!usipx->sipx_network && (ipx_primary_net != NULL)) { usipx->sipx_network = ipx_primary_net->if_netnum; intrfc = ipx_primary_net; @@ -1519,7 +1519,7 @@ len += sprintf (buffer,"%-11s%-15s%-9s%-11s%s\n", "Network", "Node_Address", "Primary", "Device", "Frame_Type"); for (i = ipx_interfaces; i != NULL; i = i->if_next) { - len += sprintf(buffer+len, "%08lX ", ntohl(i->if_netnum)); + len += sprintf(buffer+len, "%08lX ", (long unsigned int)ntohl(i->if_netnum)); len += sprintf (buffer+len,"%02X%02X%02X%02X%02X%02X ", i->if_node[0], i->if_node[1], i->if_node[2], i->if_node[3], i->if_node[4], i->if_node[5]); @@ -1572,7 +1572,7 @@ #ifdef CONFIG_IPX_INTERN len += sprintf(buffer+len, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ", - htonl(s->protinfo.af_ipx.intrfc->if_netnum), + (long unsigned int) htonl(s->protinfo.af_ipx.intrfc->if_netnum), s->protinfo.af_ipx.node[0], s->protinfo.af_ipx.node[1], s->protinfo.af_ipx.node[2], @@ -1590,7 +1590,7 @@ } else { len += sprintf (buffer+len, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ", - htonl(s->protinfo.af_ipx.dest_addr.net), + (long unsigned int) htonl(s->protinfo.af_ipx.dest_addr.net), s->protinfo.af_ipx.dest_addr.node[0], s->protinfo.af_ipx.dest_addr.node[1], s->protinfo.af_ipx.dest_addr.node[2], @@ -1639,10 +1639,10 @@ "Network", "Router_Net", "Router_Node"); for (rt = ipx_routes; rt != NULL; rt = rt->ir_next) { - len += sprintf (buffer+len,"%08lX ", ntohl(rt->ir_net)); + len += sprintf (buffer+len,"%08lX ", (long unsigned int) ntohl(rt->ir_net)); if (rt->ir_routed) { len += sprintf (buffer+len,"%08lX %02X%02X%02X%02X%02X%02X\n", - ntohl(rt->ir_intrfc->if_netnum), + (long unsigned int) ntohl(rt->ir_intrfc->if_netnum), rt->ir_router_node[0], rt->ir_router_node[1], rt->ir_router_node[2], rt->ir_router_node[3], rt->ir_router_node[4], rt->ir_router_node[5]); @@ -1906,7 +1906,7 @@ int ret; uaddr.sipx_port = 0; - uaddr.sipx_network = 0L; + uaddr.sipx_network = 0; #ifdef CONFIG_IPX_INTERN memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN); @@ -1966,7 +1966,7 @@ #endif } else { - sipx.sipx_network = 0L; + sipx.sipx_network = 0; memset(sipx.sipx_node, '\0', IPX_NODE_LEN); } sipx.sipx_port = sk->protinfo.af_ipx.port; @@ -2022,8 +2022,8 @@ void dump_addr(char *str,ipx_address *p) { - printk(KERN_DEBUG"%s: %08X:%02X%02X%02X%02X%02X%02X:%04X\n", - str,ntohl(p->net),p->node[0],p->node[1],p->node[2], + printk(KERN_DEBUG"%s: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n", + str,(long unsigned int)ntohl(p->net),p->node[0],p->node[1],p->node[2], p->node[3],p->node[4],p->node[5],ntohs(p->sock)); }