]> git.neil.brown.name Git - history.git/commitdiff
Import 2.1.63 2.1.63
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:14:01 +0000 (15:14 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:14:01 +0000 (15:14 -0500)
163 files changed:
CREDITS
Documentation/Configure.help
Documentation/devices.tex
Documentation/devices.txt
MAINTAINERS
Makefile
arch/alpha/kernel/time.c
arch/i386/defconfig
arch/i386/kernel/ioport.c
arch/i386/kernel/setup.c
arch/i386/kernel/traps.c
arch/i386/mm/fault.c
arch/sparc/config.in
drivers/block/Config.in
drivers/block/Makefile
drivers/block/linear.c
drivers/block/ll_rw_blk.c
drivers/block/md.c
drivers/block/raid0.c
drivers/block/raid1.c [new file with mode: 0644]
drivers/block/raid5.c [new file with mode: 0644]
drivers/char/Config.in
drivers/char/Makefile
drivers/char/README.cycladesZ [new file with mode: 0644]
drivers/char/busmouse.c
drivers/char/cyclades.c
drivers/char/joystick.c
drivers/char/misc.c
drivers/char/nvram.c
drivers/char/pcwd.c
drivers/char/radio.c [new file with mode: 0644]
drivers/char/rtrack.c [new file with mode: 0644]
drivers/char/rtrack.h [new file with mode: 0644]
drivers/net/3c507.c
drivers/net/dgrs.c
drivers/net/ne.c
drivers/scsi/Config.in
drivers/scsi/Makefile
drivers/scsi/fdomain.c
drivers/scsi/gdth.c [new file with mode: 0644]
drivers/scsi/gdth.h [new file with mode: 0644]
drivers/scsi/gdth_ioctl.h [new file with mode: 0644]
drivers/scsi/gdth_proc.c [new file with mode: 0644]
drivers/scsi/gdth_proc.h [new file with mode: 0644]
drivers/scsi/hosts.c
drivers/scsi/pci2000.c [new file with mode: 0644]
drivers/scsi/pci2000.h [new file with mode: 0644]
drivers/scsi/pci2220i.c [new file with mode: 0644]
drivers/scsi/pci2220i.h [new file with mode: 0644]
drivers/scsi/psi240i.c [new file with mode: 0644]
drivers/scsi/psi240i.h [new file with mode: 0644]
drivers/scsi/psi_chip.h [new file with mode: 0644]
drivers/scsi/psi_dale.h [new file with mode: 0644]
drivers/scsi/psi_roy.h [new file with mode: 0644]
drivers/scsi/scsi.h
drivers/scsi/scsi_ioctl.c
drivers/sound/.objects
drivers/sound/.version
drivers/sound/CHANGELOG
drivers/sound/Config.in
drivers/sound/Makefile
drivers/sound/Readme
drivers/sound/Readme.cards
drivers/sound/Readme.linux
drivers/sound/ad1848.c
drivers/sound/adlib_card.c
drivers/sound/audio.c
drivers/sound/configure.c
drivers/sound/cs4232.c
drivers/sound/dev_table.c
drivers/sound/dev_table.h
drivers/sound/dmabuf.c
drivers/sound/dmabuf.c.old [new file with mode: 0644]
drivers/sound/dmasound.c
drivers/sound/gus_card.c
drivers/sound/gus_midi.c
drivers/sound/gus_wave.c
drivers/sound/lowlevel/ChangeLog.awe
drivers/sound/lowlevel/Makefile
drivers/sound/lowlevel/README.awe
drivers/sound/lowlevel/aedsp16.c
drivers/sound/lowlevel/awe_compat.h [new file with mode: 0644]
drivers/sound/lowlevel/awe_config.h
drivers/sound/lowlevel/awe_hw.h
drivers/sound/lowlevel/awe_version.h [new file with mode: 0644]
drivers/sound/lowlevel/awe_wave.c
drivers/sound/mad16.c
drivers/sound/maui.c
drivers/sound/midi_synth.c
drivers/sound/midibuf.c
drivers/sound/mpu401.c
drivers/sound/opl3.c
drivers/sound/opl3sa.c [new file with mode: 0644]
drivers/sound/os.h
drivers/sound/pas2_card.c
drivers/sound/pas2_midi.c
drivers/sound/pas2_mixer.c
drivers/sound/pas2_pcm.c
drivers/sound/pss.c
drivers/sound/sb.h
drivers/sound/sb_audio.c
drivers/sound/sb_card.c
drivers/sound/sb_common.c
drivers/sound/sb_midi.c
drivers/sound/sb_mixer.c
drivers/sound/sb_mixer.h
drivers/sound/sequencer.c
drivers/sound/softoss.c [new file with mode: 0644]
drivers/sound/softoss.h [new file with mode: 0644]
drivers/sound/softoss_rs.c [new file with mode: 0644]
drivers/sound/sound_calls.h
drivers/sound/sound_config.h
drivers/sound/sound_switch.c
drivers/sound/soundcard.c
drivers/sound/soundvers.h
drivers/sound/sscape.c
drivers/sound/trix.c
drivers/sound/uart401.c
drivers/sound/uart6850.c
fs/buffer.c
fs/dcache.c
fs/ext2/namei.c
fs/fat/cache.c
fs/fat/dir.c
fs/fat/inode.c
fs/fat/mmap.c
fs/filesystems.c
fs/msdos/namei.c
fs/ncpfs/dir.c
fs/ncpfs/file.c
fs/ncpfs/inode.c
fs/ncpfs/ioctl.c
fs/ncpfs/mmap.c
fs/ncpfs/ncplib_kernel.c
fs/ncpfs/ncplib_kernel.h
fs/ncpfs/sock.c
fs/nfs/dir.c
include/asm-i386/bugs.h
include/asm-i386/io.h
include/linux/awe_voice.h
include/linux/blk.h
include/linux/blkdev.h
include/linux/cyclades.h
include/linux/dcache.h
include/linux/fs.h
include/linux/ipx.h
include/linux/locks.h
include/linux/md.h
include/linux/miscdevice.h
include/linux/ncp_fs.h
include/linux/nfs_fs.h
include/linux/proc_fs.h
include/linux/radio.h [new file with mode: 0644]
include/linux/raid1.h [new file with mode: 0644]
include/linux/raid5.h [new file with mode: 0644]
include/linux/soundcard.h
include/net/ipx.h
include/scsi/scsi.h
init/main.c
kernel/ksyms.c
mm/filemap.c
net/ipv4/ip_fragment.c
net/ipx/af_ipx.c

diff --git a/CREDITS b/CREDITS
index fa6af4a1509cca4807b79d5d827d70673b662ca0..892798e3503df423f624d79233aaaa97b8414305 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -643,12 +643,13 @@ S: CV5 8BZ
 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
@@ -868,6 +869,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
 D: Promised to send money if I would put his name in the source tree.
index 522d73a1682fc38444c6945e7b8578b286c659d0..ea7bf41dcdf8ff43869da2bedaf803b6d497ba83 100644 (file)
@@ -419,6 +419,33 @@ CONFIG_MD_STRIPED
   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 @@ CONFIG_SCSI_AM53C974
   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
@@ -5220,6 +5256,26 @@ CONFIG_JOYSTICK
   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
   If you boot your Alpha using the ARC firmware, say Y here. This option
index 37bdfae0b93bbeeb842aab974793426536ad98f1..b48b37b3eccffe5019997579cd13b1cfcabed766 100644 (file)
@@ -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 @@ foo \kill}%
  {\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 @@ I do not have any information about these devices beyond what appears
 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 @@ reply.
 \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 @@ on {\url http://home.pages.de/~videotext/\/}.
 \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 @@ For SCSI devices, {\file /dev/tape} and {\file /dev/cdrom} should
 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}
 
index 4c390293b086c3eea87cfa868e6eecb4e29012f7..1a84f5563c81f82559ad7b386aec93c35ecd5fc1 100644 (file)
@@ -1,38 +1,27 @@
                       LINUX ALLOCATED DEVICES
             Maintained by H. Peter Anvin <hpa@zytor.com>
 
-                  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 @@ I do not have any information about these devices beyond what appears
 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 @@ reply.
                  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 @@ reply.
                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 @@ reply.
                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 @@ reply.
                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 @@ reply.
                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 @@ reply.
                /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 @@ reply.
                /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 @@ reply.
                      ...
 
                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 @@ reply.
                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 @@ reply.
                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 @@ reply.
                  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 @@ reply.
                  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 @@ reply.
                 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 @@ reply.
                  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 @@ reply.
                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 @@ reply.
                  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 @@ reply.
 
  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 @@ reply.
                      ...
 
                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 @@ reply.
                 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
 
index 5f22e1bbefcf0990cd05bbf43f4d0c7bb52bd9a7..8d88bd1fbd87c901c56925f578525f536348657d 100644 (file)
@@ -204,6 +204,13 @@ M: mike@i-Connect.Net
 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
index a969c6aa7609c496d22df31af45a85101bd431eb..02dba9d90732b8f863c3d11e0bd624d3a8921485 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -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/)
 
index 4a2ccbdac83161224b38ffd537ba2e673a32861a..2b0870c3bdee710fc6e6a85ea626bed83e40a798 100644 (file)
@@ -50,8 +50,6 @@ static int set_rtc_mmss(unsigned long);
  */
 #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 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs)
        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 @@ void time_init(void)
        /* 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 <jestabro@amt.tay1.dec.com>:
+ * 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 @@ void time_init(void)
                __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 
index cc151a3cc37cf5e2285c4c232b7ee3766b240538..e60528b141a2d8b90ca0781849da6469a2840fa9 100644 (file)
@@ -137,11 +137,15 @@ CONFIG_SCSI_OMIT_FLASHPOINT=y
 # 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_82C710_MOUSE=y
 # CONFIG_RTC is not set
 # CONFIG_NVRAM is not set
 # CONFIG_JOYSTICK is not set
+# CONFIG_MISC_RADIO is not set
 
 #
 # Sound
index 8f929f603c5a090bd3cb7db95542d595b3edf1e3..9bb150075c43d2fbc9123a871e8f8344a7c97071 100644 (file)
@@ -75,17 +75,15 @@ unsigned int *stack;
  * 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;
 }
index 590b69c38e0df28da71f1e9a6fa1c3c6eed85ff0..8e5c9f83565c6e71723cc9d3e935b61ad69f7929 100644 (file)
@@ -42,6 +42,7 @@ char x86_model = 0;           /* set by kernel/head.S */
 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 @@ int get_cpuinfo(char * buffer)
                                        "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 @@ int get_cpuinfo(char * buffer)
                                        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",
index 6fb18dc35e473af895cbf3eefdc4dd0a1472053f..bafc5f9ead572870817015caa8293ecdcd8d1589 100644 (file)
@@ -413,6 +413,51 @@ asmlinkage void math_emulate(long arg)
 
 #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;
index 7021b2d4c3e8f4e9a37b91d67500587a3c41afae..e3c54c6838a5000532b73fb54d87b03004f3fba8 100644 (file)
@@ -74,6 +74,25 @@ bad_area:
        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 @@ bad_area:
                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("<handler %p... ", handler);
+               unlock_kernel();
+
+               if (handler==divide_error)
+                       do_divide_error(regs,error_code);
+               else if (handler==debug)
+                       do_debug(regs,error_code);
+               else if (handler==nmi)
+                       do_nmi(regs,error_code);
+               else if (handler==int3)
+                       do_int3(regs,error_code);
+               else if (handler==overflow)
+                       do_overflow(regs,error_code);
+               else if (handler==bounds)
+                       do_bounds(regs,error_code);
+               else if (handler==invalid_op)
+                       do_invalid_op(regs,error_code);
+               else {
+                       printk("INVALID HANDLER!\n");
+                       for (;;) __cli();
+               }
+               printk("... done>\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 @@ bad_area:
                flush_tlb();
                goto out;
        }
+
        if (address < PAGE_SIZE)
                printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
        else
index 7b88b03f28cce0a6c33525984a5aeb8d753238ea..ae8568b25008686a77dd7631dad6b1b6edc82177 100644 (file)
@@ -68,7 +68,8 @@ bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
 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
index 59486adaad860aba5d8184b178ba1fb6c772527a..e6aec9ea7bb4eaf152dc52f2a20de38ce9368b14 100644 (file)
@@ -53,6 +53,8 @@ bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
 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
index de13e072a1abc4eaec5b03e072358833c87b11ef..344705ecf8ab653dfcc70e9a62e6dcb1064accd8 100644 (file)
@@ -200,25 +200,21 @@ else
   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
 
index f0f9fec79af8717170fcfa50b7685aadc845f917..b6f72fd6a038fbf46fe733cc12526917865bc8a9 100644 (file)
@@ -163,6 +163,7 @@ static int linear_status (char *page, int minor, struct md_dev *mddev)
 
   sz+=sprintf (page+sz, "\n");
 #endif
+  sz+=sprintf (page+sz, " %dk rounding", 1<<FACTOR_SHIFT(FACTOR(mddev)));
   return sz;
 }
 
@@ -171,6 +172,8 @@ static struct md_personality linear_personality=
 {
   "linear",
   linear_map,
+  NULL,
+  NULL,
   linear_run,
   linear_stop,
   linear_status,
index c95d0038eca6144beead026b36c2bdd141160421..52de49e31072c7a122066d82c2fb713a0d2d4a92 100644 (file)
@@ -82,6 +82,11 @@ int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
  */
 int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
 
+/*
+ * The following tunes the read-ahead algorithm in mm/filemap.c
+ */
+int * max_readahead[MAX_BLKDEV] = { NULL, NULL, };
+
 static inline struct request **get_queue(kdev_t dev)
 {
        int major = MAJOR(dev);
@@ -295,7 +300,27 @@ void add_request(struct blk_dev_struct * dev, struct request * req)
        sti();
 }
 
-static void make_request(int major,int rw, struct buffer_head * bh)
+#define MAX_SECTORS 244
+
+static inline void attempt_merge (struct request *req)
+{
+       struct request *next = req->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 @@ static void make_request(int major,int rw, struct buffer_head * bh)
 
        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 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                                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 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                        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 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                        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 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                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 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                                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 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                        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 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                                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 @@ static void make_request(int major,int rw, struct buffer_head * bh)
 
 /* 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 @@ static void make_request(int major,int rw, struct buffer_head * bh)
        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 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
        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 @@ __initfunc(int blk_dev_init(void))
                req->next = NULL;
        }
        memset(ro_bits,0,sizeof(ro_bits));
+       memset(max_readahead, 0, sizeof(max_readahead));
 #ifdef CONFIG_AMIGA_Z2RAM
        z2_init();
 #endif
index 9a64e1c77ce1fc23a561a2103626f3a0e3f32282..4a29c87cc86849c48652bc6fb56b43740abe83ad 100644 (file)
@@ -9,6 +9,9 @@
 
    kerneld support by Boris Tobotras <boris@xtalk.msk.su>
 
+   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)
    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 <linux/config.h>
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/proc_fs.h>
 #include <linux/blkdev.h>
 #include <linux/genhd.h>
+#include <linux/smp_lock.h>
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
 #include <linux/errno.h>
 #include <linux/init.h>
 
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
 #define MAJOR_NR MD_MAJOR
 #define MD_DRIVER
 
 #include <linux/blk.h>
 #include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
 
 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 gendisk md_gendisk=
 };
 
 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 @@ static struct gendisk *find_gendisk (kdev_t dev)
   return (NULL);
 }
 
-
 char *partition_name (kdev_t dev)
 {
   static char name[40];                /* This should be long
@@ -93,49 +112,318 @@ char *partition_name (kdev_t dev)
 
   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<md_dev[minor].nb_dev; i++) {
+               md_dev[minor].devices[i].size &= ~(factor - 1);
+               md_size[minor] += md_dev[minor].devices[i].size;
+               md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + 
+                                                       md_dev[minor].devices[i-1].size) : 0;
+       }
+       if (pnum == RAID0 >> 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;
+
+       if (mddev->sb) {
+               free_page((unsigned long) mddev->sb);
+               mddev->sb = NULL;
+       }
+       for (i = 0; i <mddev->nb_dev; i++) {
+               realdev = mddev->devices + i;
+               if (realdev->sb) {
+                       free_page((unsigned long) realdev->sb);
+                       realdev->sb = NULL;
+               }
+       }
+}
 
-  for (i=0; i<MAX_MD_DEV; i++)
-  {
-    if (!md_dev[i].pers)
-      continue;
-    
-    for (j=0; j<md_dev[i].nb_dev; j++)
-      if (read_ahead[MAJOR(md_dev[i].devices[j].dev)]<minra)
-       minra=read_ahead[MAJOR(md_dev[i].devices[j].dev)];
-  }
-  
-  read_ahead[MD_MAJOR]=minra;
+/*
+ * 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 @@ static int do_md_run (int minor, int repart)
       return -EINVAL;
   }
   
-  min=1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
+  factor = min = 1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
   
   for (i=0; i<md_dev[minor].nb_dev; i++)
     if (md_dev[minor].devices[i].size<min)
@@ -156,118 +444,167 @@ static int do_md_run (int minor, int repart)
              partition_name (md_dev[minor].devices[i].dev), min);
       return -EINVAL;
     }
+
+  for (i=0; i<md_dev[minor].nb_dev; i++) {
+    fsync_dev(md_dev[minor].devices[i].dev);
+    invalidate_buffers(md_dev[minor].devices[i].dev);
+  }
   
   /* Resize devices according to the factor. It is used to align
      partitions size on a given chunk size. */
   md_size[minor]=0;
-  
-  for (i=0; i<md_dev[minor].nb_dev; i++)
-  {
-    md_dev[minor].devices[i].size &= ~(min - 1);
-    md_size[minor] += md_dev[minor].devices[i].size;
-    md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + md_dev[minor].devices[i-1].size) : 0;
-  }
+
+  /*
+   * Analyze the raid superblock
+   */ 
+  if (analyze_sbs(minor, pnum))
+    return -EINVAL;
 
   md_dev[minor].pers=pers[pnum];
   
   if ((err=md_dev[minor].pers->run (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; i<md_dev[minor].nb_dev; i++)
-    if (current_ra>read_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 (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);
-  }
+       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. */
-  for (i=0; i<md_dev[minor].nb_dev; i++)
-    clear_inode (md_dev[minor].devices[i].inode);
+       /* Remove locks. */
+       if (md_dev[minor].sb)
+       free_sb(md_dev + minor);
+       for (i=0; i<md_dev[minor].nb_dev; i++)
+               clear_inode (md_dev[minor].devices[i].inode);
+
+       md_dev[minor].nb_dev=md_size[minor]=0;
+       md_hd_struct[minor].nr_sects=0;
+       md_dev[minor].pers=NULL;
   
-  md_dev[minor].nb_dev=md_size[minor]=0;
-  md_hd_struct[minor].nr_sects=0;
-  md_dev[minor].pers=NULL;
+       read_ahead[MD_MAJOR] = 128;
   
-  set_ra ();                   /* calculate new read_ahead */
-  
-  printk ("STOP_DEV md%x\n", minor);
-  return (0);
+       return (0);
 }
 
-
 static int do_md_add (int minor, kdev_t dev)
 {
-  struct gendisk *gen_real;
-  int i;
-  
-  if (MAJOR(dev)==MD_MAJOR || md_dev[minor].nb_dev==MAX_REAL)
-    return -EINVAL;
-  
-  if (!fs_may_mount (dev) || md_dev[minor].pers)
-    return -EBUSY;
+       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;
   
-  if (!(gen_real=find_gendisk (dev)))
-    return -ENOENT;
+       /* 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 ! */
   
-  i=md_dev[minor].nb_dev++;
-  md_dev[minor].devices[i].dev=dev;
+       realdev->inode=get_empty_inode ();
+       realdev->inode->i_dev=dev;      /* don't care about other fields */
+       insert_inode_hash (realdev->inode);
   
-  /* 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 ! */
+       /* Sizes are now rounded at run time */
   
-  md_dev[minor].devices[i].inode=get_empty_inode ();
-  md_dev[minor].devices[i].inode->i_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)];
-
-  printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor);
-  return (0);
+/*  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);
 }
 
-
 static int md_ioctl (struct inode *inode, struct file *file,
                      unsigned int cmd, unsigned long arg)
 {
@@ -354,7 +691,6 @@ static int md_ioctl (struct inode *inode, struct file *file,
   return (0);
 }
 
-
 static int md_open (struct inode *inode, struct file *file)
 {
   int minor=MINOR(inode->i_rdev);
@@ -427,6 +763,30 @@ int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size)
   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 @@ static void do_md_request (void)
   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 @@ static void md_geninit (struct gendisk *gdisk)
   for(i=0;i<MAX_MD_DEV;i++)
   {
     md_blocksizes[i] = 1024;
+    md_maxreadahead[i] = MD_DEFAULT_DISK_READAHEAD;
     md_gendisk.part[i].start_sect=-1; /* avoid partition check */
     md_gendisk.part[i].nr_sects=0;
     md_dev[i].pers=NULL;
   }
 
-  blksize_size[MAJOR_NR] = md_blocksizes;
+  blksize_size[MD_MAJOR] = md_blocksizes;
+  max_readahead[MD_MAJOR] = md_maxreadahead;
 
   proc_register(&proc_root, &proc_md);
 }
 
+int md_error (kdev_t mddev, kdev_t rdev)
+{
+    unsigned int minor = MINOR (mddev);
+    int rc;
+
+    if (MAJOR(mddev) != MD_MAJOR || minor > 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 @@ int get_md_status (char *page)
                   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 @@ int get_md_status (char *page)
     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<<FACTOR_SHIFT(FACTOR(md_dev+i)),
-                md_dev[i].pers == pers[LINEAR>>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 @@ int unregister_md_personality (int p_num)
   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 @@ void raid5_init (void);
 
 __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,19 +1176,40 @@ __initfunc(int md_init (void))
     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);
 }
index 6d269745b72c562399c82da3986d80355bd7ab84..7b8aea1c9f6732818afa9446dd1690474af76eb9 100644 (file)
@@ -250,6 +250,7 @@ static int raid0_status (char *page, int minor, struct md_dev *mddev)
                 data->strip_zone[j].size);
   }
 #endif
+  sz+=sprintf (page+sz, " %dk chunks", 1<<FACTOR_SHIFT(FACTOR(mddev)));
   return sz;
 }
 
@@ -258,11 +259,17 @@ static struct md_personality raid0_personality=
 {
   "raid0",
   raid0_map,
+  NULL,                                /* no special make_request */
+  NULL,                                /* no special end_request */
   raid0_run,
   raid0_stop,
   raid0_status,
   NULL,                                /* no ioctls */
-  0
+  0,
+  NULL,                                /* no error_handler */
+  NULL,                                /* hot_add_disk */
+  NULL,                                /* hot_remove_disk */
+  NULL                         /* mark_spare */
 };
 
 
diff --git a/drivers/block/raid1.c b/drivers/block/raid1.c
new file mode 100644 (file)
index 0000000..e85d8fd
--- /dev/null
@@ -0,0 +1,870 @@
+/************************************************************************
+ * raid1.c : Multiple Devices driver for Linux
+ *           Copyright (C) 1996 Ingo Molnar, Miguel de Icaza, Gadi Oxman
+ *
+ * RAID-1 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 <linux/module.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/md.h>
+#include <linux/raid1.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+#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; i<n; i++) {
+               if (raid_conf->mirrors[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; i<n; i++)
+                       if (r1_bh->mirror_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<<BH_Req) | 
+                                               (1<<BH_Touched) | (1<<BH_Dirty);
+               mirror_bh [i]->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 --git a/drivers/block/raid5.c b/drivers/block/raid5.c
new file mode 100644 (file)
index 0000000..918dd2e
--- /dev/null
@@ -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 <linux/module.h>
+#include <linux/locks.h>
+#include <linux/malloc.h>
+#include <linux/md.h>
+#include <linux/raid5.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+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<<BH_Dirty);
+                                       PRINTK(("making request for buffer %d\n", i));
+                                       clear_bit(BH_Lock, &bh->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
index eee723670d239712c520b375e5f56e364af7f5eb..0a3b20d95a231a74407320c5753ef0abea25fc64 100644 (file)
@@ -109,4 +109,11 @@ if [ "$CONFIG_PPC" = "y" ]; then
 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
index 9150a37f688859411f2f7fba2169429308dd52e4..74ae53ef29a62fe122cdebbcbde9d599cc76d1e4 100644 (file)
@@ -281,6 +281,13 @@ else
   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 --git a/drivers/char/README.cycladesZ b/drivers/char/README.cycladesZ
new file mode 100644 (file)
index 0000000..024a694
--- /dev/null
@@ -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/
+
index 9e160cb93da83f97771629fc17824e735bea08ae..a33a6a8c22f735776f9b8a41eb769d2a8d267343 100644 (file)
@@ -159,8 +159,8 @@ static int open_mouse(struct inode * inode, struct file * file)
  * 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 @@ static long write_mouse(struct inode * inode, struct file * file,
  * 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;
index 32237f2c652ec9310b79a48562f446114e0f3e7e..ed4ac9846672b4d4912cdcbbb7690b5f273482c1 100644 (file)
@@ -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 @@ static char rcsid[] =
  * 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 @@ static char rcsid[] =
  *   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 @@ static char rcsid[] =
    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 @@ static char rcsid[] =
 
 #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 <linux/module.h>
 
@@ -540,13 +558,14 @@ static unsigned long cy_get_user(unsigned long *addr)
 #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 @@ struct tty_driver cy_serial_driver, cy_callout_driver;
 static volatile int cy_irq_triggered;
 static volatile int cy_triggered;
 static int cy_wild_int_mask;
-static unsigned char *intr_base_addr;
+static volatile ucchar *intr_base_addr;
 
 
 /* This is the address lookup table. The driver will probe for
@@ -632,26 +651,55 @@ static struct semaphore tmp_buf_sem = MUTEX;
  *      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 cyz_poll(unsigned long);
 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 @@ cyz_timerlist = {
     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 @@ do_softint(void *private_)
    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 @@ cyy_issue_cmd(u_char *base_addr, u_char cmd, int index)
     save_flags(flags); cli();
         /* Check to see that the previous command has completed */
         for(i = 0 ; i < 100 ; i++){
-            if (base_addr[CyCCR<<index] == 0){
+            if (cy_readb(base_addr+(CyCCR<<index)) == 0){
                 break;
             }
             udelay(10L);
@@ -894,7 +943,7 @@ cyy_issue_cmd(u_char *base_addr, u_char cmd, int index)
         }
 
         /* Issue the new command */
-        base_addr[CyCCR<<index] = cmd;
+        cy_writeb((u_long)base_addr+(CyCCR<<index), cmd);
     restore_flags(flags);
     return(0);
 } /* cyy_issue_cmd */
@@ -932,8 +981,9 @@ free_all_interrupts(int irq_lines)
   int i;
     
     for (i = 0; i < 16; i++) {
-        if (irq_lines & (1 << i))
+        if (irq_lines & (1 << i)) {
             free_irq(i,NULL);
+       }
     }
 } /* free_all_interrupts */
 
@@ -959,13 +1009,13 @@ check_wild_interrupts(void)
          * Delay for 0.1 seconds -- we use a busy loop since this may 
          * occur during the bootup sequence
          */
-        timeout = jiffies+10;
+        timeout = jiffies+(HZ/10);
         while (timeout >= jiffies)
             ;
         
         cy_triggered = 0;       /* Reset after letting things settle */
 
-        timeout = jiffies+10;
+        timeout = jiffies+(HZ/10);
         while (timeout >= jiffies)
                 ;
         
@@ -986,11 +1036,11 @@ check_wild_interrupts(void)
  * 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 @@ get_auto_irq(unsigned char *address)
      */
     cy_irq_triggered = 0;
     cli();
-        base_addr[CyCAR<<index] = 0;
+        cy_writeb((u_long)base_addr+(CyCAR<<index), 0);
         cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index);
-        base_addr[CySRER<<index] |= CyTxMpty;
+        cy_writeb((u_long)base_addr+(CySRER<<index), 
+             cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
         probe_ready = 1;
     sti();
     
-    timeout = jiffies+2;
+    timeout = jiffies+(HZ/50);
     while (timeout >= jiffies) {
         if (cy_irq_triggered)
             break;
@@ -1021,7 +1072,7 @@ get_auto_irq(unsigned char *address)
  * 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 @@ cy_probe(int irq, void *dev_id, struct pt_regs *regs)
   int index = 0;        /* probing interrupts is only for ISA */
 
     if (!probe_ready) {
-        *(intr_base_addr + (Cy_ClrIntr<<index)) = 0;
+        cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
         return;
     }
 
     cy_irq_triggered = irq;
     cy_triggered |= 1 << irq;
 
-        if(intr_base_addr[CySVRR<<index] != 0) {
-            save_xir = (u_char) intr_base_addr[CyTIR<<index];
-            save_car = intr_base_addr[CyCAR<<index];
+        if(cy_readb(intr_base_addr+(CySVRR<<index)) != 0) {
+            save_xir = (u_char) cy_readb(intr_base_addr+(CyTIR<<index));
+            save_car = cy_readb(intr_base_addr+(CyCAR<<index));
             if ((save_xir & 0x3) != 0){
                 SP("channel ");
                 CP8(save_xir);
                 SP(" requesting unexpected interrupt\n");
             }
-            intr_base_addr[CyCAR<<index] = (save_xir & 0x3);
-            intr_base_addr[CySRER<<index] &= ~CyTxMpty;
-            intr_base_addr[CyTIR<<index] = (save_xir & 0x3f);
-            intr_base_addr[CyCAR<<index] = (save_car);
+            cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_xir & 0x3));
+            cy_writeb((u_long)intr_base_addr+(CySRER<<index),
+                cy_readb(intr_base_addr+(CySRER<<index)) & ~CyTxMpty);
+            cy_writeb((u_long)intr_base_addr+(CyTIR<<index), (save_xir & 0x3f));
+            cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_car));
         }
-        *(intr_base_addr + (Cy_ClrIntr<<index)) = 0;
+        cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
                                  /* Cy_ClrIntr is 0x1800 */
     return;
 } /* cy_probe */
@@ -1105,7 +1157,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
   int chip;
   int save_xir, channel, save_car;
   char data;
-  int char_count;
+  volatile int char_count;
   int outch;
   int i,j,index;
   int too_many;
@@ -1114,12 +1166,16 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
   int mdm_status;
 
     if((cinfo = IRQ_cards[irq]) == 0){
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: spurious interrupt %d\n\r", irq);
+#endif
         return; /* spurious interrupt */
     }
 
     card_base_addr = (unsigned char *)cinfo->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 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
             base_addr = (unsigned char *)
                       (cinfo->base_addr + (cy_chip_offset[chip]<<index));
             too_many = 0;
-            while ( (status = base_addr[CySVRR<<index]) != 0x00) {
+            while ( (status = cy_readb(base_addr+(CySVRR<<index))) != 0x00) {
                 had_work++;
                 /* The purpose of the following test is to ensure that
                    no chip can monopolize the driver.  This forces the
@@ -1142,31 +1198,34 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                     break;
                 }
                 if (status & CySRReceive) { /* reception interrupt */
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: rcvd intr, chip %d\n\r", chip);
+#endif
                     /* determine the channel & change to that context */
-                    save_xir = (u_char) base_addr[CyRIR<<index];
+                    save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     i = channel + chip * 4 + cinfo->first_line;
                     info = &cy_port[i];
                     info->last_active = jiffies;
-                    save_car = base_addr[CyCAR<<index];
-                    base_addr[CyCAR<<index] = save_xir;
+                    save_car = cy_readb(base_addr+(CyCAR<<index));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
 
                     /* if there is nowhere to put the data, discard it */
                     if(info->tty == 0){
-                        j = (base_addr[CyRIVR<<index] & CyIVRMask);
+                        j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
                         if ( j == CyIVRRxEx ) { /* exception */
-                            data = base_addr[CyRDSR<<index];
+                            data = cy_readb(base_addr+(CyRDSR<<index));
                         } else { /* normal character reception */
-                            char_count = base_addr[CyRDCR<<index];
+                            char_count = cy_readb(base_addr+(CyRDCR<<index));
                             while(char_count--){
-                                data = base_addr[CyRDSR<<index];
+                                data = cy_readb(base_addr+(CyRDSR<<index));
                             }
                         }
                     }else{ /* there is an open port for this data */
                         tty = info->tty;
-                        j = (base_addr[CyRIVR<<index] & CyIVRMask);
+                        j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
                         if ( j == CyIVRRxEx ) { /* exception */
-                            data = base_addr[CyRDSR<<index];
+                            data = cy_readb(base_addr+(CyRDSR<<index));
                             if(data & info->ignore_status_mask){
                                 continue;
                             }
@@ -1177,7 +1236,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                         *tty->flip.flag_buf_ptr++ =
                                                            TTY_BREAK;
                                         *tty->flip.char_buf_ptr++ =
-                                               base_addr[CyRDSR<<index];
+                                         cy_readb(base_addr+(CyRDSR<<index));
                                         if (info->flags & ASYNC_SAK){
                                             do_SAK(tty);
                                         }
@@ -1185,12 +1244,12 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                         *tty->flip.flag_buf_ptr++ =
                                                            TTY_FRAME;
                                         *tty->flip.char_buf_ptr++ =
-                                               base_addr[CyRDSR<<index];
+                                         cy_readb(base_addr+(CyRDSR<<index));
                                     }else if(data & CyPARITY){
                                         *tty->flip.flag_buf_ptr++ =
                                                            TTY_PARITY;
                                         *tty->flip.char_buf_ptr++ =
-                                               base_addr[CyRDSR<<index];
+                                         cy_readb(base_addr+(CyRDSR<<index));
                                     }else if(data & CyOVERRUN){
                                         *tty->flip.flag_buf_ptr++ =
                                                            TTY_OVERRUN;
@@ -1204,8 +1263,8 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                             tty->flip.count++;
                                             *tty->flip.flag_buf_ptr++ =
                                                             TTY_NORMAL;
-                                            *tty->flip.char_buf_ptr++ =
-                                               base_addr[CyRDSR<<index];
+                                           *tty->flip.char_buf_ptr++ =
+                                           cy_readb(base_addr+(CyRDSR<<index));
                                         }
                                     /* These two conditions may imply */
                                     /* a normal read should be done. */
@@ -1226,7 +1285,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                             }
                         } else { /* normal character reception */
                             /* load # chars available from the chip */
-                            char_count = base_addr[CyRDCR<<index];
+                            char_count = cy_readb(base_addr+(CyRDCR<<index));
 
 #ifdef CYCLOM_ENABLE_MONITORING
                             ++info->mon.int_count;
@@ -1240,7 +1299,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                         break;
                                 }
                                 tty->flip.count++;
-                                data = base_addr[CyRDSR<<index];
+                                data = cy_readb(base_addr+(CyRDSR<<index));
                                 *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
                                 *tty->flip.char_buf_ptr++ = data;
 #ifdef CYCLOM_16Y_HACK
@@ -1251,8 +1310,8 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                         queue_task(&tty->flip.tqueue, &tq_timer);
                     }
                     /* end of service */
-                    base_addr[CyRIR<<index] = (save_xir & 0x3f);
-                    base_addr[CyCAR<<index] = (save_car);
+                    cy_writeb((u_long)base_addr+(CyRIR<<index), (save_xir & 0x3f));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
                 }
 
 
@@ -1260,23 +1319,28 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                     /* Since we only get here when the transmit buffer
                        is empty, we know we can always stuff a dozen
                        characters. */
+#ifdef CY_DEBUG_INTERRUPTS
+printk("cy_interrupt: xmit intr, chip %d\n\r", chip);
+#endif
 
                     /* determine the channel & change to that context */
-                    save_xir = (u_char) base_addr[CyTIR<<index];
+                    save_xir = (u_char) cy_readb(base_addr+(CyTIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     i = channel + chip * 4 + cinfo->first_line;
-                    save_car = base_addr[CyCAR<<index];
-                    base_addr[CyCAR<<index] = save_xir;
+                    save_car = cy_readb(base_addr+(CyCAR<<index));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
 
                     /* validate the port# (as configured and open) */
                     if( (i < 0) || (NR_PORTS <= i) ){
-                        base_addr[CySRER<<index] &= ~CyTxMpty;
+                        cy_writeb((u_long)base_addr+(CySRER<<index),
+                             cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                         goto txend;
                     }
                     info = &cy_port[i];
                     info->last_active = jiffies;
                     if(info->tty == 0){
-                        base_addr[CySRER<<index] &= ~CyTxMpty;
+                        cy_writeb((u_long)base_addr+(CySRER<<index),
+                             cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                         goto txdone;
                     }
 
@@ -1286,7 +1350,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
                     if(info->x_char) { /* send special char */
                         outch = info->x_char;
-                        base_addr[CyTDR<<index] = outch;
+                        cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
                         char_count--;
                         info->x_char = 0;
                     }
@@ -1300,29 +1364,44 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                            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<<index] = 0; /* start break */
-                        base_addr[CyTDR<<index] = 0x81;
-                        base_addr[CyTDR<<index] = 0; /* delay a bit */
-                        base_addr[CyTDR<<index] = 0x82;
-                        base_addr[CyTDR<<index] = info->x_break*200/HZ;
-                        base_addr[CyTDR<<index] = 0; /* finish break */
-                        base_addr[CyTDR<<index] = 0x83;
+                       /* start break */
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x81);
+                       /* delay a bit */
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x82);
+                        if (cy_readb(base_addr + (CyGFRCR<<index)) >= 0x48 ) {
+                           /* It is a CD1400 rev. J or later */
+                            cy_writeb((u_long)base_addr + (CyTDR<<index), 
+                                     info->x_break*500/HZ);
+                       } else {
+                            cy_writeb((u_long)base_addr + (CyTDR<<index), 
+                                     info->x_break*200/HZ);
+                       }
+                       /* finish break */
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0); 
+                        cy_writeb((u_long)base_addr + (CyTDR<<index), 0x83);
                         char_count -= 7;
                         info->x_break = 0;
                     }
 
                     while (char_count-- > 0){
                         if (!info->xmit_cnt){
-                            base_addr[CySRER<<index] &= ~CyTxMpty;
+                            cy_writeb((u_long)base_addr+(CySRER<<index),
+                               cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                             goto txdone;
                         }
                         if (info->xmit_buf == 0){
-                            base_addr[CySRER<<index] &= ~CyTxMpty;
+                            cy_writeb((u_long)base_addr+(CySRER<<index),
+                               cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                             goto txdone;
                         }
                         if (info->tty->stopped || info->tty->hw_stopped){
-                            base_addr[CySRER<<index] &= ~CyTxMpty;
+                            cy_writeb((u_long)base_addr+(CySRER<<index),
+                               cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
                             goto txdone;
                         }
                         /* Because the Embedded Transmit Commands have
@@ -1340,15 +1419,16 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                         if( outch ){
                             info->xmit_cnt--;
                             info->xmit_tail = (info->xmit_tail + 1)
-                                                      & (PAGE_SIZE - 1);
-                            base_addr[CyTDR<<index] = outch;
+                                                      & (SERIAL_XMIT_SIZE - 1);
+                            cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
                         }else{
                             if(char_count > 1){
                                 info->xmit_cnt--;
                                 info->xmit_tail = (info->xmit_tail + 1)
-                                                     & (PAGE_SIZE - 1);
-                                base_addr[CyTDR<<index] = outch;
-                                base_addr[CyTDR<<index] = 0;
+                                                     & (SERIAL_XMIT_SIZE - 1);
+                                cy_writeb((u_long)base_addr+(CyTDR<<index), 
+                                         outch);
+                                cy_writeb((u_long)base_addr+(CyTDR<<index), 0);
                                 char_count--;
                             }else{
                             }
@@ -1362,23 +1442,24 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
         txend:
                     /* end of service */
-                    base_addr[CyTIR<<index] = (save_xir & 0x3f);
-                    base_addr[CyCAR<<index] = (save_car);
+                    cy_writeb((u_long)base_addr+(CyTIR<<index), 
+                             (save_xir & 0x3f));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
                 }
 
                 if (status & CySRModem) {        /* modem interrupt */
 
                     /* determine the channel & change to that context */
-                    save_xir = (u_char) base_addr[CyMIR<<index];
+                    save_xir = (u_char) cy_readb(base_addr+(CyMIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     info = &cy_port[channel + chip * 4
                                           + cinfo->first_line];
                     info->last_active = jiffies;
-                    save_car = base_addr[CyCAR<<index];
-                    base_addr[CyCAR<<index] = save_xir;
+                    save_car = cy_readb(base_addr+(CyCAR<<index));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
 
-                    mdm_change = base_addr[CyMISR<<index];
-                    mdm_status = base_addr[CyMSVR1<<index];
+                    mdm_change = cy_readb(base_addr+(CyMISR<<index));
+                    mdm_status = cy_readb(base_addr+(CyMSVR1<<index));
 
                     if(info->tty == 0){/* no place for data, ignore it*/
                         ;
@@ -1403,7 +1484,9 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                     /* cy_start isn't used
                                         because... !!! */
                                     info->tty->hw_stopped = 0;
-                                    base_addr[CySRER<<index] |= CyTxMpty;
+                                  cy_writeb((u_long)base_addr+(CySRER<<index),
+                                       cy_readb(base_addr+(CySRER<<index)) | 
+                                       CyTxMpty);
                                     cy_sched_event(info,
                                        Cy_EVENT_WRITE_WAKEUP);
                                 }
@@ -1412,8 +1495,9 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                     /* cy_stop isn't used
                                         because ... !!! */
                                     info->tty->hw_stopped = 1;
-                                    base_addr[CySRER<<index] &=
-                                                           ~CyTxMpty;
+                                  cy_writeb((u_long)base_addr+(CySRER<<index),
+                                       cy_readb(base_addr+(CySRER<<index)) & 
+                                       ~CyTxMpty);
                                 }
                             }
                         }
@@ -1423,15 +1507,16 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                         }
                     }
                     /* end of service */
-                    base_addr[CyMIR<<index] = (save_xir & 0x3f);
-                    base_addr[CyCAR<<index] = save_car;
+                    cy_writeb((u_long)base_addr+(CyMIR<<index), 
+                             (save_xir & 0x3f));
+                    cy_writeb((u_long)base_addr+(CyCAR<<index), save_car);
                 }
             }          /* end while status != 0 */
         }            /* end loop for chips... */
     } while(had_work);
 
    /* clear interrupts */
-   *(card_base_addr + (Cy_ClrIntr<<index)) = 0;
+   cy_writeb((u_long)card_base_addr + (Cy_ClrIntr<<index), 0);
                                 /* Cy_ClrIntr is 0x1800 */
 
 } /* cyy_interrupt */
@@ -1444,7 +1529,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
 static int
 cyz_fetch_msg( 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;
@@ -1456,17 +1541,17 @@ cyz_fetch_msg( struct cyclades_card *cinfo,
        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;
 
-    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 @@ cyz_fetch_msg( struct cyclades_card *cinfo,
 
 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 @@ cyz_issue_cmd( struct cyclades_card *cinfo,
        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 @@ cyz_update_channel( struct cyclades_card *cinfo,
     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 @@ cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 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, &param) == 1){
+       while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1){
            char_count = 0;
            info = &cy_port[ channel + cinfo->first_line ];
             if((tty = info->tty) == 0) continue;
@@ -1606,9 +1702,9 @@ cyz_poll(unsigned long arg)
                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 @@ cyz_poll(unsigned long arg)
            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 @@ cyz_poll(unsigned long arg)
                                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 @@ cyz_poll(unsigned long arg)
                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 @@ cyz_poll(unsigned long arg)
 #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 @@ cyz_poll(unsigned long arg)
                   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 @@ cyz_poll(unsigned long arg)
                        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 @@ cyz_poll(unsigned long arg)
 #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 @@ cyz_poll(unsigned long arg)
                }
 #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 @@ cyz_poll(unsigned long arg)
                    data = info->xmit_buf[info->xmit_tail];
                    info->xmit_cnt--;
                    info->xmit_tail =
-                       (info->xmit_tail + 1) & (PAGE_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;
+                       (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
+
+                   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 @@ startup(struct cyclades_port * info)
 #endif
 
        save_flags(flags); cli();
-           base_addr[CyCAR<<index] = (u_char)channel;
+           cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
 
-           base_addr[CyRTPR<<index] = (info->default_timeout
+           cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->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<<index] =
-                        (u_char)channel; /* !!! Is this needed? */
-           base_addr[CyMSVR1<<index] = CyRTS;
-           base_addr[CyMSVR2<<index] = CyDTR;
+           cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+           cy_writeb((ulong)base_addr+(CyMSVR1<<index), CyRTS);
+           cy_writeb((ulong)base_addr+(CyMSVR2<<index), CyDTR);
 
 #ifdef SERIAL_DEBUG_DTR
            printk("cyc:startup raising DTR\n");
            printk("     status: 0x%x, 0x%x\n",
-                  base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+                  cy_readb(base_addr+(CyMSVR1<<index)), 
+                   cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 
-           base_addr[CySRER<<index] |= CyRxData;
+           cy_writeb((u_long)base_addr+(CySRER<<index),
+               cy_readb(base_addr+(CySRER<<index)) | CyRxData);
            info->flags |= ASYNC_INITIALIZED;
 
            if (info->tty){
@@ -1894,7 +2011,7 @@ startup(struct cyclades_port * info)
 
        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 @@ startup(struct cyclades_port * info)
             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 @@ startup(struct cyclades_port * info)
 
        /* 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 @@ start_xmit( struct cyclades_port *info )
                       + (cy_chip_offset[chip]<<index));
 
        save_flags(flags); cli();
-           base_addr[CyCAR<<index] = channel;
-           base_addr[CySRER<<index] |= CyTxMpty;
+           cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
+           cy_writeb((u_long)base_addr+(CySRER<<index), 
+               cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
        restore_flags(flags);
     } else {
        /* Don't have to do anything at this time */
@@ -2012,14 +2130,15 @@ shutdown(struct cyclades_port * info)
                free_page((unsigned long) temp);
            }
 
-           base_addr[CyCAR<<index] = (u_char)channel;
+           cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
            if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
-               base_addr[CyMSVR1<<index] = ~CyRTS;
-               base_addr[CyMSVR2<<index] = ~CyDTR;
+               cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+               cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
 #ifdef SERIAL_DEBUG_DTR
                printk("cyc shutdown dropping DTR\n");
                printk("     status: 0x%x, 0x%x\n",
-                   base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+                   cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
            }
            cyy_issue_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index);
@@ -2051,10 +2170,11 @@ shutdown(struct cyclades_port * info)
 
        zfw_ctrl =
            (struct ZFW_CTRL *)
-               (cy_card[card].base_addr + firm_id->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 @@ shutdown(struct cyclades_port * info)
                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 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
        while (1) {
            save_flags(flags); cli();
                if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
-                   base_addr[CyCAR<<index] = (u_char)channel;
-                   base_addr[CyMSVR1<<index] = CyRTS;
-                   base_addr[CyMSVR2<<index] = CyDTR;
+                   cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                   cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                   cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
 #ifdef SERIAL_DEBUG_DTR
                    printk("cyc:block_til_ready raising DTR\n");
                    printk("     status: 0x%x, 0x%x\n",
-                       base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+                       cy_readb(base_addr+(CyMSVR1<<index)), 
+                        cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
                }
            restore_flags(flags);
@@ -2208,16 +2330,16 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
                break;
            }
            save_flags(flags); cli();
-               base_addr[CyCAR<<index] = (u_char)channel;
+               cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
                if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
                && !(info->flags & ASYNC_CLOSING)
                && (C_CLOCAL(tty)
-                   || (base_addr[CyMSVR1<<index] & CyDCD))) {
+                   || (cy_readb(base_addr+(CyMSVR1<<index)) & CyDCD))) {
                        restore_flags(flags);
                        break;
                }
            restore_flags(flags);
-           if (signal_pending(current)) {
+           if (current->signal & ~current->blocked) {
                retval = -ERESTARTSYS;
                break;
            }
@@ -2243,13 +2365,13 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 
        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 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
            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 @@ cy_open(struct tty_struct *tty, struct file * filp)
     */
     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 @@ cy_open(struct tty_struct *tty, struct file * filp)
 #ifdef SERIAL_DEBUG_OPEN
     printk(" cyc:cy_open done\n");/**/
 #endif
+
     return 0;
 } /* cy_open */
 
@@ -2603,13 +2726,13 @@ cy_put_char(struct tty_struct *tty, unsigned char ch)
         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_flush_chars(struct tty_struct *tty)
                       + (cy_chip_offset[chip]<<index));
 
        save_flags(flags); cli();
-           base_addr[CyCAR<<index] = channel;
-           base_addr[CySRER<<index] |= CyTxMpty;
+           cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
+           cy_writeb((u_long)base_addr+(CySRER<<index),
+               cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
        restore_flags(flags);
     } else {
        /* Since polling is already in place,
@@ -2680,7 +2804,7 @@ cy_write_room(struct tty_struct *tty)
 
     if (serial_paranoia_check(info, tty->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 @@ set_line_char(struct cyclades_port * info)
   unsigned cflag;
   int   i;
 
+
     if (!info->tty || !info->tty->termios){
         return;
     }
@@ -2736,6 +2861,9 @@ set_line_char(struct cyclades_port * info)
     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 @@ set_line_char(struct cyclades_port * info)
                i = 16;
            else if(i == B115200) 
                i = 18;
+           else if(i == B230400 && 
+                 cy_readb(cy_card[card].base_addr+(CyGFRCR<<index)) >= 0x48) {
+               /* It is a CD1400 rev. J or later */
+               i = 20;
+           }
 #ifdef B76800
            else if(i == B76800) 
                i = 17;
@@ -2753,15 +2886,35 @@ set_line_char(struct cyclades_port * info)
        }
 
        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;
+           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<<index)) >= 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 */
        }
-       info->tbpr = baud_bpr[i]; /* Tx BPR */
-       info->tco = baud_co[i]; /* Tx CO */
-       info->rbpr = baud_bpr[i]; /* Rx BPR */
-       info->rco = baud_co[i]; /* Rx CO */
        if (baud_table[i] == 134) {
            info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
            /* get it right for 134.5 baud */
@@ -2834,70 +2987,94 @@ set_line_char(struct cyclades_port * info)
 
        chip = channel>>2;
        channel &= 0x03;
-       index = cy_card[card].bus_index;
        base_addr = (unsigned char*)
                       (cy_card[card].base_addr
                       + (cy_chip_offset[chip]<<index));
 
        save_flags(flags); cli();
-           base_addr[CyCAR<<index] = (u_char)channel;
+           cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
 
           /* tx and rx baud rate */
 
-           base_addr[CyTCOR<<index] = info->tco;
-           base_addr[CyTBPR<<index] = info->tbpr;
-           base_addr[CyRCOR<<index] = info->rco;
-           base_addr[CyRBPR<<index] = info->rbpr;
+           cy_writeb((u_long)base_addr+(CyTCOR<<index), info->tco);
+           cy_writeb((u_long)base_addr+(CyTBPR<<index), info->tbpr);
+           cy_writeb((u_long)base_addr+(CyRCOR<<index), info->rco);
+           cy_writeb((u_long)base_addr+(CyRBPR<<index), info->rbpr);
 
            /* set line characteristics  according configuration */
 
-           base_addr[CySCHR1<<index] = START_CHAR(info->tty);
-           base_addr[CySCHR2<<index] = STOP_CHAR(info->tty);
-           base_addr[CyCOR1<<index] = info->cor1;
-           base_addr[CyCOR2<<index] = info->cor2;
-           base_addr[CyCOR3<<index] = info->cor3;
-           base_addr[CyCOR4<<index] = info->cor4;
-           base_addr[CyCOR5<<index] = info->cor5;
+           cy_writeb((u_long)base_addr+(CySCHR1<<index), 
+                     START_CHAR(info->tty));
+           cy_writeb((u_long)base_addr+(CySCHR2<<index), 
+                     STOP_CHAR(info->tty));
+           cy_writeb((u_long)base_addr+(CyCOR1<<index), info->cor1);
+           cy_writeb((u_long)base_addr+(CyCOR2<<index), info->cor2);
+           cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
+           cy_writeb((u_long)base_addr+(CyCOR4<<index), info->cor4);
+           cy_writeb((u_long)base_addr+(CyCOR5<<index), info->cor5);
 
            cyy_issue_cmd(base_addr,
                     CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index);
 
-           base_addr[CyCAR<<index] =
-                               (u_char)channel; /* !!! Is this needed? */
-
-           base_addr[CyRTPR<<index] = (info->default_timeout
-                                        ? info->default_timeout
-                                        : 0x02); /* 10ms rx timeout */
+           cy_writeb((u_long)base_addr+(CyCAR<<index), 
+                     (u_char)channel); /* !!! Is this needed? */
+           cy_writeb((u_long)base_addr+(CyRTPR<<index), (info->default_timeout
+                                                ? info->default_timeout
+                                                : 0x02)); /* 10ms rx timeout */
 
            if (C_CLOCAL(info->tty)) {
-               base_addr[CySRER<<index] |= CyMdmCh; /* without modem intr */
+               /* without modem intr */
+               cy_writeb((u_long)base_addr+(CySRER<<index),
+                   cy_readb(base_addr+(CySRER<<index)) | CyMdmCh); 
                                        /* act on 1->0 modem transitions */
-               base_addr[CyMCOR1<<index] = CyCTS;
+                if ((cflag & CRTSCTS) && info->rflow) {
+                        cy_writeb((u_long)base_addr+(CyMCOR1<<index), 
+                                  (CyCTS|rflow_thr[i]));
+                } else {
+                        cy_writeb((u_long)base_addr+(CyMCOR1<<index), CyCTS);
+                }
                                        /* act on 0->1 modem transitions */
-               base_addr[CyMCOR2<<index] = CyCTS;
+               cy_writeb((u_long)base_addr+(CyMCOR2<<index), CyCTS);
            } else {
-               base_addr[CySRER<<index] |= CyMdmCh; /* with modem intr */
+               /* without modem intr */
+               cy_writeb((u_long)base_addr+(CySRER<<index),
+                   cy_readb(base_addr+(CySRER<<index)) | CyMdmCh); 
                                        /* act on 1->0 modem transitions */
-               base_addr[CyMCOR1<<index] = CyDSR|CyCTS|CyRI|CyDCD;
+                if ((cflag & CRTSCTS) && info->rflow) {
+                       cy_writeb((u_long)base_addr+(CyMCOR1<<index), 
+                                 (CyDSR|CyCTS|CyRI|CyDCD|rflow_thr[i]));
+                } else {
+                       cy_writeb((u_long)base_addr+(CyMCOR1<<index), 
+                                  CyDSR|CyCTS|CyRI|CyDCD);
+                }
                                        /* act on 0->1 modem transitions */
-               base_addr[CyMCOR2<<index] = CyDSR|CyCTS|CyRI|CyDCD;
+               cy_writeb((u_long)base_addr+(CyMCOR2<<index), 
+                         CyDSR|CyCTS|CyRI|CyDCD);
            }
 
            if(i == 0){ /* baud rate is zero, turn off line */
-               base_addr[CyMSVR2<<index] = ~CyDTR;
+               if (info->rtsdtr_inv) {
+                       cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+               } else {
+                        cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+               }
 #ifdef SERIAL_DEBUG_DTR
                printk("cyc:set_line_char dropping DTR\n");
                printk("     status: 0x%x,
-                   0x%x\n", base_addr[CyMSVR1<<index],
-                   base_addr[CyMSVR2<<index]);
+                   0x%x\n", cy_readb(base_addr+(CyMSVR1<<index)),
+                   cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
            }else{
-               base_addr[CyMSVR2<<index] = CyDTR;
+                if (info->rtsdtr_inv) {
+                       cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                } else {
+                       cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                }
 #ifdef SERIAL_DEBUG_DTR
                printk("cyc:set_line_char raising DTR\n");
                printk("     status: 0x%x, 0x%x\n",
-                   base_addr[CyMSVR1<<index],
-                   base_addr[CyMSVR2<<index]);
+                   cy_readb(base_addr+(CyMSVR1<<index)),
+                   cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
            }
 
@@ -2920,82 +3097,84 @@ set_line_char(struct cyclades_port * info)
            return;
        }
 
-       zfw_ctrl =
-            (struct ZFW_CTRL *)
-                (cy_card[card].base_addr + firm_id->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 @@ set_line_char(struct cyclades_port * info)
        }
 
        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 @@ set_line_char(struct cyclades_port * info)
            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 @@ get_modem_info(struct cyclades_port * info, unsigned int *value)
                       + (cy_chip_offset[chip]<<index));
 
        save_flags(flags); cli();
-           base_addr[CyCAR<<index] = (u_char)channel;
-           status = base_addr[CyMSVR1<<index];
-           status |= base_addr[CyMSVR2<<index];
+           cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+           status = cy_readb(base_addr+(CyMSVR1<<index));
+           status |= cy_readb(base_addr+(CyMSVR2<<index));
        restore_flags(flags);
 
        result =  ((status  & CyRTS) ? TIOCM_RTS : 0)
@@ -3152,12 +3334,11 @@ get_modem_info(struct cyclades_port * info, unsigned int *value)
        firm_id = (struct FIRM_ID *)
                    (cy_card[card].base_addr + ID_ADDRESS);
         if (ISZLOADED(cy_card[card])) {
-           zfw_ctrl =
-               (struct ZFW_CTRL *)
-               (cy_card[card].base_addr + firm_id->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 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
        case TIOCMBIS:
            if (arg & TIOCM_RTS){
                save_flags(flags); cli();
-                   base_addr[CyCAR<<index] = (u_char)channel;
-                   base_addr[CyMSVR1<<index] = CyRTS;
+               cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+                   cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                } else {
+                   cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                }
                restore_flags(flags);
            }
            if (arg & TIOCM_DTR){
                save_flags(flags); cli();
-               base_addr[CyCAR<<index] = (u_char)channel;
-               base_addr[CyMSVR2<<index] = CyDTR;
+               cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+                   cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                } else {
+                   cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                }
 #ifdef SERIAL_DEBUG_DTR
                printk("cyc:set_modem_info raising DTR\n");
                printk("     status: 0x%x, 0x%x\n",
-                   base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+                   cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
                restore_flags(flags);
            }
@@ -3222,18 +3412,28 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
        case TIOCMBIC:
            if (arg & TIOCM_RTS){
                save_flags(flags); cli();
-                   base_addr[CyCAR<<index] = (u_char)channel;
-                   base_addr[CyMSVR1<<index] = ~CyRTS;
+               cy_writeb((u_long)base_addr+(CyCAR<<index), 
+                          (u_char)channel);
+                if (info->rtsdtr_inv) {
+                       cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                } else {
+                       cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                }
                restore_flags(flags);
            }
            if (arg & TIOCM_DTR){
                save_flags(flags); cli();
-               base_addr[CyCAR<<index] = (u_char)channel;
-               base_addr[CyMSVR2<<index] = ~CyDTR;
+               cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+                       cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                } else {
+                       cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                }
 #ifdef SERIAL_DEBUG_DTR
                printk("cyc:set_modem_info dropping DTR\n");
                printk("     status: 0x%x, 0x%x\n",
-                   base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+                   cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
                restore_flags(flags);
            }
@@ -3241,33 +3441,52 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
        case TIOCMSET:
            if (arg & TIOCM_RTS){
                save_flags(flags); cli();
-                   base_addr[CyCAR<<index] = (u_char)channel;
-                   base_addr[CyMSVR1<<index] = CyRTS;
+               cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+                       cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                } else {
+                       cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                }
                restore_flags(flags);
            }else{
                save_flags(flags); cli();
-                   base_addr[CyCAR<<index] = (u_char)channel;
-                   base_addr[CyMSVR1<<index] = ~CyRTS;
+               cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+                       cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                } else {
+                       cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                }
                restore_flags(flags);
            }
            if (arg & TIOCM_DTR){
                save_flags(flags); cli();
-               base_addr[CyCAR<<index] = (u_char)channel;
-               base_addr[CyMSVR2<<index] = CyDTR;
+               cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+                       cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+                } else {
+                       cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+                }
 #ifdef SERIAL_DEBUG_DTR
                printk("cyc:set_modem_info raising DTR\n");
                printk("     status: 0x%x, 0x%x\n",
-                   base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+                   cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
                restore_flags(flags);
            }else{
                save_flags(flags); cli();
-               base_addr[CyCAR<<index] = (u_char)channel;
-               base_addr[CyMSVR2<<index] = ~CyDTR;
+               cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+                if (info->rtsdtr_inv) {
+                       cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+                } else {
+                       cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+                }
+
 #ifdef SERIAL_DEBUG_DTR
                printk("cyc:set_modem_info dropping DTR\n");
                printk("     status: 0x%x, 0x%x\n",
-                   base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
+                   cy_readb(base_addr+(CyMSVR1<<index)), 
+                    cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
                restore_flags(flags);
            }
@@ -3281,19 +3500,20 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
        firm_id = (struct FIRM_ID *)
                    (cy_card[card].base_addr + ID_ADDRESS);
         if (ISZLOADED(cy_card[card])) {
-           zfw_ctrl =
-               (struct ZFW_CTRL *)
-               (cy_card[card].base_addr + firm_id->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 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
                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 @@ set_modem_info(struct cyclades_port * info, unsigned int cmd,
                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 @@ send_break( struct cyclades_port * info, int duration)
           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 @@ set_threshold(struct cyclades_port * info, unsigned long value)
 
        info->cor3 &= ~CyREC_FIFO;
        info->cor3 |= value & CyREC_FIFO;
-       base_addr[CyCOR3<<index] = info->cor3;
+       cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
        cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index);
     } else {
        // Nothing to do!
@@ -3434,7 +3660,7 @@ get_threshold(struct cyclades_port * info, unsigned long *value)
                       (cy_card[card].base_addr
                       + (cy_chip_offset[chip]<<index));
 
-       tmp = base_addr[CyCOR3<<index] & CyREC_FIFO;
+       tmp = cy_readb(base_addr+(CyCOR3<<index)) & CyREC_FIFO;
        cy_put_user(tmp,value);
     } else {
        // Nothing to do!
@@ -3475,7 +3701,7 @@ set_timeout(struct cyclades_port * info, unsigned long value)
                       (cy_card[card].base_addr
                       + (cy_chip_offset[chip]<<index));
 
-       base_addr[CyRTPR<<index] = value & 0xff;
+       cy_writeb((u_long)base_addr+(CyRTPR<<index), value & 0xff);
     } else {
        // Nothing to do!
     }
@@ -3500,7 +3726,7 @@ get_timeout(struct cyclades_port * info, unsigned long *value)
                       (cy_card[card].base_addr
                       + (cy_chip_offset[chip]<<index));
 
-       tmp = base_addr[CyRTPR<<index];
+       tmp = cy_readb(base_addr+(CyRTPR<<index));
        cy_put_user(tmp,value);
     } else {
        // Nothing to do!
@@ -3524,7 +3750,6 @@ get_default_timeout(struct cyclades_port * info, unsigned long *value)
     return 0;
 }/* get_default_timeout */
 
-
 /*
  * This routine allows the tty driver to implement device-
  * specific ioctl's.  If the ioctl number passed in cmd is
@@ -3601,6 +3826,26 @@ cy_ioctl(struct tty_struct *tty, struct file * file,
         case CYSETDEFTIMEOUT:
             ret_val = set_default_timeout(info, (unsigned long)arg);
             break;
+       case CYSETRFLOW:
+           info->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 @@ cy_throttle(struct tty_struct * tty)
 #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_throttle(struct tty_struct * tty)
                       + (cy_chip_offset[chip]<<index));
 
        save_flags(flags); cli();
-           base_addr[CyCAR<<index] = (u_char)channel;
-           base_addr[CyMSVR1<<index] = ~CyRTS;
+       cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+       if (info->rtsdtr_inv) {
+               cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
+       } else {
+               cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
+       }
        restore_flags(flags);
     } else {
        // Nothing to do!
@@ -3796,7 +4045,7 @@ cy_unthrottle(struct tty_struct * tty)
 #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
 
@@ -3820,8 +4069,12 @@ cy_unthrottle(struct tty_struct * tty)
                       + (cy_chip_offset[chip]<<index));
 
        save_flags(flags); cli();
-           base_addr[CyCAR<<index] = (u_char)channel;
-           base_addr[CyMSVR1<<index] = CyRTS;
+       cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
+       if (info->rtsdtr_inv) {
+               cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
+       } else {
+               cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
+       }
        restore_flags(flags);
     }else{
        // Nothing to do!
@@ -3861,9 +4114,10 @@ cy_stop(struct tty_struct *tty)
                            + (cy_chip_offset[chip]<<index));
 
         save_flags(flags); cli();
-            base_addr[CyCAR<<index] =
-               (u_char)(channel & 0x0003); /* index channel */
-            base_addr[CySRER<<index] &= ~CyTxMpty;
+            cy_writeb((u_long)base_addr+(CyCAR<<index),
+              (u_char)(channel & 0x0003)); /* index channel */
+            cy_writeb((u_long)base_addr+(CySRER<<index), 
+               cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
         restore_flags(flags);
     } else {
        // Nothing to do!
@@ -3900,8 +4154,10 @@ cy_start(struct tty_struct *tty)
                       + (cy_chip_offset[chip]<<index));
 
         save_flags(flags); cli();
-            base_addr[CyCAR<<index] = (u_char)(channel & 0x0003);
-            base_addr[CySRER<<index] |= CyTxMpty;
+            cy_writeb((u_long)base_addr+(CyCAR<<index),
+              (u_char)(channel & 0x0003)); /* index channel */
+            cy_writeb((u_long)base_addr+(CySRER<<index), 
+               cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
         restore_flags(flags);
     } else {
        // Nothing to do!
@@ -3968,24 +4224,25 @@ cy_flush_buffer(struct tty_struct *tty)
  * ---------------------------------------------------------------------
  */
 
-
 /* initialize chips on Cyclom-Y card -- return number of valid
    chips (which is number of ports/4) */
-__initfunc(static int
-cyy_init_card(unsigned char *true_base_addr,int index))
+__initfunc(static unsigned short
+cyy_init_card(volatile ucchar *true_base_addr,int index))
 {
   unsigned int chip_number;
-  unsigned char* base_addr;
+  volatile ucchar* base_addr;
 
-    true_base_addr[Cy_HwReset<<index] = 0; /* Cy_HwReset is 0x1400 */
-    true_base_addr[Cy_ClrIntr<<index] = 0; /* Cy_ClrIntr is 0x1800 */
+    cy_writeb((u_long)true_base_addr+(Cy_HwReset<<index), 0); 
+                                               /* Cy_HwReset is 0x1400 */
+    cy_writeb((u_long)true_base_addr+(Cy_ClrIntr<<index), 0); 
+                                               /* Cy_ClrIntr is 0x1800 */
     udelay(500L);
 
-    for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){
+    for(chip_number=0; chip_number<CyMAX_CHIPS_PER_CARD; chip_number++){
         base_addr = true_base_addr
                       + (cy_chip_offset[chip_number]<<index);
         udelay(1000L);
-        if(base_addr[CyCCR<<index] != 0x00){
+        if(cy_readb(base_addr+(CyCCR<<index)) != 0x00){
             /*************
             printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
                chip_number, (unsigned long)base_addr);
@@ -3993,7 +4250,7 @@ cyy_init_card(unsigned char *true_base_addr,int index))
             return chip_number;
         }
 
-        base_addr[CyGFRCR<<index] = 0;
+        cy_writeb((u_long)base_addr+(CyGFRCR<<index), 0);
         udelay(10L);
 
         /* The Cyclom-16Y does not decode address bit 9 and therefore
@@ -4003,16 +4260,16 @@ cyy_init_card(unsigned char *true_base_addr,int index))
            and this must be a Cyclom-16Y, not a Cyclom-32Ye.
         */
         if (chip_number == 4
-        && *(true_base_addr
+        && cy_readb(true_base_addr
            + (cy_chip_offset[0]<<index)
            + (CyGFRCR<<index)) == 0){
             return chip_number;
         }
 
-        base_addr[CyCCR<<index] = CyCHIP_RESET;
+        cy_writeb((u_long)base_addr+(CyCCR<<index), CyCHIP_RESET);
         udelay(1000L);
 
-        if(base_addr[CyGFRCR<<index] == 0x00){
+        if(cy_readb(base_addr+(CyGFRCR<<index)) == 0x00){
             /*
             printk(" chip #%d at %#6lx is not responding ",
                chip_number, (unsigned long)base_addr);
@@ -4020,7 +4277,7 @@ cyy_init_card(unsigned char *true_base_addr,int index))
             */
             return chip_number;
         }
-        if((0xf0 & base_addr[CyGFRCR<<index]) != 0x40){
+        if((0xf0 & (cy_readb(base_addr+(CyGFRCR<<index)))) != 0x40){
             /*
             printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n",
                chip_number, (unsigned long)base_addr,
@@ -4028,10 +4285,16 @@ cyy_init_card(unsigned char *true_base_addr,int index))
             */
             return chip_number;
         }
-        base_addr[CyGCR<<index] = CyCH0_SERIAL;
-        base_addr[CyPPR<<index] = 244;
-                              /* better value than CyCLOCK_25_1MS * 5
-                                         to run clock at 200 Hz */
+        cy_writeb((u_long)base_addr+(CyGCR<<index), CyCH0_SERIAL);
+        if (cy_readb(base_addr+(CyGFRCR<<index)) >= 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<<index), CyCLOCK_60_2MS);
+       } else {
+           /* f = 200 Hz */
+           cy_writeb((u_long)base_addr+(CyPPR<<index), CyCLOCK_25_5MS);
+       }
 
         /*
         printk(" chip #%d at %#6lx is rev 0x%2x\n",
@@ -4039,11 +4302,9 @@ cyy_init_card(unsigned char *true_base_addr,int index))
               base_addr[CyGFRCR<<index]);
         */
     }
-
     return chip_number;
 } /* cyy_init_card */
 
-
 /*
  * ---------------------------------------------------------------------
  * cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
@@ -4053,9 +4314,9 @@ cyy_init_card(unsigned char *true_base_addr,int index))
 __initfunc(static int
 cy_detect_isa(void))
 {
-  unsigned int          cy_isa_irq,nboard;
-  unsigned char         *cy_isa_address;
-  unsigned short        i,j,cy_isa_nchan;
+  unsigned short       cy_isa_irq,nboard;
+  volatile ucchar      *cy_isa_address;
+  unsigned short       i,j,cy_isa_nchan;
 
         nboard = 0;
 
@@ -4067,10 +4328,13 @@ cy_detect_isa(void))
                 }
 
                 /* probe for CD1400... */
-#if (LINUX_VERSION_CODE >= 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 @@ cy_detect_isa(void))
                 /* 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 @@ cy_detect_isa(void))
                         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 @@ cy_detect_isa(void))
                 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 @@ cy_detect_isa(void))
                 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 @@ cy_detect_pci(void))
   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 @@ cy_detect_pci(void))
                 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 @@ cy_detect_pci(void))
                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 @@ cy_detect_pci(void))
                         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 @@ cy_detect_pci(void))
                 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 @@ cy_detect_pci(void))
                                | 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 @@ cy_detect_pci(void))
                    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 @@ cy_detect_pci(void))
                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 @@ cy_detect_pci(void))
                    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 @@ cy_detect_pci(void))
               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_detect_pci(void))
                 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 @@ cy_detect_pci(void))
                         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 @@ cy_detect_pci(void))
                    {
                        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 @@ cy_detect_pci(void))
                 /* 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 @@ cy_detect_pci(void))
                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_detect_pci(void))
                 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 @@ cy_detect_pci(void))
                         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 @@ cy_detect_pci(void))
                    {
                        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 @@ cy_detect_pci(void))
                 /* 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 @@ show_version(void)
     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 @@ cy_init(void))
   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 @@ cy_init(void))
 
     /* look for isa boards */
     cy_isa_nboard = cy_detect_isa();
-
+    
     /* look for pci boards */
     cy_pci_nboard = cy_detect_pci();
 
@@ -4649,10 +4943,10 @@ cy_init(void))
     /* 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 @@ cy_init(void))
                     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 @@ cy_init(void))
                 }
                 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 @@ cy_init(void))
                     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<<index)) >= 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 @@ show_status(int line_num)
 
 /* Global Registers */
 
-        printk(" CyGFRCR %x\n", base_addr[CyGFRCR<<index]);
-        printk(" CyCAR %x\n", base_addr[CyCAR<<index]);
-        printk(" CyGCR %x\n", base_addr[CyGCR<<index]);
-        printk(" CySVRR %x\n", base_addr[CySVRR<<index]);
-        printk(" CyRICR %x\n", base_addr[CyRICR<<index]);
-        printk(" CyTICR %x\n", base_addr[CyTICR<<index]);
-        printk(" CyMICR %x\n", base_addr[CyMICR<<index]);
-        printk(" CyRIR %x\n", base_addr[CyRIR<<index]);
-        printk(" CyTIR %x\n", base_addr[CyTIR<<index]);
-        printk(" CyMIR %x\n", base_addr[CyMIR<<index]);
-        printk(" CyPPR %x\n", base_addr[CyPPR<<index]);
+        printk(" CyGFRCR %x\n", cy_readb(base_addr + CyGFRCR<<index));
+        printk(" CyCAR %x\n", cy_readb(base_addr + CyCAR<<index));
+        printk(" CyGCR %x\n", cy_readb(base_addr + CyGCR<<index));
+        printk(" CySVRR %x\n", cy_readb(base_addr + CySVRR<<index));
+        printk(" CyRICR %x\n", cy_readb(base_addr + CyRICR<<index));
+        printk(" CyTICR %x\n", cy_readb(base_addr + CyTICR<<index));
+        printk(" CyMICR %x\n", cy_readb(base_addr + CyMICR<<index));
+        printk(" CyRIR %x\n", cy_readb(base_addr + CyRIR<<index));
+        printk(" CyTIR %x\n", cy_readb(base_addr + CyTIR<<index));
+        printk(" CyMIR %x\n", cy_readb(base_addr + CyMIR<<index));
+        printk(" CyPPR %x\n", cy_readb(base_addr + CyPPR<<index));
 
-        base_addr[CyCAR<<index] = (u_char)channel;
+        cy_writeb(base_addr + CyCAR<<index, (u_char)channel);
 
 /* Virtual Registers */
 
-        printk(" CyRIVR %x\n", base_addr[CyRIVR<<index]);
-        printk(" CyTIVR %x\n", base_addr[CyTIVR<<index]);
-        printk(" CyMIVR %x\n", base_addr[CyMIVR<<index]);
-        printk(" CyMISR %x\n", base_addr[CyMISR<<index]);
+        printk(" CyRIVR %x\n", cy_readb(base_addr + CyRIVR<<index));
+        printk(" CyTIVR %x\n", cy_readb(base_addr + CyTIVR<<index));
+        printk(" CyMIVR %x\n", cy_readb(base_addr + CyMIVR<<index));
+        printk(" CyMISR %x\n", cy_readb(base_addr + CyMISR<<index));
 
 /* Channel Registers */
 
-        printk(" CyCCR %x\n", base_addr[CyCCR<<index]);
-        printk(" CySRER %x\n", base_addr[CySRER<<index]);
-        printk(" CyCOR1 %x\n", base_addr[CyCOR1<<index]);
-        printk(" CyCOR2 %x\n", base_addr[CyCOR2<<index]);
-        printk(" CyCOR3 %x\n", base_addr[CyCOR3<<index]);
-        printk(" CyCOR4 %x\n", base_addr[CyCOR4<<index]);
-        printk(" CyCOR5 %x\n", base_addr[CyCOR5<<index]);
-        printk(" CyCCSR %x\n", base_addr[CyCCSR<<index]);
-        printk(" CyRDCR %x\n", base_addr[CyRDCR<<index]);
-        printk(" CySCHR1 %x\n", base_addr[CySCHR1<<index]);
-        printk(" CySCHR2 %x\n", base_addr[CySCHR2<<index]);
-        printk(" CySCHR3 %x\n", base_addr[CySCHR3<<index]);
-        printk(" CySCHR4 %x\n", base_addr[CySCHR4<<index]);
-        printk(" CySCRL %x\n", base_addr[CySCRL<<index]);
-        printk(" CySCRH %x\n", base_addr[CySCRH<<index]);
-        printk(" CyLNC %x\n", base_addr[CyLNC<<index]);
-        printk(" CyMCOR1 %x\n", base_addr[CyMCOR1<<index]);
-        printk(" CyMCOR2 %x\n", base_addr[CyMCOR2<<index]);
-        printk(" CyRTPR %x\n", base_addr[CyRTPR<<index]);
-        printk(" CyMSVR1 %x\n", base_addr[CyMSVR1<<index]);
-        printk(" CyMSVR2 %x\n", base_addr[CyMSVR2<<index]);
-        printk(" CyRBPR %x\n", base_addr[CyRBPR<<index]);
-        printk(" CyRCOR %x\n", base_addr[CyRCOR<<index]);
-        printk(" CyTBPR %x\n", base_addr[CyTBPR<<index]);
-        printk(" CyTCOR %x\n", base_addr[CyTCOR<<index]);
+        printk(" CyCCR %x\n", cy_readb(base_addr + CyCCR<<index));
+        printk(" CySRER %x\n", cy_readb(base_addr + CySRER<<index));
+        printk(" CyCOR1 %x\n", cy_readb(base_addr + CyCOR1<<index));
+        printk(" CyCOR2 %x\n", cy_readb(base_addr + CyCOR2<<index));
+        printk(" CyCOR3 %x\n", cy_readb(base_addr + CyCOR3<<index));
+        printk(" CyCOR4 %x\n", cy_readb(base_addr + CyCOR4<<index));
+        printk(" CyCOR5 %x\n", cy_readb(base_addr + CyCOR5<<index));
+        printk(" CyCCSR %x\n", cy_readb(base_addr + CyCCSR<<index));
+        printk(" CyRDCR %x\n", cy_readb(base_addr + CyRDCR<<index));
+        printk(" CySCHR1 %x\n", cy_readb(base_addr + CySCHR1<<index));
+        printk(" CySCHR2 %x\n", cy_readb(base_addr + CySCHR2<<index));
+        printk(" CySCHR3 %x\n", cy_readb(base_addr + CySCHR3<<index));
+        printk(" CySCHR4 %x\n", cy_readb(base_addr + CySCHR4<<index));
+        printk(" CySCRL %x\n", cy_readb(base_addr + CySCRL<<index));
+        printk(" CySCRH %x\n", cy_readb(base_addr + CySCRH<<index));
+        printk(" CyLNC %x\n", cy_readb(base_addr + CyLNC<<index));
+        printk(" CyMCOR1 %x\n", cy_readb(base_addr + CyMCOR1<<index));
+        printk(" CyMCOR2 %x\n", cy_readb(base_addr + CyMCOR2<<index));
+        printk(" CyRTPR %x\n", cy_readb(base_addr + CyRTPR<<index));
+        printk(" CyMSVR1 %x\n", cy_readb(base_addr + CyMSVR1<<index));
+        printk(" CyMSVR2 %x\n", cy_readb(base_addr + CyMSVR2<<index));
+        printk(" CyRBPR %x\n", cy_readb(base_addr + CyRBPR<<index));
+        printk(" CyRCOR %x\n", cy_readb(base_addr + CyRCOR<<index));
+        printk(" CyTBPR %x\n", cy_readb(base_addr + CyTBPR<<index));
+        printk(" CyTCOR %x\n", cy_readb(base_addr + CyTCOR<<index));
 
     restore_flags(flags);
 } /* show_status */
index e85d36a9883bf9873ee959f3cb976c89eeaef693..ce949767c6d6d3e6035683550f47dd6100c0c69e 100644 (file)
@@ -47,6 +47,9 @@ VERSION INFO:
                                compatibility.
                             Better ioctl names. Kept binary compatibility.
                             Removed 'save_busy'. Just set busy to 1.
+11/03/97  Brian Gerst 0.9.1: Fixed bug which caused driver to always time out 
+                             but never report a timeout (broken while loop).
+                             Fixed js_read for new VFS code.
 */
 
 #include <linux/module.h>
@@ -227,20 +230,21 @@ static int js_release (struct inode *inode, struct file *file)
  *     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 @@ static long js_read (struct inode *inode, struct file *file, char *buf, unsigned
                /*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 @@ static long js_read (struct inode *inode, struct file *file, char *buf, unsigned
                                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 */
index 5699ab1795cb2323797f16d12089b679c5b6ae19..414ac948c2619b3744411f71fa8abd0a23b298ec 100644 (file)
@@ -76,6 +76,7 @@ extern void pcwatchdog_init(void);
 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
@@ -257,6 +258,9 @@ __initfunc(int misc_init(void))
 #ifdef CONFIG_NVRAM
        nvram_init();
 #endif
+#ifdef CONFIG_MISC_RADIO
+       radio_init();
+#endif
 #ifdef CONFIG_HFMODEM
        hfmodem_init();
 #endif
index 4029817495f76465d2d07dea75fd1bb36cf06c32..329ac7b326a2b73875745820d60d33316a94a31d 100644 (file)
@@ -227,11 +227,11 @@ static long long nvram_llseek(struct file *file,loff_t offset, int origin )
        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 @@ static long nvram_read( struct inode * inode, struct file * file,
 
        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 @@ static long nvram_write( struct inode * inode, struct file * file,
                nvram_write_int( c, i );
        }
        nvram_set_checksum_int();
-       file->f_pos = i;
+       *ppos = i;
 
        restore_flags(flags);
        return( tmp - buf );
index 2f8a5a75bffe5f41fcae9de4876f9892e395669f..d03f0b0c745d76f2aebdd850be3aac5063520670 100644 (file)
@@ -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 <linux/module.h>
@@ -222,7 +223,7 @@ static void pcwd_send_heartbeat(void)
 }
 
 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 @@ static int pcwd_ioctl(struct inode *inode, struct file *file,
        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 @@ static int pcwd_open(struct inode *ino, struct file *filep)
        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 @@ static struct file_operations pcwd_fops = {
        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 --git a/drivers/char/radio.c b/drivers/char/radio.c
new file mode 100644 (file)
index 0000000..2b79e98
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Radio Card Device Driver for Linux
+ *
+ * (c) 1997 Matthew Kirkwood <weejock@ferret.lmh.ox.ac.uk>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+
+#include <linux/miscdevice.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/config.h>
+
+#include <linux/radio.h>
+#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 --git a/drivers/char/rtrack.c b/drivers/char/rtrack.c
new file mode 100644 (file)
index 0000000..efe0104
--- /dev/null
@@ -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 <linux/config.h>
+#include <linux/radio.h>
+#include <linux/ioport.h>
+
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#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 --git a/drivers/char/rtrack.h b/drivers/char/rtrack.h
new file mode 100644 (file)
index 0000000..b5a9bc1
--- /dev/null
@@ -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 <linux/radio.h>
+
+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 */
index bb0c7b772e1c3643e2f78e4b88f9e692e7fe6d8e..a331f72d6e2f57d71f0cde421f0974cc06bdbb4e 100644 (file)
@@ -447,7 +447,7 @@ static int el16_send_packet(struct sk_buff *skb, struct device *dev)
 {
        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 @@ static int el16_send_packet(struct sk_buff *skb, struct device *dev)
                        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 @@ static int el16_send_packet(struct sk_buff *skb, struct device *dev)
                } 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 @@ static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        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 @@ static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
        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 @@ static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
        /* 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 @@ static void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                        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 void el16_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 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 struct net_device_stats *el16_get_stats(struct device *dev)
 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 @@ static void init_rx_bufs(struct device *dev)
        /* 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++ = 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;
+         write_ptr = dev->mem_start + cur_rxbuf;
+
+               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 @@ static void init_rx_bufs(struct device *dev)
 
        /* 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 @@ static void init_82586_mem(struct device *dev)
        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 @@ static void init_82586_mem(struct device *dev)
 
        {
                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 @@ static void init_82586_mem(struct device *dev)
        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 @@ static void hardware_send_packet(struct device *dev, void *buf, short length)
        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 hardware_send_packet(struct device *dev, void *buf, short length)
 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 @@ static void el16_rx(struct device *dev)
                        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 @@ static void el16_rx(struct device *dev)
                }
 
                /* 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;
index 17b4f5387352b591e8b638fbfce704acdac27d35..9ed7b2c8d945391ccbe630d84bdbb5d77cb2ecd6 100644 (file)
@@ -176,7 +176,7 @@ int dgrs_spantree = -1;
 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        hashexpire = -1;
 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");
index a962a8c9ee43df745035d5e0119d64f85ec8f12d..04cc029615ed9a4086b705046146dde8f817c3dc 100644 (file)
@@ -78,6 +78,8 @@ pci_clone_list[] __initdata = {
        {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
index b0245e1d051e2fbfd0d279b06a5e8c3e562e83b3..ab52e4ed1f174570c1d3b75d89995c0f6f08ec9d 100644 (file)
@@ -49,6 +49,7 @@ dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) supp
     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 @@ if [ "$CONFIG_PARPORT" != "n" ]; then
   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
index 7f514a4ec05239c290eb7e16544adb9f88f8a0f1..dbe98db15991bff9cdf0331d7c07d750373fd6d4 100644 (file)
@@ -15,6 +15,7 @@ MOD_LIST_NAME := SCSI_MODULES
 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 @@ else
   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 @@ else
   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
@@ -433,6 +466,9 @@ BusLogic.o: BusLogic.c FlashPoint.c
 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
 
index 43599d806d54e00c543c80b1a0716e6b52a41970..2f59d69f3bc9940a179c98c99bb41f36f6319b7d 100644 (file)
@@ -470,6 +470,7 @@ struct signature {
    { "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 --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
new file mode 100644 (file)
index 0000000..77a9aba
--- /dev/null
@@ -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 *
+ *                                                                      *
+ * <achim@vortex.de>                                                    *
+ *                                                                      *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published    *
+ * by the Free Software Foundation; either version 2 of the License,    *
+ * or (at your option) any later version.                               *
+ *                                                                      *
+ * This program is distributed in the hope that it will be useful,      *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the         *
+ * GNU General Public License for more details.                         *
+ *                                                                      *
+ * You should have received a copy of the GNU General Public License    *
+ * along with this kernel; if not, write to the Free Software           *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            *
+ *                                                                      *
+ * 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 <linux/module.h>
+#endif
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <linux/reboot.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#if LINUX_VERSION_CODE >= 0x010300
+#include <linux/blk.h>
+#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;idx<a*1000L;++idx) udelay(1000);}
+
+#ifdef SLOWMOTION_GDTH
+#define SLOWM   WAITSEC(2)  
+#undef  INIT_RETRIES
+#undef  INIT_TIMEOUT
+#undef  POLL_TIMEOUT
+#define INIT_RETRIES    15
+#define INIT_TIMEOUT    150
+#define POLL_TIMEOUT    150
+#else
+#define SLOWM
+#endif
+
+#ifdef __SERIAL__
+#define MAX_SERBUF 160
+static void ser_init(void);
+static void ser_puts(char *str);
+static void ser_putc(char c);
+static int  ser_printk(const char *fmt, ...);
+static char strbuf[MAX_SERBUF+1];
+#ifdef __COM2__
+#define COM_BASE 0x2f8
+#else
+#define COM_BASE 0x3f8
+#endif
+static void ser_init()
+{
+    unsigned port=COM_BASE;
+
+    outb(0x80,port+3);
+    outb(0,port+1);
+    /* 19200 Baud, if 9600: outb(12,port) */
+    outb(6, port);
+    outb(3,port+3);
+    outb(0,port+1);
+    /*
+    ser_putc('I');
+    ser_putc(' ');
+    */
+}
+
+static void ser_puts(char *str)
+{
+    char *ptr;
+
+    ser_init();
+    for (ptr=str;*ptr;++ptr)
+        ser_putc(*ptr);
+}
+
+static void ser_putc(char c)
+{
+    unsigned port=COM_BASE;
+
+    while ((inb(port+5) & 0x20)==0);
+    outb(c,port);
+    if (c==0x0a)
+    {
+        while ((inb(port+5) & 0x20)==0);
+        outb(0x0d,port);
+    }
+}
+
+static int ser_printk(const char *fmt, ...)
+{
+    va_list args;
+    int i;
+
+    va_start(args,fmt);
+    i = vsprintf(strbuf,fmt,args);
+    ser_puts(strbuf);
+    va_end(args);
+    return i;
+}
+
+#define TRACE(a)    {if (DebugState==1) {ser_printk a; SLOWM}}
+#define TRACE2(a)   {if (DebugState==1 || DebugState==2) {ser_printk a; SLOWM}}
+#define TRACE3(a)   {if (DebugState!=0) {ser_printk a; SLOWM}}
+
+#else /* !__SERIAL__ */
+#define TRACE(a)    {if (DebugState==1) {LOGEN;printk a; SLOWM}}
+#define TRACE2(a)   {if (DebugState==1 || DebugState==2) {LOGEN;printk a; SLOWM}}
+#define TRACE3(a)   {if (DebugState!=0) {LOGEN;printk a; SLOWM}}
+#endif
+
+#else /* !DEBUG */
+#define TRACE(a)
+#define TRACE2(a)
+#define TRACE3(a)
+#endif
+
+#ifdef GDTH_STATISTICS
+static ulong max_rq=0, max_index=0, max_sg=0;
+static ulong act_ints=0, act_ios=0, act_stats=0, act_rq=0;
+#define GDTH_TIMER      31                      /* see linux/timer.h ! */
+#endif
+
+#define PTR2USHORT(a)   (ushort)(ulong)(a)
+#define JIFFYWAIT(a)    {ulong gdtjf;gdtjf=jiffies+(a);while(gdtjf>jiffies);}
+#define GDTOFFSOF(a,b)  (size_t)&(((a*)0)->b)   
+#define INDEX_OK(i,t)   ((i)<sizeof(t)/sizeof((t)[0]))
+
+#define NUMDATA(a)      ( (gdth_num_str  *)((a)->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=<IRQ>
+ *
+ * Where: <IRQ> 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 <linux/stat.h> 
+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; i<MAXHA && irqs[i]!=0xff; ++i) {
+               if (irqs[i]!=0) {
+                   irq_found=TRUE;
+                   break;
+               }
+           }
+           if (irq_found) {
+               ha->irq = 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; i<gdth_ctr_count; ++i) {
+        ha = HADATA(gdth_ctr_tab[i]);
+        if (ha->irq != (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; i<GDTH_MAXCMDS; ++i) {
+        if (gdth_cmd_tab[i][hanum].cmnd == UNUSED_CMND) {
+            gdth_cmd_tab[i][hanum].cmnd = ha->pccb->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; j<GDTH_MAXCMDS; ++j) {
+        if (gdth_cmd_tab[j][hanum].cmnd != UNUSED_CMND)
+            ++i;
+    }
+    if (max_index < i) {
+        max_index = i;
+        TRACE3(("GDT: max_index = %d\n",(ushort)i));
+    }
+#endif
+
+    TRACE(("gdth_release_event() hanum %d\n",hanum));
+    ha = HADATA(gdth_ctr_tab[hanum]);
+
+    if (ha->pccb->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_no<MAXBUS; ++bus_no) {
+       chn->channel_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; b<ha->bus_cnt; ++b) {
+        for (t=0; t<MAXID; ++t) {
+            TRACE(("gdth_search_drives() rawd. bus %d id %d\n",b,t));
+            if (ha->id[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; i<cdev_cnt && i<MAX_HDRIVES; ++i) {
+        TRACE(("gdth_search_drives() cachedev. %d\n",i));
+        if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,i,0,0)) {
+            /* dynamic relation between host drive number and Bus/ID */
+            /* search free position */
+            pos_found = FALSE;
+            for (b=0,t=0; b<ha->bus_cnt; ++b) {
+                for (t=0; t<MAXID; ++t) {
+                    if (ha->id[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; i<scp->use_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; i<scp->use_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 (i<GDTH_MAXSG)
+                cmdp->u.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; i<scp->use_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 (i<GDTH_MAXSG)
+            cmdp->u.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; i<msg->msg_alen && i<MSGLEN; ++i) {
+                /* getchar() ?? */           
+                /* .. */
+                if (c == '\r')
+                    break;
+                msg->msg_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; i<GDTH_MAXCMDS; ++i) 
+        if (gdth_cmd_tab[i][hanum].cmnd != UNUSED_CMND)
+            ++act_stats;
+
+    ha = HADATA(gdth_ctr_tab[hanum]);
+    for (act_rq=0,nscp=ha->req_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<<GDTH_TIMER;
+    sti();
+}
+#endif
+
+int gdth_detect(Scsi_Host_Template *shtp)
+{
+    struct Scsi_Host *shp;
+    gdth_ha_str *ha;
+    unsigned long flags;
+    ulong isa_bios;
+    ushort eisa_slot,device_id,index;
+    gdth_pci_str pcistr;
+    int i,j,hanum;
+    unchar b;
+    
+#ifdef DEBUG_GDTH
+    printk("GDT: This driver contains debugging information !! Trace level = %d\n",
+        DebugState);
+    printk("     Destination of debugging information: ");
+#ifdef __SERIAL__
+#ifdef __COM2__
+    printk("Serial port COM2\n");
+#else
+    printk("Serial port COM1\n");
+#endif
+#else
+    printk("Console\n");
+#endif
+    WAITSEC(3);
+#endif
+
+    TRACE(("gdth_detect()\n"));
+
+    if (disable_gdth_scan) {
+        printk("GDT: Controller driver disabled from command line !\n");
+        return 0;
+    }
+
+    /* initializations */
+    gdth_polling = TRUE; b = 0;
+    for (i=0; i<GDTH_MAXCMDS; ++i)
+        for (j=0; j<MAXHA; ++j)
+            gdth_cmd_tab[i][j].cmnd = UNUSED_CMND;
+    for (i=0; i<4; ++i)
+        for (j=0; j<MAXHA; ++j)
+            gdth_ioctl_tab[i][j] = NULL;
+    gdth_clear_events();
+
+    /* scanning for controllers, at first: ISA controller */
+    for (isa_bios=0xc8000UL; isa_bios<=0xd8000UL; isa_bios+=0x8000UL) {
+        if (gdth_search_isa(isa_bios)) {        /* controller found */
+            shp = scsi_register(shtp,sizeof(gdth_ext_str));
+            ha = HADATA(shp);
+            if (!gdth_init_isa(isa_bios,ha)) {
+                scsi_unregister(shp);
+                continue;
+            }
+            /* controller found and initialized */
+            printk("Configuring GDT-ISA HA at BIOS 0x%05lX IRQ %u DRQ %u\n",
+                   isa_bios,ha->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; i<MAXID; ++i) {
+                if (ha->id[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; i<MAXBUS; ++i) {
+                for (j=0; j<MAXID; ++j) {
+                    ha->id[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; b<ha->bus_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; i<MAXID; ++i) {
+                    if (ha->id[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; i<MAXID; ++i) {
+                if (ha->id[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; i<MAXBUS; ++i) {
+                for (j=0; j<MAXID; ++j) {
+                    ha->id[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; b<ha->bus_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; i<MAXID; ++i) {
+                    if (ha->id[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; i<MAXID; ++i) {
+                if (ha->id[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; i<MAXBUS; ++i) {
+                for (j=0; j<MAXID; ++j) {
+                    ha->id[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; b<ha->bus_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; i<MAXID; ++i) {
+                    if (ha->id[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<<GDTH_TIMER;
+#endif
+#if LINUX_VERSION_CODE >= 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<<GDTH_TIMER);
+#endif
+#if LINUX_VERSION_CODE >= 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=<IRQ>\n");
+        printk("Where: <IRQ>: 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 --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h
new file mode 100644 (file)
index 0000000..4e2c477
--- /dev/null
@@ -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
+ *
+ * <achim@vortex.de>
+ *
+ * $Id: gdth.h,v 1.9 1997/11/04 09:55:42 achim Exp $
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+
+#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 <linux/pci.h> */
+#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 --git a/drivers/scsi/gdth_ioctl.h b/drivers/scsi/gdth_ioctl.h
new file mode 100644 (file)
index 0000000..01f5db4
--- /dev/null
@@ -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 --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c
new file mode 100644 (file)
index 0000000..1a34996
--- /dev/null
@@ -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; i<gdth_ctr_vcount; ++i) {
+        if (gdth_ctr_vtab[i]->host_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 --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h
new file mode 100644 (file)
index 0000000..a3d5dcd
--- /dev/null
@@ -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
+
index 7bf860e8cd3c02926e648517b25f1b4c1b707059..c871f93b23a0c7f7748ff6cd99123626e80d1d80 100644 (file)
 #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 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #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,
@@ -341,6 +368,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #ifdef CONFIG_SCSI_SUNESP
     SCSI_SPARC_ESP,
 #endif
+#ifdef CONFIG_SCSI_GDTH
+    GDTH,
+#endif
 #ifdef CONFIG_SCSI_QLOGICPTI
     QLOGICPTI,
 #endif
diff --git a/drivers/scsi/pci2000.c b/drivers/scsi/pci2000.c
new file mode 100644 (file)
index 0000000..0a27c90
--- /dev/null
@@ -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 <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2000.h"
+#include "psi_roy.h"
+
+#include<linux/stat.h>
+
+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 --git a/drivers/scsi/pci2000.h b/drivers/scsi/pci2000.h
new file mode 100644 (file)
index 0000000..ded9939
--- /dev/null
@@ -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 <linux/types.h>
+#include <linux/kdev_t.h>
+
+#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 --git a/drivers/scsi/pci2220i.c b/drivers/scsi/pci2220i.c
new file mode 100644 (file)
index 0000000..124cc7b
--- /dev/null
@@ -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 <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2220i.h"
+#include "psi_dale.h"
+
+#include<linux/stat.h>
+
+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 --git a/drivers/scsi/pci2220i.h b/drivers/scsi/pci2220i.h
new file mode 100644 (file)
index 0000000..0fafc26
--- /dev/null
@@ -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 <linux/types.h>
+#include <linux/kdev_t.h>
+
+#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 --git a/drivers/scsi/psi240i.c b/drivers/scsi/psi240i.c
new file mode 100644 (file)
index 0000000..8e12f4b
--- /dev/null
@@ -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 <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "psi240i.h"
+#include "psi_chip.h"
+
+#include<linux/stat.h>
+
+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 --git a/drivers/scsi/psi240i.h b/drivers/scsi/psi240i.h
new file mode 100644 (file)
index 0000000..282ee1b
--- /dev/null
@@ -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 <linux/types.h>
+#include <linux/kdev_t.h>
+
+#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 --git a/drivers/scsi/psi_chip.h b/drivers/scsi/psi_chip.h
new file mode 100644 (file)
index 0000000..644c42f
--- /dev/null
@@ -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 --git a/drivers/scsi/psi_dale.h b/drivers/scsi/psi_dale.h
new file mode 100644 (file)
index 0000000..d8a407f
--- /dev/null
@@ -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 --git a/drivers/scsi/psi_roy.h b/drivers/scsi/psi_roy.h
new file mode 100644 (file)
index 0000000..36a0341
--- /dev/null
@@ -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
+
index 6404809ccc21b8afab6f6e1b80de3a48112bac2a..dd0f18fa1edb9c6f246c751f6512d3b1ad96b570 100644 (file)
@@ -498,8 +498,7 @@ static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors
            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;
index 073de6095abdb47f9f037402b643a6282c823d04..0971ce95453e2fe1135ef07a423005b1a989d317 100644 (file)
@@ -341,6 +341,11 @@ int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
                    &((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;
index f2f789a28097d59e434807c6d290e8082440669f..e7880fcc211056997a6473ef2424f44144d4cd99 100644 (file)
@@ -89,3 +89,11 @@ ifdef CONFIG_UART401
        OBJS := $(OBJS)  uart401.o
 endif
 
+ifdef CONFIG_OPL3SA1
+       OBJS := $(OBJS)  opl3sa.o
+endif
+
+ifdef CONFIG_SOFTOSS
+       OBJS := $(OBJS)  softoss.o softoss_rs.o
+endif
+
index 56cbb7db280155a57dbfb994f416b794a44f6f9c..39159f8e27dd7c3e2ffc97a89c78c09a3183a82f 100644 (file)
@@ -1,2 +1,2 @@
-3.8a
+3.8s
 0x030804
index 17c2dad5f32dce355aecc129a09dda2b362aa6c8..50e797f24cd4300ba6875592c047d786b8e3b5dd 100644 (file)
@@ -1,6 +1,13 @@
-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).
 
index d14653d4598b96ae727715348f2b1b3fadd849a0..310bf01bf1f4036199e328100c2e0e87e75ec108 100644 (file)
-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
index b0a3483d54f7f33ce06f913a9dbe3e6d77569f89..271c073e32a558580ddd10a34f7830963f8136a2 100644 (file)
@@ -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 @@ mkscript: setup
 
 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 @@ configure: configure.c
 
 dep:
        $(CPP) -M $(CFLAGS) -I. *.c > .depend
+       cd lowlevel;make dep
 
 setup:
        @echo Compiling Sound Driver v $(VERSION) for Linux
index c5002a1f793f3d2ae1594d8a62a6affaa64ce557..113ad9130798db5880e044359100a6a3e2ee2bff 100644 (file)
@@ -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 @@ contributors. (I could have forgotten some names.)
 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 @@ Best regards,
 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
index 148afd6b08e96c1a22aaf8bea09e1730b63640e1..79bbe464bfb7941a78ee03d6583aa55762adc5ef 100644 (file)
@@ -5,7 +5,13 @@ This document describes configuring soundcards with freeware version of
 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 @@ IMPORTANT!    This document covers only cards that were "known" when
                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 @@ IMPORTANT!    This document covers only cards that were "known" when
                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 @@ IMPORTANT!    This document covers only cards that were "known" when
                offered by these programs are not necessarily valid.
 
 
-
 THE BIGGEST MISTAKES YOU CAN DO
 ===============================
 
@@ -211,7 +216,8 @@ Sound Blasters
                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 @@ Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4)
        ----------------------------------------------------------------
        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 @@ Ensoniq SoundScape and compatibles
        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 @@ different operating systems.
 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 @@ it's possible to use just one (8 bit) DMA channel by answering the 8 bit
 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 @@ as free copies of soundcards, SDKs and operating systems.
 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
-
index b1121ebbe15a9e780672286f9d1cd94811536d08..103470869a4a69b9a542840c8ffd8ba7dfe1782d 100644 (file)
@@ -67,10 +67,10 @@ Readme.cards for info about configuring the driver with your card. Also
 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!!!
index 82ec86f037c03edf432782f9ae048e92127f8151..c48fa823df3c0c578e9de95767d0f639d4636e81 100644 (file)
@@ -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 @@ typedef struct
     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 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
   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 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
      * 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 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
 
   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 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
     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 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
     else
       {
        DDB (printk ("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
-       /* return 0; */
+       return 0;
       }
 
   /*
@@ -1618,8 +1622,7 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
                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 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
                  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_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
 
   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 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
   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 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
     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 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
     {
       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 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
 
   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 @@ ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int shar
        }
     }
   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 @@ adintr (int irq, void *dev_id, struct pt_regs *dummy)
 
       if (irq > 15)
        {
-         /* printk ("ad1848.c: Bogus interrupt %d\n", irq); */
+         /* printk("ad1848.c: Bogus interrupt %d\n", irq); */
          return;
        }
 
@@ -2149,8 +2151,7 @@ init_deskpro (struct address_info *hw_config)
       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 @@ probe_ms_sound (struct address_info *hw_config)
     {
       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;
index 227f140b673da756cd8832503e1f0b99dc6656f4..7eda358b3dc428882440b5056d52e174c5ff3c66 100644 (file)
@@ -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 @@ probe_adlib (struct address_info *hw_config)
 
   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;
     }
 
index d1ba581b1574e03fa45715c2760850d6a640e9cf..c227f805bb85e7d518c0a2d96c6c9ae97022b7da 100644 (file)
@@ -30,7 +30,7 @@ static int      dev_nblock[MAX_AUDIO_DEV];    /* 1 if in nonblocking mode */
 #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 @@ set_format (int dev, int fmt)
   else
     return local_format[dev];
 
-  return audio_format[dev];
+  return local_format[dev];
 }
 
 int
@@ -247,12 +247,16 @@ audio_write (int dev, struct fileinfo *file, const char *buf, int count)
        {
          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 @@ audio_ioctl (int dev, struct fileinfo *file_must_not_be_used,
 {
   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 @@ audio_ioctl (int dev, struct fileinfo *file_must_not_be_used,
          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 @@ reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
   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 @@ reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
 
   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 @@ reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
   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 @@ dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
     return 0;
 }
 
-static int
+int
 dma_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
 
@@ -841,6 +843,11 @@ dma_ioctl (int dev, unsigned int cmd, caddr_t arg)
 
        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;
 
index 6b50a3aa5707c37a001b3895d5048dabcd67979e..a65c1b117d019554e96c787b329642545aea44e3 100644 (file)
 #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))
 
                  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 @@ hw_entry        hw_table[] =
   {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 @@ char           *questions[] =
   "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 @@ char           *help[] =
   "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 @@ ask_parameters (void)
 
   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 @@ ask_parameters (void)
                  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
index 1946cd427dd90ee2f03f79bc54af06489ad21ec7..5bdb68b685ea68127520618beaea8888043562cb 100644 (file)
@@ -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 */
index 10daa50ad352e9b418c2b46d31b00c0a026d593f..a0708c14d11e004eb8445bfa85bc62582146ecb3 100644 (file)
@@ -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 @@ sound_unload_drivers (void)
          }
       }
 
+       for (i=0;i<num_audiodevs;i++)
+          DMAbuf_deinit(i);
+
   if (trace_init)
     printk ("Sound unload complete\n");
 }
@@ -163,8 +167,6 @@ sound_unload_driver (int type)
 {
   int             i, drv = -1, n = num_sound_cards;
 
-  unsigned long   flags;
-
 
   DEB (printk ("unload driver %d: ", type));
 
@@ -185,11 +187,6 @@ sound_unload_driver (int type)
          }
       }
   DEB (printk ("\n"));
-
-  save_flags (flags);
-  cli ();
-
-  restore_flags (flags);
 }
 
 
@@ -302,8 +299,7 @@ sndtable_init_card (int unit, struct address_info *hw_config)
        return 1;
       }
 
-  DEB (printk ("sndtable_init_card: No card defined with type=%d, num cards: %d\n",
-              unit, num_sound_cards));
+  DEB (printk ("sndtable_init_card: No card defined with type=%d, num cards: %d\n", unit, num_sound_cards));
   return 0;
 }
 
index a7e4026e85a09c32a81592e87891cedeabc93b68..d9a1be347acbb1e04a512393db524a50ec9c7add 100644 (file)
@@ -15,7 +15,6 @@
 #ifndef _DEV_TABLE_H_
 #define _DEV_TABLE_H_
 
-#include <linux/config.h>
 
 /*
  * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
  */
 #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 @@ struct dma_buffparms {
        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 @@ struct sound_timer_operations {
                {"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 @@ struct sound_timer_operations {
             {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},
index 8133b3500c8b5ca71628954be212d58c3f083273..2c9363237787eaa7dd06530c7eced4e4ebb797bc 100644 (file)
@@ -94,6 +94,8 @@ open_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan)
   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 @@ dma_reset_output (int dev)
   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 @@ dma_reset_output (int dev)
   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 @@ DMAbuf_sync (int dev)
       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 @@ DMAbuf_sync (int dev)
       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 @@ DMAbuf_sync (int dev)
       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 @@ DMAbuf_release (int dev, int mode)
 
   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 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
   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 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
            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 @@ DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction)
       enable_dma (dmap->dma);
     }
   restore_flags (flags);
-  /* printk ("%04x ", pos); */
+  /* printk( "%04x ",  pos); */
 
   return pos;
 }
@@ -802,8 +805,8 @@ DMAbuf_space_in_queue (int dev)
    */
 
   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 @@ DMAbuf_space_in_queue (int dev)
                                 */
       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 @@ output_sleep (int dev, int dontblock)
       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 @@ output_sleep (int dev, int dontblock)
        tmout = 20 * HZ;
     }
 
-  if (signal_pending(current))
+  if ((current->signal & ~current->blocked))
     return -EIO;
 
 
@@ -882,7 +887,7 @@ output_sleep (int dev, int dontblock)
       ;
       dma_reset_output (dev);
     }
-  else if (signal_pending(current))
+  else if ((current->signal & ~current->blocked))
     {
       err = -EINTR;
     }
@@ -895,11 +900,14 @@ find_output_space (int dev, char **buf, int *size)
 {
   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 @@ find_output_space (int dev, char **buf, int *size)
 #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 @@ find_output_space (int dev, char **buf, int *size)
       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 @@ DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
   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 @@ DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
   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 @@ local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
   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 @@ 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))
@@ -1308,7 +1327,7 @@ do_inputintr (int dev)
     }
   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 @@ DMAbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
   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 --git a/drivers/sound/dmabuf.c.old b/drivers/sound/dmabuf.c.old
new file mode 100644 (file)
index 0000000..18a997f
--- /dev/null
@@ -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 <linux/config.h>
+
+#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
index 7588c3e1108f8299d2082a3f86dadaa367e260b7..b7da7932a673f6da4b273e803c8a4d99b8408835 100644 (file)
@@ -628,7 +628,7 @@ struct sound_queue {
 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 long state_read(char *dest, unsigned long count);
 
 
 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_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)
 {
-    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 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)
 {
     return -ESPIPE;
 }
@@ -3184,25 +3186,25 @@ static int sound_ioctl(struct inode *inode, struct file *file, u_int cmd,
                    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:
index 1c8881684ca28a92f0580cd8101e633bd80b9c04..30b86bb9db21c533ecfacba502c91a300a842037 100644 (file)
 
 #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 @@ gusintr (int irq, void *dev_id, struct pt_regs *dummy)
 /*
  * 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)
index 9fc38fc88010d00e49f1c0463c53d746b82ccb6e..1c80715535dba30de0fd107573f0dee65f656633 100644 (file)
@@ -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;
@@ -296,3 +297,4 @@ gus_midi_interrupt (int dummy)
 }
 
 #endif
+#endif
index 8c1523750d7b334126829c05f57927b18bd25823..0682d38f81afa88c52262509cb3487918788d0a4 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/ultrasound.h>
 #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 @@ extern int      gus_pnp_flag;
 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 @@ gus_voice_fade (int voice)
   if (voices[voice].mode & WAVE_ENVELOPES)
     {
       start_release (voice, flags);
+      restore_flags (flags);
       return;
     }
 
@@ -692,6 +692,7 @@ gus_voice_fade (int voice)
       gus_voice_off ();
       gus_rampoff ();
       gus_voice_init (voice);
+      restore_flags (flags);
       return;
     }
 
@@ -1026,9 +1027,7 @@ pnp_mem_init (void)
 
   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 @@ guswave_set_instr (int dev, int voice, int instr_no)
 
   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 @@ guswave_open (int dev, int mode)
 
   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 @@ guswave_load_patch (int dev, int format, const char *addr,
 
   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 @@ gus_audio_open (int dev, int mode)
 
   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 @@ do_volume_irq (int voice)
 
     default:;
     }
+  restore_flags (flags);
 }
 
 void
index 9ee76e4b656bcfac9c9bbd5fd4dac9ef50865f33..bf2fc3f7d785a345c8a7330d6a685426ec5d4a34 100644 (file)
@@ -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
index d4687017ff896efcf880b33f166d0a45529432d6..f1c48976d5811abf11a30a03b368eaffd24c20e2 100644 (file)
@@ -15,6 +15,13 @@ OBJS := $(OBJS) aedsp16.o
 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 @@ clean:
 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
index db082bf526d7748473a4fdab7a9e2f3b0ea6b7c4..d508db4d59b6248940973c2272605520be79f698 100644 (file)
@@ -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 @@ and suggestions directly to Iwai (_NOT_ to Linus Torvalds or Hannu
 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 @@ Compile conditions are defined in awe_config.h.
     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 @@ Compile conditions are defined in awe_config.h.
 - 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 @@ Compile conditions are defined in awe_config.h.
     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 @@ to reboot their system many times :-)
 
 * 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.
 
 
index beb1695a64630846156ec207ca94439f66d19cb6..93db242a1a69d09b58f7cf8f0a894b521e65e025 100644 (file)
 #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 --git a/drivers/sound/lowlevel/awe_compat.h b/drivers/sound/lowlevel/awe_compat.h
new file mode 100644 (file)
index 0000000..d0c34a1
--- /dev/null
@@ -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
+
index c80eb3f358436667be27e4938db229885dc69e57..84de143cfc17f99d97ac5efacb2a0b07fde16f92 100644 (file)
@@ -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
  */
 #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:
 
 
 /*----------------------------------------------------------------
- * 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
 
 
 /*----------------------------------------------------------------
  *----------------------------------------------------------------*/
 
 /* 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
 
+/* add mixer control of emu8000 equalizer */
+#define CONFIG_AWE32_MIXER
 
-#ifdef linux
-/* i tested this only on my linux */
-#define INLINE  __inline__
-#else
-#define INLINE /**/
-#endif
-
+/* look up voices according to MIDI channel priority */
+#define AWE_LOOKUP_MIDI_PRIORITY
 
 /*----------------------------------------------------------------*/
 
index 6aa151fc1db433fdc2e1e7c10604f7f2d1011f79..4579cec61aae09a12ecd2aa82ca6471a7d790c04 100644 (file)
@@ -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
  *
 #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 --git a/drivers/sound/lowlevel/awe_version.h b/drivers/sound/lowlevel/awe_version.h
new file mode 100644 (file)
index 0000000..8fe8525
--- /dev/null
@@ -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
index 740ecb94be0e0cd68a44e7fbdc28af828e1f78ac..f7a884b9f18fc2f882d5ceb4c86dc88b7cf9fae6 100644 (file)
@@ -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 <i386/isa/sound/awe_config.h>
 #else
 
 #ifdef __FreeBSD__
 #  include <i386/isa/sound/awe_hw.h>
+#  include <i386/isa/sound/awe_version.h>
 #  include <i386/isa/sound/awe_voice.h>
 #else
 #  include "awe_hw.h"
+#  include "awe_version.h"
 #  include <linux/awe_voice.h>
 #endif
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+/* include finetune table */
 #ifdef AWE_OBSOLETE_VOXWARE
 #  ifdef __FreeBSD__
 #    define SEQUENCER_C
@@ -57,6 +60,8 @@
 #  include <machine/ultrasound.h>
 #endif
 
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
 
 /*----------------------------------------------------------------
  * debug message
@@ -77,33 +82,71 @@ static int debug_mode = 0;
  * 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 @@ typedef struct _awe_chan_info {
        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 @@ typedef struct _voice_info {
 } 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 patch_opened = 0;           /* sample already loaded? */
 
-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 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 int awe_channel_mode = 0;               /* channel control mode */
+static unsigned int drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
+
+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_release_region(void);
 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 int calc_parm_search(int msec, short *table);
 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_set_pan(int voice, int forced);
 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 @@ static void awe_hw_gus_control(int dev, int cmd, unsigned char *event);
 #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_send_array(unsigned short *data);
 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 <i386/isa/sound/awe_compat.h>
+#else
+#  include "awe_compat.h"
+#endif
 
 /*----------------------------------------------------------------
  * synth operation table
@@ -423,7 +445,9 @@ static void awe_set_reverb_mode(int mode);
 
 static struct synth_operations awe_operations =
 {
-       "EMU8K",
+#ifdef AWE_OSS38
+       "Emu8000",
+#endif
        &awe_info,
        0,
        SYNTH_TYPE_SAMPLE,
@@ -449,6 +473,15 @@ static struct synth_operations awe_operations =
        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 @@ static struct synth_operations awe_operations =
  *================================================================*/
 
 #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 @@ int attach_awe(void)
                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 @@ int attach_awe(void)
                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 @@ int attach_awe(void)
        /* 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: <SoundBlaster EMU8000 MIDI (RAM%dk)>", 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 @@ int attach_awe(void)
 
        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 @@ int probe_awe_obsolete(struct address_info *hw_config)
 
 #endif
 
+
 /*================================================================
  * clear sample tables 
  *================================================================*/
@@ -552,15 +642,14 @@ awe_reset_samples(void)
        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 @@ awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
 
 /* 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 @@ awe_peek(unsigned short cmd, unsigned short port)
 }
 
 /* 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 @@ awe_wait(unsigned short delay)
                        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 @@ awe_initialize(void)
 
        /* enable audio */
        awe_poke(AWE_HWCF3, 0x0004);
+
+       /* set equalizer */
+       awe_equalizer(5, 9);
 }
 
 
@@ -717,7 +816,7 @@ awe_initialize(void)
 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 @@ static int
 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 @@ calc_parm_delay(int msec)
 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 @@ calc_parm_search(int msec, short *table)
 
 /* 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 @@ FX_OFFSET(FX_Rec *rec, int lo, int hi, int mode)
 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 @@ awe_note_on(int voice)
 
        /* 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 @@ awe_note_on(int voice)
 
        /* 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 @@ awe_note_on(int voice)
        /* 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 @@ awe_note_on(int voice)
 
        /* 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_note_off(int voice)
        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_terminate(int voice)
 {
        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_voice_pitch(int voice, int forced)
        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 @@ awe_set_voice_vol(int voice, int forced)
 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 @@ awe_set_pan(int voice, int forced)
        }
        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_fx_fmmod(int voice, int forced)
 {
        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_fx_tremfrq(int voice, int forced)
 {
        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_fx_fm2frq2(int voice, int forced)
 {
        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 @@ awe_calc_pitch(int voice)
        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 @@ awe_calc_pitch(int voice)
        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 @@ awe_calc_pitch_from_freq(int voice, int freq)
        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 @@ awe_calc_volume(int voice)
 
        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 @@ awe_calc_volume(int voice)
        /* 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 @@ awe_calc_volume(int voice)
 }
 
 
+/* 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)
-{
-       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));
+static void awe_channel_init(int ch, int init_all)
+{
+       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_open(int dev, int mode)
        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 @@ awe_close(int dev)
 }
 
 
+/* 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 @@ awe_ioctl(int dev, unsigned int cmd, caddr_t arg)
 {
        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 @@ awe_ioctl(int dev, unsigned int cmd, caddr_t arg)
                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 @@ awe_start_note(int dev, int voice, int note, int velocity)
        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);
+       else
+               awe_alloc_multi_voices(voice, note, velocity, key);
 
-       /* turn off other voices (for drums) */
+       /* turn off other voices exlusively (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 */
+               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) {
-                       /* 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);
-               }
+       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 @@ awe_search_instr(int bank, int preset)
 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 @@ awe_reset(int dev)
        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 @@ awe_hw_control(int dev, unsigned char *event)
 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 @@ awe_hw_gus_control(int dev, int cmd, unsigned char *event)
        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 @@ static fx_affect_func fx_realtime[] = {
        /* 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 @@ awe_hw_awe_control(int dev, int cmd, unsigned char *event)
        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 @@ awe_hw_awe_control(int dev, int cmd, unsigned char *event)
                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_hw_awe_control(int dev, int cmd, unsigned char *event)
                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_EXCLUSIVE_SOUND:
-               DEBUG(0,printk("AWE32: exclusive mode = %d\n", p1));
-               awe_exclusive_sound = p1;
+       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_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 @@ awe_hw_awe_control(int dev, int cmd, unsigned char *event)
 }
 
 
+/* 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 @@ awe_load_patch(int dev, int format, const char *addr,
                /* 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 @@ awe_load_patch(int dev, int format, const char *addr,
 
        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 @@ awe_load_patch(int dev, int format, const char *addr,
 }
 
 
+/* 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"));
+       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);
+       }
+
+       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
 
-       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 (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 @@ awe_read_word(int pos)
        }
        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 @@ awe_read_word(int pos)
 
 /* 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 @@ awe_write_wave_data(const char *addr, long offset, int size)
        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 @@ awe_write_wave_data(const char *addr, long offset, int size)
                        }
                }
        }
+       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 @@ awe_write_wave_data(const char *addr, long offset, int size)
                        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 @@ static int
 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);
+
+       free_sample = awe_free_sample();
+       smp = &samples[free_sample].v;
 
-       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;
+       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 @@ awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag)
                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;
 
-       /* 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++;
+       add_info_list(free_info);
+       add_sf_info(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;
        }
-       return -1;
+       infos[rec].next = -1;
+       sflists[sf_id].num_info++;
 }
 
-
-/* voice pressure change */
-static void
-awe_aftertouch(int dev, int voice, int pressure)
+/* prepend this sample to sf list */
+static void add_sf_sample(int rec)
 {
-       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);
-       }
+       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++;
 }
 
-
-/* voice control change */
-static void
-awe_controller(int dev, int voice, int ctrl_num, int value)
+/* purge the old records which don't belong with the same file id */
+static void purge_old_list(int rec, int next)
 {
-       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);
+       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;
                }
-               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;
+       } else {
+               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;
 
+       if (infos[rec].disabled)
+               return;
 
-/* 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)
+       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 @@ awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_i
        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,
+                                     &note, 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,
+                                     &note, 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 @@ awe_setup_voice(int dev, int voice, int chn)
 }
 
 
+#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_init_fm(void)
        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 @@ awe_init_fm(void)
 
 /* 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_open_dram_for_write(int offset)
        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_open_dram_for_check(void)
                        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 @@ awe_close_dram(void)
        }
 
        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 @@ static int
 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 @@ awe_detect(void)
 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_check_dram(void)
                 */
                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 @@ awe_check_dram(void)
  *================================================================*/
 
 /* 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 @@ static void awe_set_chorus_mode(int effect)
 /* 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 @@ static struct ReverbCmdPair {
   {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 */
index f4f86ff578769709ed68814a42567ce52f017ca0..065202789653c475abf74d2fe803b409c1ef701d 100644 (file)
@@ -60,7 +60,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_MAD16)
+#ifdef CONFIG_MAD16
 
 #include "sb.h"
 
@@ -297,8 +297,7 @@ wss_init (struct address_info *hw_config)
   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;
     }
 
index 166b41c227d12d3400e0280c437d427687aaa299..48430d76d6e5f5bb8e2da3dff14d3f265eff756a 100644 (file)
@@ -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_wait (int mask)
              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 @@ maui_load_patch (int dev, int format, const char *addr,
 
   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;
     }
 
index 8685b9307069f0a083815ec6c083ae253ef2c0f3..606ee1299934d16dec5476b8aa28ecb5a03b5507 100644 (file)
@@ -18,7 +18,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_MIDI)
+#ifdef CONFIG_MIDI
 
 #define _MIDI_SYNTH_C_
 
@@ -89,7 +89,7 @@ do_midi_msg (int synthno, unsigned char *msg, int mlen)
       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 @@ midi_synth_input (int orig_dev, unsigned char data)
       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 @@ midi_synth_load_patch (int dev, int format, const char *addr,
 
   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 @@ midi_synth_load_patch (int dev, int format, const char *addr,
 
   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 @@ midi_synth_load_patch (int dev, int format, const char *addr,
        }
 
       while (!midi_devs[orig_dev]->outputc (orig_dev, (unsigned char) (data & 0xff)) &&
-            !signal_pending(current))
+            !(current->signal & ~current->blocked))
 
        {
          unsigned long   tlimit;
index 3309a165afe161a097ba484af7d99ae681ba0cc6..6d2009dc1b3230d173b9b76b5d2b290172e54f1a 100644 (file)
@@ -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 @@ drain_midi_queue (int dev)
    */
 
   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 @@ MIDIbuf_release (int dev, struct fileinfo *file)
                                                   * devices
                                                 */
 
-      while (!signal_pending(current) &&
+      while (!(current->signal & ~current->blocked) &&
             DATA_AVAIL (midi_out_buf[dev]))
 
        {
@@ -370,8 +370,8 @@ MIDIbuf_write (int dev, struct fileinfo *file, const char *buf, int count)
                  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 @@ MIDIbuf_read (int dev, struct fileinfo *file, char *buf, int count)
          }
        input_sleep_flag[dev].opts &= ~WK_SLEEP;
       };
-      if (signal_pending(current))
+      if ((current->signal & ~current->blocked))
        c = -EINTR;             /*
                                   * The user is getting restless
                                 */
index 5a5e90e27b1e253eb4a2317d9a730ac98c92b312..58c69eed2ac9c38aad62be15dbd795ed81ed5808 100644 (file)
@@ -55,7 +55,7 @@ struct mpu_config
 
 #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 @@ mpu_input_scanner (struct mpu_config *devc, unsigned char midic)
        default:
          if (midic <= 0xef)
            {
-             /* printk("mpu time: %d ", midic); */
+             /* printk( "mpu time: %d ",  midic); */
              devc->m_state = ST_TIMED;
            }
          else
@@ -229,7 +229,7 @@ mpu_input_scanner (struct mpu_config *devc, unsigned char midic)
 
        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 @@ mpu_input_scanner (struct mpu_config *devc, unsigned char midic)
            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 @@ mpu_input_scanner (struct mpu_config *devc, unsigned char midic)
        else
          {
            devc->last_status = midic;
-           /* printk ("midi msg "); */
+           /* printk"midi msg "); */
            msg -= 8;
            devc->m_left = len_tab[msg];
 
@@ -309,7 +309,7 @@ mpu_input_scanner (struct mpu_config *devc, unsigned char midic)
          break;
 
        case 0xf6:
-         /* printk("tune_request\n"); */
+         /* printk( "tune_request\n"); */
          devc->m_state = ST_INIT;
 
          /*
@@ -342,7 +342,7 @@ mpu_input_scanner (struct mpu_config *devc, unsigned char midic)
          break;
 
        case 0xff:
-         /* printk("midi hard reset"); */
+         /* printk( "midi hard reset"); */
          devc->m_state = ST_INIT;
          break;
 
@@ -659,7 +659,7 @@ retry:
   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 @@ retry:
        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 @@ probe_mpu401 (struct address_info *hw_config)
 
   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;
     }
 
index 5c71dc585ec11b073359670a6af2869e8355778c..a0033576667a6d7e8931b3846e43b1570f43a4e7 100644 (file)
@@ -22,7 +22,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_YM3812)
+#ifdef CONFIG_YM3812
 
 #include "opl3.h"
 
@@ -111,8 +111,8 @@ opl3_ioctl (int dev,
       {
        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 @@ opl3_start_note (int dev, int voice, int note, int volume)
 
   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 --git a/drivers/sound/opl3sa.c b/drivers/sound/opl3sa.c
new file mode 100644 (file)
index 0000000..c4154d7
--- /dev/null
@@ -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 <linux/config.h>
+
+#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
index 4b1abd06e6be1fffc2ebd9813349df9b2b688e35..1acffb7684ef30aa1c81543fc0dfd247c0465110 100644 (file)
@@ -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 <linux/module.h>
 #include <linux/version.h>
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
 #endif
 #if LINUX_VERSION_CODE > 131328
 #define LINUX21X
 #include <linux/poll.h>
 #include <linux/pci.h>
 #include <linux/bios32.h>
-#else
 #endif
 
 #include <linux/wrapper.h>
-
 #include <linux/soundcard.h>
 
-
 #define FALSE  0
 #define TRUE   1
 
-
-
 struct snd_wait {
          volatile int opts;
        };
@@ -70,8 +57,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
 
index b109b22f14179fb32b5949e14990998b0904378e..e00bc636405dee2fc1d75186a6a211439ab0a96a 100644 (file)
@@ -7,7 +7,7 @@
 #include <linux/config.h>
 #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 @@ static unsigned char sb_dma_bits[] =
  * 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 @@ config_pas_hw (struct address_info *hw_config)
        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];
index 05a509f913bc54fcd9c188c31d8464a6263c553f..bfdde4e8f49bb9e8487b04cba6192cfb2f6aeaed 100644 (file)
@@ -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 @@ pas_midi_open (int dev, int mode,
   cli ();
 
   if ((err = pas_set_intr (0x10)) < 0)
-    return err;
+    {
+      restore_flags (flags);
+      return err;
+    }
 
   /*
    * Enable input available and output FIFO empty interrupts
@@ -287,3 +291,4 @@ pas_midi_interrupt (void)
 }
 
 #endif
+#endif
index 8a240e28b3273b9a3790a8578a8f741f07d2084e..e5444822e9d94b2007e08d3c0cad401447a9e382 100644 (file)
@@ -15,7 +15,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_PAS)
+#ifdef CONFIG_PAS
 
 #ifndef DEB
 #define DEB(what)              /* (what) */
index e082fc9cce9fa1c15812b5db0b578e49c60abe8a..bba7753d938cb729b9993e01774d88047b00276e 100644 (file)
@@ -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)
@@ -460,3 +461,4 @@ pas_pcm_interrupt (unsigned char status, int cause)
 }
 
 #endif
+#endif
index 52374790f7bbac3d8d3f3cadedd20c1fac676ece..90ac363ac4e9dcd645f47234fa66ab758b60df38 100644 (file)
@@ -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 @@ probe_pss (struct address_info *hw_config)
   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 @@ pss_download_boot (pss_confdata * devc, unsigned char *block, int size, int flag
            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 @@ pss_download_boot (pss_confdata * devc, unsigned char *block, int size, int flag
        return 0;
 
       val = inw (REG (PSS_DATA));
-      /* printk("<PSS: microcode version %d.%d loaded>", val/16, val % 16); */
+      /* printk( "<PSS: microcode version %d.%d loaded>",  val/16,  val % 16); */
     }
 
   return 1;
@@ -518,7 +518,7 @@ download_boot_block (void *dev_info, copr_buffer * buf)
 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)
     {
@@ -891,3 +891,4 @@ unload_pss_mss (struct address_info *hw_config)
 }
 
 #endif
+#endif
index ba2f3be18ec1b845be4915959dee8a0003a2c6dc..d5bf17c4b826f821ab4492b91f0321ae8e9d296f 100644 (file)
@@ -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_audio_init (sb_devc *devc, char *name);
 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
index a4e29ba8c6a205a6685111f0e88e3993b71a9149..7d589f9c14e11b3f0de811d16eac582dd3ac4a24 100644 (file)
@@ -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 @@ sb_audio_open (int dev, int mode)
 
   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 @@ sb_audio_open (int dev, int mode)
     {
       if (sound_open_dma (devc->dma16, "Sound Blaster 16 bit"))
        {
+         restore_flags (flags);
          return -EBUSY;
        }
     }
@@ -542,8 +544,12 @@ sbpro_audio_set_channels (int dev, short channels)
     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;
 }
index d6e4bc67ae163c8cbcf9793cbdd06eee3df270b6..ba4c83a7b2f0038b417933dd2aa2d1b0ef30c3f7 100644 (file)
@@ -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 @@ probe_sb (struct address_info *hw_config)
 {
   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;
     }
 
index bd9e05704e6157b565e188374105f70e9200ca14..000c599efe29fce42f07c3ca3d96ea85e0b8e977 100644 (file)
@@ -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 @@ static int      smw_ucodeLen = 0;
 
 #endif
 
+
 int
 sb_dsp_command (sb_devc * devc, unsigned char val)
 {
@@ -170,7 +171,7 @@ sbintr (int irq, void *dev_id, struct pt_regs *dummy)
        break;
 
       default:
-       /* printk ("Sound Blaster: Unexpected interrupt\n"); */
+       /* printk"Sound Blaster: Unexpected interrupt\n"); */
        ;
       }
 /*
@@ -184,12 +185,13 @@ sbintr (int irq, void *dev_id, struct pt_regs *dummy)
     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 @@ sb_dsp_reset (sb_devc * devc)
   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 @@ init_Jazz16 (sb_devc * devc, struct address_info *hw_config)
   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 @@ sb_dsp_detect (struct address_info *hw_config)
     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 @@ sb_dsp_detect (struct address_info *hw_config)
 
   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 @@ void
 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 @@ sb_dsp_init (struct address_info *hw_config)
       return;
     }
 
+
   devc = detected_devc;
   detected_devc = NULL;
 
@@ -727,7 +790,7 @@ sb_dsp_init (struct address_info *hw_config)
              }
        }
 
-#ifdef __SMP__
+#if defined(__SMP__) || defined(__FreeBSD__)
       /* Skip IRQ detection if SMP (doesn't work) */
       devc->irq_ok = 1;
 #else
@@ -735,6 +798,8 @@ sb_dsp_init (struct address_info *hw_config)
        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 @@ sb_dsp_init (struct address_info *hw_config)
  * 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 @@ sb_dsp_unload (struct address_info *hw_config)
     }
   else
     release_region (hw_config->io_base, 16);
+
 }
 
 /*
@@ -1010,8 +1076,7 @@ smw_midi_init (sb_devc * devc, struct address_info *hw_config)
 
   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 @@ probe_sbmpu (struct address_info *hw_config)
        }
       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:
index cffaafc7b2d74500ce940b67ce7140b0553cd0e9..6335ce6f5febc33f8de5515f72feb015aca928a2 100644 (file)
@@ -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
@@ -232,3 +233,4 @@ sb_dsp_midi_init (sb_devc * devc)
 }
 
 #endif
+#endif
index 5e6c578996816f78d880dbf4e7dff7d5ac68b7a1..525ad0a4f9e81c5fbd36e816d7342d6a995b8269 100644 (file)
@@ -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 @@ sb_mixer_set_stereo (sb_devc * devc, int mode)
 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 @@ sb_mixer_init (sb_devc * devc)
   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;
index 5711b91055d229ad6316bd2ee0280658ed8e1401..e6b54355e952af80e2d5672ca26ce451270cb627 100644 (file)
@@ -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)
 
@@ -283,3 +284,4 @@ static char     smw_mix_regs[] =    /* Left mixer registers */
 #define SRC__LINE        7     /* Use Line-in for recording source */
 
 #endif
+#endif
index 10e293372f5838770a9ae60c2b19bd52355f6b8c..5b1f1014904df713be6630c7feda1bb1d29b5d03 100644 (file)
@@ -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_opened[MAX_MIDI_DEV] =
 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 @@ sequencer_midi_input (int dev, unsigned char data)
   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 @@ seq_input_event (unsigned char *event_rec, int len)
 
   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 @@ seq_timing_event (unsigned char *event_rec)
          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 @@ seq_timing_event (unsigned char *event_rec)
       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 @@ play_event (unsigned char *q)
          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 @@ play_event (unsigned char *q)
     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 @@ play_event (unsigned char *q)
               */
 
              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 @@ sequencer_open (int dev, struct fileinfo *file)
        }
     }
 
-  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 @@ seq_drain_midi_queues (void)
 
   n = 1;
 
-  while (!signal_pending(current) && n)
+  while (!(current->signal & ~current->blocked) && n)
     {
       n = 0;
 
@@ -1279,7 +1311,7 @@ sequencer_release (int dev, struct fileinfo *file)
 
   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 @@ seq_sync (void)
 {
   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 @@ seq_reset (void)
   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 @@ seq_reset (void)
   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 @@ sequencer_ioctl (int dev, struct fileinfo *file,
 
       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 @@ sequencer_ioctl (int dev, struct fileinfo *file,
       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 @@ sequencer_ioctl (int dev, struct fileinfo *file,
 
        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 @@ sequencer_ioctl (int dev, struct fileinfo *file,
        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 --git a/drivers/sound/softoss.c b/drivers/sound/softoss.c
new file mode 100644 (file)
index 0000000..aeba172
--- /dev/null
@@ -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 <linux/config.h>
+
+
+/*
+ * 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 <sys/ultrasound.h>
+
+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 --git a/drivers/sound/softoss.h b/drivers/sound/softoss.h
new file mode 100644 (file)
index 0000000..1bffa23
--- /dev/null
@@ -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 --git a/drivers/sound/softoss_rs.c b/drivers/sound/softoss_rs.c
new file mode 100644 (file)
index 0000000..b74b664
--- /dev/null
@@ -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 <linux/config.h>
+
+
+#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
index 8bb54fa9d337fd6265be6a22ad27710f821de612..0d17f08c057b40c27f47df76e2213ece1e61ab4d 100644 (file)
@@ -41,6 +41,7 @@ void audio_init_devices (void);
 
 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 @@ void MIDIbuf_init(void);
  */
 
 /*     From soundcard.c        */
-#ifndef __bsdi__
+#if !defined(__bsdi__) && !defined(__NjetBSD__)
 void tenmicrosec(int *osp);
 #endif
 void request_sound_timer (int count);
index baa04be613969f7574fbae697ee7ce24d082c0e6..ab4674ae362281fcc82636260c8d91b9d526647d 100644 (file)
@@ -12,6 +12,7 @@
 
 
 #include "local.h"
+
 #include "os.h"
 #include "soundvers.h"
 
@@ -145,6 +146,7 @@ struct channel_info {
 #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
index bacdccb66e53b04b4adc075ee6cf9bc4448bd196..ed7fca7afc1718ab0da27bdc7e2fb95fafd93756 100644 (file)
@@ -195,9 +195,6 @@ init_status (void)
   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 @@ sound_open_sw (int dev, struct fileinfo *file)
        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 @@ static int
 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 @@ static int
 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;
index 450197a34224ff994b7eda8c2317fdec7f5c02e3..3eab31d8aac74229f7411149524c8a1e82724ebb 100644 (file)
@@ -56,31 +56,34 @@ static char     dma_alloc_map[8] =
 
 
 
-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 @@ sound_mmap (struct file *file, struct vm_area_struct *vma)
 
   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 @@ sound_mmap (struct file *file, struct vm_area_struct *vma)
                        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 @@ sound_free_dma (int chn)
 {
   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 @@ sound_alloc_dmap (int dev, struct dma_buffparms *dmap, int chan)
       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 @@ sound_alloc_dmap (int dev, struct dma_buffparms *dmap, int chan)
          != ((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 @@ sound_start_dma (int dev, struct dma_buffparms *dmap, int chan,
 {
   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);
index 5b9b6a5c650347b732f96049e59f6c9e124a3988..b3a8356c2167d0f71efc64e6917db1f4485082ae 100644 (file)
@@ -1,2 +1,2 @@
-#define SOUND_VERSION_STRING "3.8a"
+#define SOUND_VERSION_STRING "3.8s-971110"
 #define SOUND_INTERNAL_VERSION 0x030804
index c5cf7de3fae0ddd1348472fb299fa34efeb213f9..3485757e11ec68fbb5690bf93ddf329620f863b2 100644 (file)
@@ -15,7 +15,7 @@
 
 #include "sound_config.h"
 
-#if defined(CONFIG_SSCAPEHW)
+#ifdef CONFIG_SSCAPEHW
 
 #include "coproc.h"
 
@@ -213,15 +213,6 @@ host_read (struct sscape_info *devc)
   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 @@ set_mt32 (struct sscape_info *devc, int value)
                 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 @@ set_control (struct sscape_info *devc, int ctrl, int value)
   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);
 }
index 5bb60ea6ab6034c605012cb881db41e541b826fd..64fd1c7a86ccab3a354f6ca34c82591f6ee510d3 100644 (file)
@@ -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 @@ probe_trix_wss (struct address_info *hw_config)
 
   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;
     }
 
index e8a399cb615e5fe3993e47a6727d604e16673742..6356e28f057d267c77c453ee35e7cf0f28fc555c 100644 (file)
@@ -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 @@ enter_uart_mode (uart401_devc * devc)
 
   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 @@ attach_uart401 (struct address_info *hw_config)
     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 @@ attach_uart401 (struct address_info *hw_config)
   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 @@ reset_uart401 (uart401_devc * devc)
 
   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 @@ reset_uart401 (uart401_devc * devc)
 
   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 @@ unload_uart401 (struct address_info *hw_config)
   int             irq = hw_config->irq;
 
   if (irq < 0)
-    irq *= -1;
+    {
+      irq *= -1;
+    }
 
   if (irq < 1 || irq > 15)
     return;
@@ -460,3 +463,4 @@ unload_uart401 (struct address_info *hw_config)
 
 
 #endif
+#endif
index 547cbba92dc98cb1cfd06cf0f55665054cf217af..a814e4cde88ca0569f65ff107477334594f05b18 100644 (file)
@@ -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;
 
@@ -332,3 +333,4 @@ unload_uart6850 (struct address_info *hw_config)
 }
 
 #endif
+#endif
index 6025dcd31444fc2af64dd5ea65c1841557ca8195..b2c96cb377fefbab97c078294396dff2b6977117 100644 (file)
@@ -553,6 +553,11 @@ static inline struct buffer_head * find_buffer(kdev_t dev, int block, int size)
        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 @@ printk("refill_freelist: waking bdflush\n");
        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 @@ get_free:
        /* 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<<BH_Touched);
-       bh->b_dev=dev;
-       bh->b_blocknr=block;
        insert_into_queues(bh);
        return bh;
 
@@ -1254,18 +1273,75 @@ static inline void free_async_buffers (struct buffer_head * bh)
 
        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 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
                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);
 
                /*
@@ -1382,74 +1453,6 @@ void mark_buffer_uptodate(struct buffer_head * bh, int on)
        clear_bit(BH_Uptodate, &bh->b_state);
 }
 
-/*
- * 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.
@@ -1755,7 +1758,7 @@ asmlinkage int sync_old_buffers(void)
        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 @@ int bdflush(void * unused)
                 * 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 @@ int bdflush(void * unused)
                
                /* 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);
                }
index bf312c1b204107595c2c145457c87430b6d7a540..febf1400c0cab720e854d674bd38312987f36022 100644 (file)
@@ -97,12 +97,14 @@ repeat:
                        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 @@ static inline void prune_one_dentry(struct dentry * dentry)
        struct dentry * parent;
 
        list_del(&dentry->d_hash);
+       list_del(&dentry->d_child);
        if (dentry->d_inode) {
                struct inode * inode = dentry->d_inode;
 
@@ -337,6 +340,69 @@ repeat:
        }
 }
 
+/*
+ * 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 @@ printk("d_alloc: %d unused, pruning dcache\n", dentry_stat.nr_unused);
        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 @@ void d_move(struct dentry * dentry, struct dentry * target)
        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);
 }
 
 /*
index b4e879a3e1e24c61b6f044dff24e3009f1597fad..1ebdbf4449f5d367799c5ada049c2108ac40967c 100644 (file)
@@ -353,8 +353,6 @@ int ext2_create (struct inode * dir, struct dentry * dentry, int mode)
        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 @@ int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
        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 @@ int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
                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 @@ int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
                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 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        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 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
                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 @@ int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
        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 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry)
        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 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry)
                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 @@ int ext2_rmdir (struct inode * dir, struct dentry *dentry)
 
 end_rmdir:
        brelse (bh);
+out:
        return retval;
 }
 
@@ -656,11 +669,11 @@ int ext2_unlink(struct inode * dir, struct dentry *dentry)
        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 @@ int ext2_unlink(struct inode * dir, struct dentry *dentry)
                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 @@ int ext2_unlink(struct inode * dir, struct dentry *dentry)
 
 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 @@ int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symnam
        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 @@ int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symnam
        }
        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 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
                                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 @@ static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
                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;
index c8551a26c5b9b71322652c0f7834a38aab2aff4d..ece255ca1060c0c31a7fcbc1b29ab63f202b17ec 100644 (file)
@@ -188,7 +188,8 @@ printk("cache add: <%s,%d> %d (%d)\n", kdevname(inode->i_dev),
                    && 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;
                        }
index 0707c922c0d1df863deeecde5a2150dbf2ccbb6d..7568f876a7b922f4e51fe085e8b88e057ab81929 100644 (file)
@@ -97,7 +97,7 @@ uni16_to_x8(unsigned char *ascii, unsigned char *uni, int uni_xlate,
        return (op - ascii);
 }
 
-
+#if 0
 static void dump_de(struct msdos_dir_entry *de)
 {
        int i;
@@ -109,7 +109,7 @@ static void dump_de(struct msdos_dir_entry *de)
        }
        printk("]\n");
 }
-
+#endif
 int fat_readdirx(
        struct inode *inode,
        struct file *filp,
index db077c0c875b72209733aad0b99453f93ecb614b..732d0b9baea7f914956f02f0809440d6f9909ec5 100644 (file)
@@ -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 <linux/version.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 
 #include "msbuffer.h"
 
-#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0)
 #include <asm/uaccess.h>
-#define FAT_COPY_TO_USER(uaddr, kaddr, len) copy_to_user(uaddr, kaddr, len)
-#else
-#include <asm/segment.h>
-#define FAT_COPY_TO_USER(uaddr, kaddr, len) memcpy_tofs(uaddr, kaddr, len)
-#endif
 #include <asm/unaligned.h>
 
-#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 @@ void fat_put_super(struct super_block *sb)
        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 @@ static int parse_options(char *options,int *fat, int *blksize, int *debug,
 {
        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 @@ static int parse_options(char *options,int *fat, int *blksize, int *debug,
        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 @@ static int parse_options(char *options,int *fat, int *blksize, int *debug,
                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 @@ static int parse_options(char *options,int *fat, int *blksize, int *debug,
                }
                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 @@ static int parse_options(char *options,int *fat, int *blksize, int *debug,
                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 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
                        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 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
        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 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
                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 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
                       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 @@ struct super_block *fat_read_super(struct super_block *sb,void *data, int silent
        }
        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)
 {
index b12440005461df2f32d91590abf814db6bdf6e03..545874a048d7164e60d7261e30134f45f5f86fc8 100644 (file)
 #include <linux/malloc.h>
 #include <linux/msdos_fs.h>
 
-#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0)
 #include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#endif
 #include <asm/system.h>
 
 /*
index 2664f4bde2635f2d529c10b3a7d2daf50bba2254..fa9694a081de5e55ab6ff2b1863ee6a79e5b8934 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/lockd/bind.h>
 #include <linux/lockd/xdr.h>
 #include <linux/init.h>
+#include <linux/nls.h>
 
 extern void device_setup(void);
 extern void binfmt_setup(void);
index 91a744562b37f665a41b6c34f7832cd0304f297d..a0eb8165a12e1cedd4b6b610fe75058209b3c6ec 100644 (file)
 
 #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 @@ int msdos_lookup(struct inode *dir,struct dentry *dentry)
 
        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 @@ int msdos_lookup(struct inode *dir,struct dentry *dentry)
        }
        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 @@ int msdos_lookup(struct inode *dir,struct dentry *dentry)
                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 @@ int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
        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 @@ static void dump_fat(struct super_block *sb,int start)
 /***** 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 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
        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 @@ int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
 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 @@ static int msdos_unlinkx(
        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 @@ static int msdos_unlinkx(
        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 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,
                : (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 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,
                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);
        }
index 04bff0d41d67918ca7448ff0b298586b99809637..6bb9993f013f4c85da279b559ae8e00f59435952 100644 (file)
@@ -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
  *
  */
 
 #include <linux/malloc.h>
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
-#include <linux/ncp_fs.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 #include <linux/errno.h>
 #include <linux/locks.h>
+
+#include <linux/ncp_fs.h>
 #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 @@ struct ncp_dirent {
        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 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 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_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 inline void str_upper(char *name)
-{
-       while (*name) {
-               if (*name >= 'a' && *name <= 'z') {
-                       *name -= ('a' - 'A');
-               }
-               name++;
-       }
-}
+static ssize_t ncp_dir_read(struct file *, char *, size_t, loff_t *);
+static int ncp_readdir(struct file *, void *, filldir_t);
 
-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 inline int ncp_preserve_case(struct inode *i)
-{
-       return (ncp_namespace(i) == NW_NS_OS2);
-}
+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 @@ struct inode_operations ncp_dir_inode_operations =
        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 inline int ncp_single_volume(struct ncp_server *server)
+static struct dentry_operations ncp_dentry_operations =
 {
-       return (server->m.mounted_vol[0] != '\0');
-}
+       ncp_lookup_validate,    /* d_validate(struct dentry *) */
+       ncp_hash_dentry,        /* d_hash */
+       ncp_compare_dentry,     /* d_compare */
+       ncp_delete_dentry       /* d_delete(struct dentry *) */
+};
 
-inline ino_t
- ncp_info_ino(struct ncp_server * server, struct ncp_inode_info * info)
+static int 
+ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
 {
-       return ncp_single_volume(server)
-           ? info->finfo.i.dirEntNum : (ino_t) info;
+  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; i<this->len ; i++)
+    hash = partial_name_hash(al[i],hash);
+  this->hash = end_name_hash(hash);
+  
+  return 0;
 }
 
-static inline int ncp_is_server_root(struct inode *inode)
+static int
+ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
 {
-       struct ncp_server *s = NCP_SERVER(inode);
+       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);
 
-       return ((!ncp_single_volume(s))
-               && (inode->i_ino == ncp_info_ino(s, &(s->root))));
+       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);
 }
 
-struct ncp_inode_info *
- ncp_find_inode(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 *server = NCP_SERVER(inode);
-       struct ncp_inode_info *root = &(server->root);
-       struct ncp_inode_info *this = root;
-
-       ino_t ino = inode->i_ino;
+       struct inode *inode = dentry->d_inode;
 
-       do {
-               if (ino == ncp_info_ino(server, this)) {
-                       return this;
+       if (inode)
+       {
+               if (is_bad_inode(inode))
+               {
+                       d_drop(dentry);
                }
-               this = this->next;
+               /*
+                * 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? */
        }
-       while (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.
+ */
+
+/*
+ * Generate a unique inode number.
+ */
+ino_t ncp_invent_inos(unsigned long n)
+{
+       static ino_t ino = 1;
 
-       return NULL;
+       if (ino + 2*n < ino)
+       {
+               /* wrap around */
+               ino += n;
+       }
+       ino += n;
+       return ino;
 }
 
-static long ncp_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count)
+/*
+ * 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)
 {
-       return -EISDIR;
+       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 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_single_volume(struct ncp_server *server)
+{
+       return (server->m.mounted_vol[0] != '\0');
+}
+
+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 @@ static inline void ncp_unlock_dircache(void)
        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 @@ static int ncp_readdir(struct file *filp,
        }
        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 @@ static int ncp_readdir(struct file *filp,
                /* Nothing found, even from a ncp call */
                goto finished;
        }
+
        while (index < c_size) {
                ino_t ino;
-
-               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("ncp_readdir: entry->path= %s\n", entry->i.entryName);
-               DDPRINTK("ncp_readdir: entry->f_pos = %ld\n", entry->f_pos);
+               struct qstr qname;
+
+               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);
+
+               /* 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 @@ static int ncp_readdir(struct file *filp,
        }
       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_invalid_dir_cache(struct inode *ino)
 
 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 @@ void ncp_free_dir_cache(void)
        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);
-
-       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();
-       }
+       int result;
+
+       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];
-
-       if ((name[0] == '.')
-           && ((len == 1)
-               || ((len == 2)
-                   && (name[1] == '.')))) {
-               iput(dir);
-               return -EEXIST;
+       struct ncpfs_inode_info finfo;
+       __u8 _name[dentry->d_name.len + 1];
+
+       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];
-
-       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);
+       int error, result;
+       __u8 _name[dentry->d_name.len + 1];
+
+       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;
-       }
-       if (!ncp_conn_valid(NCP_SERVER(dir))) {
-               iput(dir);
-               return -EIO;
+               printk(KERN_WARNING "ncp_unlink: inode is NULL or not a directory\n");
+               goto out;
+       }
+       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;
-       }
-       if ((ncp_find_dir_inode(old_dir, old_name) != NULL)
-           || (ncp_find_dir_inode(new_dir, new_name) != NULL)) {
-               res = -EBUSY;
-               goto finished;
+               printk(KERN_WARNING "ncp_rename: new inode is NULL or not a directory\n");
+               goto out;
        }
-       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 ncp_rename(struct inode *old_dir, const char *old_name, int old_len,
 
 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 @@ static int local2utc(int time)
 }
 
 /* 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;
 
index 9a2067848045f08a6debf5d31f52bdc91c1e7b7e..2f22f25d10eedc6fc198187c94e0eb62f7ebad94 100644 (file)
@@ -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 @@ static int ncp_fsync(struct file *file, struct dentry *dentry)
        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;
+       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;
        }
-       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;
+       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;
                }
+               /*
+                * Update the inode information.
+                */
+       update:
+               ncp_update_inode(inode, &finfo);
        }
-       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;
+       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;
-       }
-       if ((errno = ncp_make_open(inode, O_RDONLY)) != 0) {
-               return errno;
+       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;
        }
-       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 @@ static long ncp_file_read(struct inode *inode, struct file *file, char *buf, uns
                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 @@ static long ncp_file_read(struct inode *inode, struct file *file, char *buf, uns
        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 @@ static long ncp_file_write(struct inode *inode, struct file *file, const char *b
        }
 
        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 =
index 2e903151e1806e6b37f41e3bb64c04a4e59e1f18..61cdd03191e7a53b75218a112e7b3b82fa997664 100644 (file)
@@ -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 <asm/byteorder.h>
 
 #include <linux/sched.h>
-#include <linux/ncp_fs.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/string.h>
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
+
+#include <linux/ncp_fs.h>
 #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_read_inode(struct inode *inode)
        }
 }
 
-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 @@ struct super_block *
        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 @@ struct super_block *
           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 @@ struct super_block *
 
        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) {
-               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));
+       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;
 
-       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 @@ static void ncp_put_super(struct super_block *sb)
        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 @@ static void ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
        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 @@ static int ncp_notify_change(struct inode *inode, struct iattr *attr)
                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 @@ static int ncp_notify_change(struct inode *inode, struct iattr *attr)
        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 @@ static int ncp_notify_change(struct inode *inode, struct iattr *attr)
 
                /* 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 @@ EXPORT_NO_SYMBOLS;
 
 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 @@ int init_module(void)
 
 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
 }
 
index 9b88c3c9f1fd4dca9a2bf202c57c263655662afd..e9f0ca86c46ce6b18f3ec8e307be0d7caa6ff57d 100644 (file)
@@ -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 <asm/uaccess.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
-#include <linux/ncp_fs.h>
 #include <linux/ioctl.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+
 #include <linux/ncp.h>
+#include <linux/ncp_fs.h>
 
 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 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
 
                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 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
                                          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;
index 5dfdeb27fe44974e30bb36b3f67ca14579fa90a5..2a140ea55dda445738183de1bebe6921d22a9613 100644 (file)
@@ -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 inline int min(int a, int b)
 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 @@ int ncp_mmap(struct file *file, struct vm_area_struct *vma)
 {
        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 @@ int ncp_mmap(struct file *file, struct vm_area_struct *vma)
                return -EACCES;
        if (!IS_RDONLY(inode)) {
                inode->i_atime = CURRENT_TIME;
-               mark_inode_dirty(inode);
        }
 
        vma->vm_dentry = dget(file->f_dentry);
index f7d4de4a1003c96483f807999676940dbdbc689b..8d60732ef27dc5ff9570ee0dfccb6fbe48374846 100644 (file)
@@ -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 inline int min(int a, int b)
 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 @@ static void ncp_add_pstring(struct ncp_server *server, const char *s)
        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 @@ static __u32
        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 @@ int ncp_negotiate_buffersize(struct ncp_server *server,
        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 @@ int ncp_get_volume_info_with_number(struct ncp_server *server, int n,
        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 @@ int ncp_get_volume_info_with_number(struct ncp_server *server, int n,
 
        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 @@ int ncp_close_file(struct ncp_server *server, const char *file_id)
        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 @@ static void ncp_extract_file_info(void *structure, struct nw_info_struct *target
        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 @@ static inline int ncp_has_os2_namespace(struct ncp_server *server, __u8 volume)
 
        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 @@ int ncp_lookup_volume(struct ncp_server *server,
 
        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_lookup_volume(struct ncp_server *server,
 }
 
 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_modify_file_or_subdir_dos_info(struct ncp_server *server,
 }
 
 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 @@ static inline void ConvertToNWfromDWORD(__u32 sfd, __u8 ret[6])
 /* 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 @@ int ncp_open_create_file_or_subdir(struct ncp_server *server,
        /* 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 @@ int ncp_search_for_file_or_subdir(struct ncp_server *server,
        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 @@ int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
 
        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 @@ int ncp_read(struct ncp_server *server, const char *file_id,
        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 @@ int ncp_write(struct ncp_server *server, const char *file_id,
        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;
 }
index 48425adb5ab1133c0a1415a4a355ce21828b247f..69388576b4dcbcb96b79b1d194a4308a102f97ec 100644 (file)
@@ -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 <linux/fs.h>
-#include <linux/ncp.h>
-#include <linux/ncp_fs.h>
-#include <linux/ncp_fs_sb.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/malloc.h>
 #include <asm/string.h>
 
 #include <linux/ncp.h>
+#include <linux/ncp_fs.h>
+#include <linux/ncp_fs_sb.h>
 
-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_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_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_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 *, 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_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 */
index 30421571febea92ff9a5b64a14d404a9b06513bf..c6c8d6f58ae7c9d2fee98c6d978f910771da0557 100644 (file)
@@ -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 <linux/sched.h>
-#include <linux/ncp_fs.h>
 #include <linux/errno.h>
 #include <linux/socket.h>
 #include <linux/fcntl.h>
 #include <linux/mm.h>
 #include <linux/netdevice.h>
 #include <net/scm.h>
+#include <net/sock.h>
 #include <linux/ipx.h>
+#include <linux/poll.h>
 
 #include <linux/ncp.h>
 #include <linux/ncp_fs.h>
 #include <linux/ncp_fs_sb.h>
-#include <net/sock.h>
-#include <linux/poll.h>
 
 static int _recv(struct socket *sock, unsigned char *ubuf, int size,
                 unsigned flags)
@@ -82,7 +82,7 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
        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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
        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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
        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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
                         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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
                                 * 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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
                                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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
                                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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
                               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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
                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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
                        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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
 /* 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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
                 * 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 @@ static int do_ncp_rpc_call(struct ncp_server *server, int size)
         */
        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 @@ static int ncp_do_request(struct ncp_server *server, int size)
        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 @@ static int ncp_do_request(struct ncp_server *server, int size)
        }
        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 @@ static int ncp_do_request(struct ncp_server *server, int size)
 
 /* 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 @@ int ncp_request(struct ncp_server *server, int function)
        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 @@ int ncp_request(struct ncp_server *server, int function)
 
        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 @@ void ncp_lock_server(struct ncp_server *server)
 #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_lock_server(struct ncp_server *server)
 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);
index 115c84d458164d430ae04238d9e4c5092e72f8fa..166f664d9866a01a66fe30fef799a231ca6b5952 100644 (file)
 
 #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 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
        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 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
        }
        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 @@ old_dentry->d_parent->d_name.name, old_dentry->d_name.name);
         * 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
index 2e83b1b4890a41a8256720fb0834e2ee52a0bbb6..76456af5fc6cbabd58e3f8b8aa4e0e9c918dc6b1 100644 (file)
@@ -166,6 +166,32 @@ __initfunc(static void check_amd_k6(void))
        }
 }
 
+/*
+ * 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 @@ __initfunc(static void check_bugs(void))
        check_hlt();
        check_popad();
        check_amd_k6();
+       check_pentium_f00f();
        system_utsname.machine[1] = '0' + x86;
 }
index d5dba06d7d8051610172e5ac2efcd03a9a39e3f0..0d611cef4edf0e2ce7d8b22f0bbda2047768fcd7 100644 (file)
  *             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
 /*
  * 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 @@ extern inline void outs##s(unsigned short port, const void * addr, unsigned long
 : "=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
@@ -99,71 +96,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__
 
 #include <linux/vmalloc.h>
index cdfb0ff2ecab10ddf4eee57bfb676b0498120f51..382d17729ab2417dfda93434741510cd906cc362 100644 (file)
@@ -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 @@ typedef struct awe_patch_info {
 
        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 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 optarg;                   /* optional argument */
+       int len;                        /* data length (without this header) */
+
+       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 @@ typedef struct awe_patch_info {
 #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_parm {
 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 @@ typedef struct _awe_voice_info {
        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 @@ typedef struct _awe_voice_info {
  * 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_voice_rec {
 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 @@ typedef struct awe_sample_info {
 #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 @@ typedef struct _awe_mode_rec {
 #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 @@ typedef struct _awe_mode_rec {
 #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 @@ typedef struct _awe_mode_rec {
 #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 @@ enum {
 /*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 */
index 66ef4b47efd0b4bc77968c1df6f9762abd19018b..0625942de1e43fdceb91447e3c09079a6387fea9 100644 (file)
@@ -461,8 +461,7 @@ static void end_request(int uptodate) {
        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) {
index faae32756027e3fe4e0e7ba119adf08d169aed89..9bad9538630e6072e718111729f9a7136fedca53 100644 (file)
@@ -55,9 +55,12 @@ extern struct blk_dev_struct blk_dev[MAX_BLKDEV];
 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];
 
@@ -65,4 +68,6 @@ extern int * blksize_size[MAX_BLKDEV];
 
 extern int * hardsect_size[MAX_BLKDEV];
 
+extern int * max_readahead[MAX_BLKDEV];
+
 #endif
index ef9db064f6dc6e1794627c7a3cf8444a705cf6ce..bddb6706762be936b6a66e22fe3d741170a9790e 100644 (file)
@@ -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 <marcio@cyclades.com> 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 @@ struct cyclades_monitor {
 #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 @@ struct cyclades_monitor {
 #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 @@ struct CYZ_BOOT_CTRL {
  *     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 @@ typedef unsigned char        ucchar;         /* 8 bits, unsigned */
 #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 @@ struct RUNTIME_9060 {
 #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 @@ struct CH_CTRL {
        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 @@ struct BOARD_CTRL {
 
        /* 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 @@ struct ZFW_CTRL {
 
 
 
+
 #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 @@ struct cyclades_card {
     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 @@ struct cyclades_port {
        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 @@ struct cyclades_port {
 
 
 
-#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 @@ struct cyclades_port {
 #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 @@ struct cyclades_port {
 #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 */
-
index f5d7b88cfbb0a934253c1ba6cb3357549c2e946d..4565960739eec49bcaa98bb3972786e28de74204 100644 (file)
@@ -57,10 +57,13 @@ struct dentry {
        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 void d_delete(struct dentry *);
 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)
index 2fd95bec0bc443e2bf0c99447464bd84ecd883e4..a5dfa8463fb256246497f331c7e958512bb85019 100644 (file)
@@ -164,7 +164,6 @@ typedef char buffer_block[BLOCK_SIZE];
 #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 {
        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 struct file * get_empty_filp(void);
 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 *);
index 43ff8b36b60626ab0f195f3b41ca71e39e4cbeb0..9cf48e4d03dcd5093a2329f710d02696b6cb17ae 100644 (file)
@@ -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 @@ struct sockaddr_ipx
 
 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 @@ typedef struct ipx_config_data
 
 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];
index 9e32ef883a69568d4bb01b8f9341610d0b3ce92d..2094a4d19f7938ed95fd132f5d4c3bfe663ef077 100644 (file)
@@ -26,8 +26,11 @@ extern inline void lock_buffer(struct buffer_head * bh)
                __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
index 2777a7191e69e76f3fdadfa0d1c47c47f4633757..cb0d899e264a1492a94b641ea1c388019172c470 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
    md.h : Multiple Devices driver for Linux
           Copyright (C) 1994-96 Marc ZYNGIER
 #define _MD_H
 
 #include <linux/major.h>
-#include <linux/mm.h>
 #include <linux/ioctl.h>
+#include <linux/types.h>
+
+/*
+ * 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)
 #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 <linux/types.h>
+#include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/blkdev.h>
 
+/*
+ * 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 @@ struct real_dev
   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
index 2100669c087312ac3902d2a08a905fc90dedb935..a0770c23f1c9543a5c228d944b77630b11018820 100644 (file)
@@ -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
index 7fb97f9d5eaf3cce285c9407301f96b9144bf4fe..039ad1559ad6a866ab752172d383504edb547411 100644 (file)
@@ -55,6 +55,20 @@ struct ncp_fs_info {
 
 #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 @@ struct ncp_fs_info {
 
 #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 @@ static inline void ncp_kfree_s(void *obj, int size)
 
 #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 @@ int ncp_disconnect(struct ncp_server *server);
 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__ */
 
index 1b32dcebedec7b2a061bb56bdd3106efa7af399b..e233695c01f557b851f5e2ea346b064dfd04c707 100644 (file)
@@ -133,12 +133,11 @@ extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
 /*
  * 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,21 +150,15 @@ extern struct inode_operations nfs_file_inode_operations;
  * 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
  */
index 2f84b986416ba40c686abd7e10edff72f0fbca9d..5cff15720ac0ae312dc4f7ce3481ec3365f92e57 100644 (file)
@@ -147,6 +147,9 @@ enum net_directory_inos {
 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 @@ enum scsi_directory_inos {
        PROC_SCSI_BUSLOGIC,
        PROC_SCSI_U14_34F,
        PROC_SCSI_FDOMAIN,
+       PROC_SCSI_GDTH,
        PROC_SCSI_GENERIC_NCR5380,
        PROC_SCSI_IN2000,
        PROC_SCSI_PAS16,
diff --git a/include/linux/radio.h b/include/linux/radio.h
new file mode 100644 (file)
index 0000000..5d6a885
--- /dev/null
@@ -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 <linux/ioctl.h>
+
+/*
+ * 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 --git a/include/linux/raid1.h b/include/linux/raid1.h
new file mode 100644 (file)
index 0000000..2f4e525
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef _RAID1_H
+#define _RAID1_H
+
+#include <linux/md.h>
+
+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 --git a/include/linux/raid5.h b/include/linux/raid5.h
new file mode 100644 (file)
index 0000000..214f49d
--- /dev/null
@@ -0,0 +1,110 @@
+#ifndef _RAID5_H
+#define _RAID5_H
+
+#ifdef __KERNEL__
+#include <linux/md.h>
+#include <asm/atomic.h>
+
+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
index 1626ea917b95398e3292c504eb1df4c43c66da83..bfd28359e53d11b8243eedf7e9f153fa58339d72 100644 (file)
@@ -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 */
 #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 @@ typedef struct copr_msg {
  * 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 @@ typedef struct copr_msg {
 #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 @@ typedef struct copr_msg {
 
 #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 @@ typedef struct copr_msg {
 #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)
index 374a899ba663230fda426f12b941a22a9e0c9335..ed7b7adbbe144c80d84ebd45c6ea1da5d621a254 100644 (file)
@@ -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 @@ typedef struct
 
 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 @@ extern void ipxrtr_device_down(struct device *dev);
 
 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 @@ typedef struct ipx_interface {
 }      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];
index f48f02c2e7e4ddc0c639bcd803aea880d7c9b33d..c503aeec0801bb6c59800cde7f7354c3ec56b37e 100644 (file)
 /* 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.
index a0c85d7b2ff237771f79b1a78d3ad9d47cde3314..9aaf4e308241f5c0f6649a48e0863e764db1ea1c 100644 (file)
@@ -117,6 +117,7 @@ extern void generic_NCR5380_setup(char *str, int *intr);
 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);
@@ -405,6 +406,9 @@ struct {
 #ifdef CONFIG_SCSI_AHA1542
        { "aha1542=", aha1542_setup},
 #endif
+#ifdef CONFIG_SCSI_GDTH
+       { "gdth=", gdth_setup},
+#endif
 #ifdef CONFIG_SCSI_AIC7XXX
        { "aic7xxx=", aic7xxx_setup},
 #endif
index b8fec5e9dfcf17bf2d4c4202c226125a51f5c857..536b5d3075d0d389a1375b3124ccabc7108cd085 100644 (file)
@@ -184,7 +184,6 @@ EXPORT_SYMBOL(__bforget);
 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(blkdev_release);
 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);
index 5e20e4b23a4f4e84007deda46e60e74a875e7b58..a6eb1b2a36455a5db5bbc51b9f5b7bed42ae55b5 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/swap.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/blkdev.h>
 
 #include <asm/system.h>
 #include <asm/pgtable.h>
@@ -445,12 +446,20 @@ static void profile_readahead(int async, struct file *filp)
 #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 @@ static inline unsigned long generic_file_readahead(int reada_ok, struct file * f
 
                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 generic_file_read(struct file * filp, char * buf,
        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 @@ ssize_t generic_file_read(struct file * filp, char * buf,
 
                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 (;;) {
index 71bdea02e8f3f7b489d630f6b05dba691217bea9..5edcb4a9c8ec9c3f8ec3e8b5ba34e19b3db15f6e 100644 (file)
@@ -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 <linux/types.h>
@@ -339,7 +340,7 @@ static struct sk_buff *ip_glue(struct ipq *qp)
        /* 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);
index bf660cf0bc7cebd41a99fddf6bc0a7e927978e51..8ed6fd46affb9b2695ef0c5b0daf3b81f5893253 100644 (file)
@@ -221,7 +221,7 @@ static void ipx_destroy_socket(struct sock *sk)
  * 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 @@ static ipx_interface *ipxitf_find_using_phys(struct device *dev, unsigned short
        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 @@ static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
        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 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
        }
 
        /* 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 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
                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 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
                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 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
                        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 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
        }
 #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 @@ static int ipxitf_create_internal(ipx_interface_definition *idef)
                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 @@ static int ipxitf_create(ipx_interface_definition *idef)
        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 @@ static int ipxitf_create(ipx_interface_definition *idef)
        }
 
        /* 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 @@ static ipx_interface *ipxitf_auto_create(struct device *dev,
        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 int ipxitf_ioctl(unsigned int cmd, void *arg)
 *                                                                                                                  *
 \*******************************************************************************************************************/
 
-static ipx_route *ipxrtr_lookup(unsigned long net)
+static ipx_route *ipxrtr_lookup(__u32 net)
 {
        ipx_route *r;
 
@@ -1188,7 +1188,7 @@ static ipx_route *ipxrtr_lookup(unsigned long net)
        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 @@ static int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, stru
        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 @@ static int ipx_interface_get_info(char *buffer, char **start, off_t offset,
        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 @@ static int ipx_get_info(char *buffer, char **start, off_t offset,
 #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 @@ static int ipx_get_info(char *buffer, char **start, off_t offset,
                        } 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 @@ static int ipx_rt_get_info(char *buffer, char **start, off_t offset,
                        "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 @@ static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
                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 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
 #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_data(char *str,unsigned char *d, int len)
 
 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));
 }