]> git.neil.brown.name Git - history.git/commitdiff
Import 2.1.28 2.1.28
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:12:58 +0000 (15:12 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:12:58 +0000 (15:12 -0500)
107 files changed:
Documentation/Changes
drivers/sound/.objects
drivers/sound/.version
drivers/sound/CHANGELOG
drivers/sound/Config.in
drivers/sound/Config.std
drivers/sound/Makefile.old [deleted file]
drivers/sound/Readme
drivers/sound/Readme.aedsp16 [deleted file]
drivers/sound/Readme.cards
drivers/sound/Readme.linux
drivers/sound/ad1848.c
drivers/sound/ad1848_mixer.h
drivers/sound/adlib_card.c
drivers/sound/aedsp16.c [deleted file]
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/finetune.h
drivers/sound/gus_card.c
drivers/sound/gus_midi.c
drivers/sound/gus_vol.c
drivers/sound/gus_wave.c
drivers/sound/ics2101.c
drivers/sound/iwmem.h
drivers/sound/lowlevel/ChangeLog.awe
drivers/sound/lowlevel/Config.tmpl
drivers/sound/lowlevel/Makefile
drivers/sound/lowlevel/README.aedsp16 [new file with mode: 0644]
drivers/sound/lowlevel/README.awe
drivers/sound/lowlevel/aci.c
drivers/sound/lowlevel/aedsp16.c [new file with mode: 0644]
drivers/sound/lowlevel/awe_config.h [new file with mode: 0644]
drivers/sound/lowlevel/awe_hw.h
drivers/sound/lowlevel/awe_voice.h [deleted file]
drivers/sound/lowlevel/awe_wave.c
drivers/sound/lowlevel/init.c
drivers/sound/mad16.c
drivers/sound/maui.c
drivers/sound/midi_ctrl.h
drivers/sound/midi_synth.c
drivers/sound/midi_synth.h
drivers/sound/midibuf.c
drivers/sound/mpu401.c
drivers/sound/opl3.c
drivers/sound/opl3.h
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/sound_calls.h
drivers/sound/sound_config.h
drivers/sound/sound_switch.c
drivers/sound/sound_timer.c
drivers/sound/soundcard.c
drivers/sound/soundvers.h
drivers/sound/sscape.c
drivers/sound/sys_timer.c
drivers/sound/trix.c
drivers/sound/uart401.c
drivers/sound/uart6850.c
drivers/sound/vivo.c [deleted file]
fs/buffer.c
include/linux/awe_voice.h [new file with mode: 0644]
include/linux/soundcard.h
include/linux/ultrasound.h
include/net/inet_common.h
include/net/raw.h
include/net/rawv6.h
include/net/sock.h
include/net/tcp.h
include/net/udp.h
kernel/sched.c
net/core/sock.c
net/ipv4/af_inet.c
net/ipv4/icmp.c
net/ipv4/ip_input.c
net/ipv4/ip_sockglue.c
net/ipv4/packet.c
net/ipv4/proc.c
net/ipv4/raw.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_output.c
net/ipv4/tcp_timer.c
net/ipv4/udp.c
net/ipv6/af_inet6.c
net/ipv6/icmp.c
net/ipv6/ipv6_input.c
net/ipv6/raw.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/netsyms.c

index 37722f375d724c544e258328f8b5aa9ddb27f43a..f5b7c90c61e048787ad5e49558e2d10ce2cc0892 100644 (file)
@@ -26,7 +26,7 @@ HTML-ized shopping list.
 http://www.datanet.hu/generations/linux/Changes2.html is an
 English-language HTML version.
 
-Last updated: February 11, 1997.
+Last updated: February 27, 1997.
 Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
 
 Current Minimal Requirements
@@ -38,7 +38,7 @@ encountered a bug!
 - Kernel modules        modutils-2.1.23
 - Gnu C                 2.7.2.1
 - Binutils              2.7.0.3
-- Linux C Library       5.4.17
+- Linux C Library       5.4.23
 - Dynamic Linker (ld.so) 1.8.5
 - Linux C++ Library     2.7.2.1
 - Procps                1.01
@@ -73,7 +73,7 @@ accordingly.
    For modules to work, you need to be running libc-5.4.x or greater.
 Since updates to libc fix other problems as well (security flaws, for
 example) and since 5.4.7 is missing a few needed symbols, try to get
-the latest 5.4.x you can.  Currently, libc-5.4.17 is the latest public
+the latest 5.4.x you can.  Currently, libc-5.4.23 is the latest public
 release.
 
    If you upgrade to libc-5.4.x, you also have to upgrade your dynamic
@@ -113,12 +113,6 @@ information to them if you need to replace the automatic routes.
 in some standard tools.  Check in /proc/net/rt_local to verify their
 presence.
 
-SysVinit
-========
-
-   To avoid an oops while shutting down or rebooting the computer,
-upgrade to SysVinit 2.69.
-
 RPM
 ===
 
@@ -160,8 +154,6 @@ Libc++: ls -l /usr/lib/libg++.so.*
 Modutils: insmod -V
 Mount: mount --version
 Procps: ps --version
-SysVinit: strings `egrep -li INIT_VERSION=sysvinit- /proc/*/environ | head
--1` | egrep -i INIT_VERSION=sysvinit-
 RPM:  rpm --version
 Sh-utils:  expr --v
 
@@ -191,12 +183,12 @@ ftp://sunsite.unc.edu/pub/Linux/GCC/release.gcc-2.7.2.1
 Linux C Library
 ===============
 
-The 5.4.17 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.17.bin.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.17.bin.tar.gz
-Installation notes for 5.4.17:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.17
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.13
+The 5.4.23 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.23.bin.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.23.bin.tar.gz
+Installation notes for 5.4.23:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.23
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.23
 
 Linux C++ Library
 =================
@@ -227,14 +219,7 @@ Procps utilities
 
 The 1.01 release:
 ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.01.tgz
-ftp://sunsite.unc.edu/pub/Linux/system/Status/ps/procps-1.01.tgz
-
-SysVinit utilities
-==================
-
-The 2.69 release (when it gets there):
-ftp://tsx-11.mit.edu/pub/linux/sources/sbin/sysvinit-2.69.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/system/Daemons/init/sysvinit-2.69.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/system/status/ps/procps-1.01.tgz
 
 RPM utilities
 =============
@@ -254,7 +239,7 @@ DOSEMU
 
 The 0.64.3.1 release:
 ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/dosemu0.64.3.1.tgz
-ftp://sunsite.unc.edu/pub/Linux/system/Emulators/dosemu0.64.3.1.tgz
+ftp://sunsite.unc.edu/pub/Linux/system/emulators/dosemu0.64.3.1.tgz
 
 Loadlin
 =======
index 20f44261f3043cc4f9f3c672bcd6becaffb377ec..f2f789a28097d59e434807c6d290e8082440669f 100644 (file)
@@ -8,10 +8,6 @@ ifdef CONFIG_YM3812
        OBJS := $(OBJS)  adlib_card.o
 endif
 
-ifdef CONFIG_AEDSP16
-       OBJS := $(OBJS)  aedsp16.o
-endif
-
 ifdef CONFIG_AUDIO
        OBJS := $(OBJS)  audio.o
 endif
index d2bf6cccaeb207cd01fba3611145c593ec671ef4..b344e143cd551b155cf9a615acb72867d1128003 100644 (file)
@@ -1,2 +1,2 @@
-3.7-beta12
-0x030707
+3.8-beta9
+0x030803
index c1437820f4e5eda694a529de7fc70f3ac491a7a5..3b82d35ce57e83571ff9f705df827d557bf0ebe7 100644 (file)
@@ -1,7 +1,25 @@
-Changelog for version 3.7-beta10
---------------------------------
+Changelog for version 3.8-beta8
+-------------------------------
 
-Since 3.5-beta5
+Since 3.8-beta6
+- Fixed the famous Quake delay bug.
+
+Since 3.8-beta5
+- Fixed many bugs in audio playback.
+
+Since 3.8-beta4
+- Just minor changes.
+
+Since 3.8-beta1
+- Major rewrite of audio playback handling.
+- Added AWE32 support by Takashi Iwai (in ./lowlevel/).
+
+Since 3.7-beta#
+- Passing of ioctl() parameters between soundcard.c and other modules has been
+changed so that arg always points to kernel space.
+- Some bugfixes.
+
+Since 3.7-beta5
 - Disabled MIDI input with GUS PnP (Interwave). There seems to be constant
 stream of received 0x00 bytes when the MIDI receiver is enabled.
 
index e28aabb2baa16c3ca7fbf03e354cb2550804f55a..fb8a76753ce637c484928dfdfae26b8de3d4b94a 100644 (file)
@@ -15,4 +15,55 @@ bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND
 if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
    bool 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER
    bool 'AWE32 synth' CONFIG_AWE32_SYNTH
+   bool 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16
+
+   if [ "$CONFIG_AEDSP16" = "y" ]; then
+      comment 'SC-6600 Audio Cards have no jumper switches at all'
+      bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
+
+      if [ "$CONFIG_SB" = "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+         bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO
+         if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then
+           comment 'Audio Excel DSP 16 [Sound Blaster Pro]'
+            hex 'I/O base for Audio Excel DSP 16 220 or 240' \
+                                                  AEDSP16_BASE $SBC_BASE
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+                                                  AEDSP16_SBC_IRQ $SBC_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_SBC_DMA $SBC_DMA
+         fi
+      fi
+   
+      if [ "$CONFIG_MSS" = "y" -a "$CONFIG_AEDSP16_SBPRO" != "y" ]; then
+         bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS
+         if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then
+           comment 'Audio Excel DSP 16 [Microsoft Sound System]'
+           hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+                                                  AEDSP16_MSS_IRQ $MSS_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_MSS_DMA $MSS_DMA
+         fi
+      fi
+
+      if [ "$CONFIG_MIDI" = "y" ]; then
+         bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
+         if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
+           comment 'Audio Excel DSP 16 [MPU-401]'
+           if [ "$CONFIG_AEDSP16_SBPRO" != "y" \
+                -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+              hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+           fi
+            int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' \
+                                                       AEDSP16_MPU_IRQ $MPU_IRQ
+         fi
+      fi
+
+      if [ "$CONFIG_SC6600" = "y" ]; then
+         comment 'SC-6600 specific configuration'
+         bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY
+         int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' \
+                                               CONFIG_SC6600_CDROM 4
+         hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
+      fi
+
+   fi
 fi
index e28aabb2baa16c3ca7fbf03e354cb2550804f55a..fb8a76753ce637c484928dfdfae26b8de3d4b94a 100644 (file)
@@ -15,4 +15,55 @@ bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND
 if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
    bool 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER
    bool 'AWE32 synth' CONFIG_AWE32_SYNTH
+   bool 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16
+
+   if [ "$CONFIG_AEDSP16" = "y" ]; then
+      comment 'SC-6600 Audio Cards have no jumper switches at all'
+      bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
+
+      if [ "$CONFIG_SB" = "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+         bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO
+         if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then
+           comment 'Audio Excel DSP 16 [Sound Blaster Pro]'
+            hex 'I/O base for Audio Excel DSP 16 220 or 240' \
+                                                  AEDSP16_BASE $SBC_BASE
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+                                                  AEDSP16_SBC_IRQ $SBC_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_SBC_DMA $SBC_DMA
+         fi
+      fi
+   
+      if [ "$CONFIG_MSS" = "y" -a "$CONFIG_AEDSP16_SBPRO" != "y" ]; then
+         bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS
+         if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then
+           comment 'Audio Excel DSP 16 [Microsoft Sound System]'
+           hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+                                                  AEDSP16_MSS_IRQ $MSS_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_MSS_DMA $MSS_DMA
+         fi
+      fi
+
+      if [ "$CONFIG_MIDI" = "y" ]; then
+         bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
+         if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
+           comment 'Audio Excel DSP 16 [MPU-401]'
+           if [ "$CONFIG_AEDSP16_SBPRO" != "y" \
+                -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+              hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+           fi
+            int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' \
+                                                       AEDSP16_MPU_IRQ $MPU_IRQ
+         fi
+      fi
+
+      if [ "$CONFIG_SC6600" = "y" ]; then
+         comment 'SC-6600 specific configuration'
+         bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY
+         int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' \
+                                               CONFIG_SC6600_CDROM 4
+         hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
+      fi
+
+   fi
 fi
diff --git a/drivers/sound/Makefile.old b/drivers/sound/Makefile.old
deleted file mode 100644 (file)
index 6f070e6..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-# Makefile for the Linux sound card driver
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes. (hopefully)
-#
-#
-
-.PHONY: dummy
-SUB_DIRS       = lowlevel
-VERSION                = `head -1 .version`
-TARGET_OS      = linux
-USRINCDIR      = /usr/include
-MODULEDIR      = /lib/modules/misc
-
-FIXEDOBJS      = soundcard.o dev_table.o sound_switch.o
-
-ifndef NO_LOWLEVEL
-       FIXEDOBJS := $(FIXEDOBJS) lowlevel/lowlevel.o
-endif
-
-ifeq (.defines,$(wildcard .defines))
-include .defines
-include .objects
-endif
-
-ifndef TOPDIR
-TOPDIR=/usr/src/linux
-endif
-
-
-ifndef HOSTCC
-build:
-       @echo Compiling modularized sound driver
-       @make sound.o
-       @echo Sound module compiled.
-
-install:       sound.o
-       cp sound.o $(MODULEDIR)
-endif
-
-.c.o:
-       $(CC) $(CFLAGS) -c $<
-
-ifeq ($(CONFIG_SOUND),y)
-
-all:   local.h sound.a
-
-OBJS += $(FIXEDOBJS)
-
-else
-all:
-endif
-
-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
-
-sound.a: $(OBJS) 
-       -rm -f sound.a
-       $(AR) rcs sound.a $(OBJS)
-       sync
-
-clean:
-       rm -f core core.* *.o *.a tmp_make *~ x y z *%
-       rm -f configure
-       cd lowlevel;make clean
-       cd vivo;make clean
-
-indent:
-       for n in *.c;do echo indent $$n;indent $$n;done
-
-local.h: 
-       $(MAKE) clean
-       $(MAKE) setup
-       $(MAKE) oldconfig
-       $(MAKE) dep
-       @echo
-       @echo
-       @echo
-       @echo NOTE! Object file dependencies may not be up to date. Run
-       @echo make again if kernel/driver doesn''t link properly. Restarting
-       @echo it now may save some time.
-       @echo
-       @echo
-
-config: configure
-       @$(MAKE) setup
-       @./configure > local.h
-       @echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
-       @echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h
-#      @echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null
-#      @echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
-       @echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
-
-oldconfig: setup configure
-       @./configure -o > local.h
-       @echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
-       @echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h
-#      @echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null
-#      @echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
-       @echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
-
-kernelconfig: setup
-       rm -f configure
-       $(HOSTCC) -o configure configure.c
-       ./configure fixedlocal > local.h
-       ./configure fixeddefines > .defines
-       @echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
-       @echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h
-#      @echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null
-#      @echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
-       @echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
-
-mkscript: setup
-       rm -f configure
-       $(HOSTCC) -o configure configure.c
-       ./configure script > Config.in
-       cat lowlevel/Config.tmpl >> Config.in
-       ./configure fixedlocal > local.h
-       ./configure fixeddefines > .defines
-
-clrconf:
-       rm -f local.h .depend synth-ld.h trix_boot.h smw-midi0001.h maui_boot.h .defines
-
-configure: configure.c
-       $(HOSTCC) -o configure configure.c
-       @cat .blurb
-
-dep:
-       $(CPP) -M $(CFLAGS) -I. *.c > .depend
-
-setup:
-       @echo Compiling Sound Driver v $(VERSION) for Linux
-
-sound.o: local.h $(FIXEDOBJS) sound.a
-       -rm -f sound.o
-       $(LD) -r -o sound.o $(FIXEDOBJS) sound.a
-
-modules: local.h sound.o
-       ln -fs `pwd`/sound.o $(TOPDIR)/modules/sound.o
-
-
-lowlevel/lowlevel.o: dummy
-               cd lowlevel;make CC="$(CC)" CFLAGS="$(CFLAGS)"
-
-contrib:
-               cd lowlevel;make clean;make module "CC=$(CC)" CFLAGS="$(CFLAGS)"
-
-ifdef USE_DEPEND
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
-endif
index 0658b0128ab5abd26af67031429c08dea650ad94..a859afb12fff012e919ec9321c0b72d784d0d233 100644 (file)
@@ -1,4 +1,4 @@
-OSS Lite version 3.7-beta release notes
+OSS Lite version 3.8-beta release notes
 ---------------------------------------
 
 Most up to date information about this driver is available from 
@@ -12,11 +12,11 @@ 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).
 
-New Programmer's Guide is currently under work (June/July 96). Please
-check http://www.4front-tech.com/pguide for more info.
+Please check http://www.4front-tech.com/pguide for more info about programming
+with OSS.
 
    ====================================================
--  THIS VERSION ____REQUIRES____ Linux 2.1.5 OR LATER.
+-  THIS VERSION ____REQUIRES____ Linux 2.1.26 OR LATER.
    ====================================================
 
 Packages "snd-util-3.7.tar.gz" and "snd-data-0.1.tar.Z"
diff --git a/drivers/sound/Readme.aedsp16 b/drivers/sound/Readme.aedsp16
deleted file mode 100644 (file)
index b205a9d..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-Informations about Audio Excel DSP 16 can be found in the source
-file aedsp16.c
-Please, read the head of the source before using it. It contain useful
-informations.
-
-    Riccardo
index 25afef2ac0d9243b238cc01dbe6df041c1e8dc66..7f89cf17cab39e25bc01ccff36bb6031a28ca5f7 100644 (file)
@@ -1,4 +1,4 @@
-Configuring version 3.7 (for Linux) with some most common soundcards
+Configuring version 3.8 (for Linux) with some most common soundcards
 ====================================================================
 
 This document describes configuring soundcards with freeware version of
@@ -19,14 +19,95 @@ IMPORTANT!  This document covers only cards that were "known" when
                Cards having some kind of loadable "microcode" such as
                PSS, SM Wave, AudioTrix Pro and Maui/Tropez must be
                configured using the old method. The new one will not 
-               work with them.
+               work with them. The "old" method is used by default by
+               "make config". "make xconfig" will always use the "new"
+               method. "make menuconfig" will allow you to select which
+               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.
+
+               The /etc/soundconf file (forget it if you don't know what
+               this file does) contains settings that are used only by
+               the "old" method. Don't ever think the "active" settings
+               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.
 
                When using make xconfig and/or make menuconfig, you should
                carefully check each sound configuration option (particularly
-               "Support for /dev/dsp and /dev/audio").
+               "Support for /dev/dsp and /dev/audio"). The default values
+               offered by these programs are not necessarily valid.
+
+
+
+THE BIGGEST MISTAKE YOU CAN DO
+==============================
+
+1. Assuming that the card is Sound Blaster compatible when it's not.
+--------------------------------------------------------------------
+
+The number one mistake is to assume that your card is compatible with
+Sound Blaster. Only the cards made by Creative Technology or which have
+one or more chips labeled by Creative are SB compatible. In addition there
+are few sound chipsets which are SB compatible in Linux such as ESS1688 or
+Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything
+in Linux. 
+
+IF YOU REALLY ARE 150% SURE YOU REALLY HAVE A SOUND BLASTER YOU CAN SKIP THE
+REST OF THIS CHAPTER.
+
+For most other "supposed to be SB compatible" cards you have use other
+than SB drivers (see below). It is possible to get most soundcards to work
+in SB mode but in general it's complete waste of time. There are several
+problems which you will encounter by using SB mode with cards that are not
+truly SB compatible:
+
+- The SB emulation is at most SB Pro (DSP version 3.x) which means that 
+you get only 8 bit audio (there is always an another ("native") mode which
+gives the 16 bit capability). The 8 bit only operation is the reason why
+many users claim that sound quality in Linux is much worse than in DOS.
+In addition some applications require 16 bit mode and they produce just
+noise with a 8 bit only device.
+- The card may work only in some cases but refuse to work most of the
+time. The SB compatible mode always requires special intialization which is 
+done by the DOS/Windows drivers. This kind of cards work in Linux after
+you have warm booted it after DOS but they don't work after cold boot
+(power on or reset).
+- You get the famous "DMA timed out" messages. Usually all SB clones have
+software selectable IRQ and DMA settings. If the (power on default) values
+currently used by the card don't match configuration of the driver you will
+get the above error message whenever you try to record or play. There are
+few other reasons to the DMA timeout message but using the SB mode seems
+to be the most common cause.
+
+2. Trying to use a PnP (Plug & Play) card just like an ordinary soundcard
+-------------------------------------------------------------------------
+
+Plug & Play is a protocol defined by Intel and Microsoft. It let's operating
+systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA
+cards. The problem with PnP cards is that the standard Linux doesn't currently
+(versions 2.1.x and earlier) don't support PnP. This means that you will have
+to use some special tricks (see later) to get a PnP card alive. Many PnP cards
+work after they have been initialized but this is not always the case.
+
+There are usually both a non PnP and PnP versions of the same soundcard.
+The non PnP version is the original model which usually has been discontinued
+more than an year ago. The PnP version has the same name but with a PnP
+appended to it (sometimes not). This causes major confusion since even the
+non PnP model works with Linux the PnP one doesn't.
+
+You should carefully check if "Plug & Play" or "PnP" is mentioned in the name
+of the card or in the documentation or package that came with the card. 
+Everything described in the rest of this document is not necessarily valid
+for PnP models of soudcards even you have managed to wake up the card properly.
+Many PnP cards are simply too much different than their original non PnP
+ancestors which are covered by this document.
 
 Cards that are not (fully) supported by this driver
----------------------------------------------------
+===================================================
 
 See http://www.4front-tech.com/ossfree for information about soundcards 
 to be supported in future. 
@@ -45,14 +126,14 @@ New versions of most soundcards use so called ISA PnP protocol for
 soft configuring their I/O, IRQ, DMA and shared memory resources.
 Currently at least cards made by Creative Technology (SB32 and SB32AWE
 PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and
-Aztech (some Sound Galaxy models) use PnP technology. The CS4232 audio
+Aztech (some Sound Galaxy models) use PnP technology. The CS4232/4236 audio
 chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other
 motherboards) is also based on PnP technology but there is a "native" driver
 available for it (see information about CS4232 later in this document).
 
 PnP soundcards (as well as most other PnP ISA cards) are not supported
 by this version of the driver . Proper
-support for them should be released during spring 97 once kernel level
+support for them should be released during 97 once the kernel level
 PnP support is available.
 
 There is a method to get most of the PnP cards to work. The basic method
@@ -86,7 +167,7 @@ versions of isapnptools so at least version 1.6 is required with soundcards.
 
 Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers.
 See http://www.4front-tech.com/linux.html for more info. This is the way
-you propably like to do it if you don't waste hours of time in recompiling
+you probably like to do it if you don't waste hours of time in recompiling
 kernel and the required tools.
 
 Read this before trying to configure the driver
@@ -125,18 +206,31 @@ Sound Blasters
                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".
+
+       ======================================================================
        "Supposed to be SB compatible" cards.
                Forget the SB compatibility and check for other alternatives
                first. The only cards that work with the SB driver in
                Linux have been made by Creative Technology (there is at least
                one chip on the card with "CREATIVE" printed on it). The
                only other SB compatible chips are ESS and Jazz16 chips
-               (maybe ALSxxx chips too but they propably don't work).
+               (maybe ALSxxx chips too but they probably don't work).
+               Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or
+               "Crystal" are _NOT_ SB compatible in Linux.
 
                Practically all soundcards have some kind of SB emulation mode
                in addition to their native (16 bit) mode. In most cases this
-               (8 bit only) SB compatible mode doesn't work with Linux. However
-               in most cases the native 16 bit mode is supported by Linux.
+               (8 bit only) SB compatible mode doesn't work with Linux. If
+               you get it working it may cause problems with games and
+               applications which require 16 bit audio. Some 16 bit only
+               applications don't check if the card actually supports 16 bits.
+               They just dump 16 bit data to a 8 bit card which produces just
+               noise.
+
+               In most cases the 16 bit native mode is supported by Linux.
+               Use the SB mode with "clones" only if you don't find anything
+               better from the rest of this doc.
+       ======================================================================
 
 Gravis Ultrasound (GUS)
        GUS
@@ -177,7 +271,7 @@ Windows Sound System (MSS/WSS)
        This UART chip is used in the MIDI interface of some (rare)
        soundcards. It's supported by the driver in case you need it.
 
-Yamaha FM synthesizers (OPL2, OPL3 and OPL4)
+Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4)
        Most soundcards have a FM synthesizer chip. The OPL2 is a 2
        operator chip used in the original AdLib card. Currently it's used
        only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator 
@@ -193,6 +287,13 @@ Yamaha FM synthesizers (OPL2, OPL3 and OPL4)
        card has a FM chip made by Yamaha. Don't enable it if your card
        has a software (TRS) based FM emulator.
 
+       ----------------------------------------------------------------
+       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.
+       ----------------------------------------------------------------
+
 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
@@ -203,11 +304,17 @@ PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC)
 
        The driver supports downloading DSP algorithms to these cards.
 
+       NOTE! You will have to use the "old" config script when configuring
+       PSS cards.
+
 MediaTrix AudioTrix Pro
        The ATP card is built around a CS4231 codec and an OPL4 synthesizer
        chips. The OPL4 mode is supported by a microcontroller running a
        General MIDI emulator. There is also a SB 1.5 compatible playback mode.
 
+       NOTE! You will have to use the "old" config script when configuring
+       AudioTrix Pro.
+
 Ensoniq SoundScape and compatibles
        Ensoniq has designed a soundcard architecture based on the
        OTTO synthesizer chip used in their professional MIDI synthesizers.
@@ -218,31 +325,35 @@ Ensoniq SoundScape and compatibles
 
 MAD16 and Mozart based cards
        The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929),
-       OPTi 82C924 (non PnP mode) and OPTi 82C930 interface
+       OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface
        chips are used in many different soundcards, including some
        cards by Reveal miro and Turtle Beach (Tropez). The purpose of these
        chips is to connect other audio components to the PC bus. The
        interface chip performs address decoding for the other chips.
        NOTE! Tropez Plus is not MAD16 but CS4232 based.
+       NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible
+       in the PnP mode. You will have to use them in MAD16 mode after having
+       initialized them using isapnptools or DOS.
 
 Audio Excel DSP16 
        Support for this card was written by Riccardo Faccetti
        (riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in
-       this source distribution is not fully functional. A patch is
-       available from sunsite.unc.edu/pub/Linux/kernel/sound.
-       
-Crystal CS4232 based cards such as AcerMagic S23, TB Tropez _Plus_ and 
+       the lowlevel/ directory. To use it you should use the "new" config
+       script and to enable the "Additional low level drivers" option.
+Crystal CS4232 and 4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and 
        many PC motherboards (Compaq, HP, Intel, ...)
        CS4232 is a PnP multimedia chip which contains a CS3231A codec,
        SB and MPU401 emulations. There is support for OPL3 too.
        Unfortunately the MPU401 mode doesn't work (I don't know how to
-       initialize it). 
+       initialize it). CS4236 is an enhanced (compatible) version of 4232.
 
-Turtle Beach Maui and Tropez
+Turtle Beach Maui and Tropez "classic"
        This driver version supports sample, patch and program loading commands
        described in the Maui/Tropez User's manual. 
        There is now full initialization support too. The audio side of
        the Tropez is based on the MAD16 chip (see above).
+       NOTE! You will have to use the "old" config script when configuring
+       Maui or Tropez.
 
 Jumpers and software configuration
 ----------------------------------
@@ -430,21 +541,32 @@ select some options automatically as well.
          in this file. For cards having native support in the driver, consult
          the card specific instructions later in this file. Some drivers
          have their own MSS support and enabling this option will cause a
-         conflict.
+         conflict. 
+         Note! The MSS driver permits configuring two DMA channels. This is a
+         "nonstandard" feature and works only with very few cards (if any).
+         In most cases the second DMA channel should be disabled or set to
+         the same channel than the first one. Trying to configure two separate
+         channels with cards that don't support this feature will prevent
+         audio (at least recording) from working.
   "Ensoniq Soundscape support",
        - Answer 'y' if you have a soundcard based on the Ensoniq SoundScape
          chipset. Such cards are being manufactured at least by Ensoniq,
-         Spea and Reveal (note that Reveal makes other cards also).
+         Spea and Reveal (note that Reveal makes other cards also). Oldest
+         cards made by Spea don't work properly with Linux. 
+         Soundscape PnP as well as Ensoniq VIVO work only with the commercial
+         OSS/Linux version.
   "MediaTrix AudioTrix Pro support",
        - Answer 'y' if you have the AudioTrix Pro.
   "Support for MAD16 and/or Mozart based cards",
        - Answer y if your card has a Mozart (OAK OTI-601) or MAD16
-         (OPTi 82C928, 82C929, 82C924 or 82C930) audio interface chip. 
+         (OPTi 82C928, 82C929, 82C924/82C925 or 82C930) audio interface chip. 
          These chips are
          currently quite common so it's possible that many no-name cards
          have one of them. In addition the MAD16 chip is used in some
          cards made by known manufacturers such as Turtle Beach (Tropez),
          Reveal (some models) and Diamond (some recent models).
+         Note OPTi 82C924 and 82C925 are MAD16 compatible only in non PnP
+         mode (jumper selectable on many cards).
   "Support for TB Maui"
        - This enables TB Maui specific initialization. Works with TB Maui
        and TB Tropez (may not work with Tropez Plus).
@@ -773,9 +895,8 @@ TB Tropez is based on the 82C929 chip. It has two MIDI ports.
 The one connected to the MAD16 chip is the second one (there is a second
 MIDI connector/pins somewhere??). If you have not connected the second MIDI
 port, just disable the MIDI port of MAD16. The 'Maui' compatible synth of
-Tropez is jumper configurable and not connected to the MAD16 chip.
-It can be used by enabling the stand alone MPU401 support but you have
-to initialize it by using the MS-DOS SNDSETUP program.
+Tropez is jumper configurable and not connected to the MAD16 chip (the
+Maui driver can be used with it).
 
 Some MAD16 based cards may cause feedback, whistle or terrible noise if the
 line3 mixer channel is turned too high. This happens at least with Shuttle
@@ -790,7 +911,7 @@ MAD16 cards having a CS4231 codec support full duplex mode. This mode
 can be enabled by configuring the card to use two DMA channels. Possible
 DMA channel pairs are: 0&1, 1&0 and 3&0.
 
-NOTE! Cards having an OPTi 82C924 chip work with OSS/Free only in
+NOTE! Cards having an OPTi 82C924/82C925 chip work with OSS/Free only in
 non-PnP mode (usually jumper selectable). The PnP mode is supported only
 by OSS/Linux.
 
@@ -812,6 +933,9 @@ specific section for instructions about configuring more than one MPU401 cards.
 Logitech Soundman Wave
 ----------------------
 
+NOTE! You will have to use the "old" config script when configuring
+      SM Wave.
+
 Read the above MV Jazz specific instructions first.
 
 The Logitech SoundMan Wave (don't confuse with the SM16 or SM Games) is
@@ -860,7 +984,20 @@ if you want to use MIDI features of ES1688. ES688 doesn't have MPU mode
 so you don't need to enable it (the driver uses normal SB MIDI automatically
 with ES688).
 
-NOTE! ESS cards are not compatible with MSS/WSS.
+NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support
+of OSS doesn't work with it.
+
+There are some ES1688/688 based soundcards and (particularily) motherboards
+which use software configurable I/O port relocation feature of the chip.
+This ESS proprietary feature is supported only by OSS/Linux.
+
+There are ES1688 based cards which use different interrupt pin assignment than
+recommended by ESS (5, 7, 9/2 and 10). In this case all IRQ's don't work.
+At least a card called (Pearl?) Hypersound 16 supports IRQ15 but it doesn't
+work.
+
+ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688
+brobably works with OSS/Free after initialization using isapnptools.
 
 Reveal cards
 ------------
index 35c977bef926b00201a75b98790121da076acad9..053880e849e8a69dcf109999d5bf8347b787892b 100644 (file)
@@ -72,6 +72,15 @@ Please check http://www.4front-tech.com/osslite for more info.
 Hannu Savolainen
 hannu@voxware.pp.fi
 
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+NOTE!
+
+Running the script enclosed below is usually not required. All known Linux
+distributions build them automaticly during installation. You need to run
+this script only if "ls /dev/sndstat" displays "No such file or directory".
+In case of any other error message you should start looking for the reason
+from somewhere else (see above).
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 ----------------- cut here ------------------------------
 #!/bin/sh
 # *****************************************
index d40e4eb855439f3a8fd54c299e1a7c6141314b1e..92a71fcdf76de74ccc50b0d64a0bb61af7e60cd9 100644 (file)
@@ -15,7 +15,7 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -42,17 +42,13 @@ typedef struct
     unsigned char   saved_regs[16];
     int             debug_flag;
 
-    int             speed;
-    unsigned char   speed_bits;
-    int             channels;
-    int             audio_format;
-    unsigned char   format_bits;
     int             audio_flags;
+    int             record_dev, playback_dev;
 
     int             xfer_count;
     int             audio_mode;
+    int             open_mode;
     int             intr_active;
-    int             opened;
     char           *chip_name;
     int             model;
 #define MD_1848                1
@@ -76,16 +72,29 @@ typedef struct
     mixer_ents     *mix_devices;
     int             mixer_output_port;
   }
-
 ad1848_info;
 
+typedef struct ad1848_port_info
+  {
+    int             open_mode;
+    int             speed;
+    unsigned char   speed_bits;
+    int             channels;
+    int             audio_format;
+    unsigned char   format_bits;
+  }
+ad1848_port_info;
+
 static int      nr_ad1848_devs = 0;
 static volatile char irq2dev[17] =
 {-1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1};
 
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 static int      timer_installed = -1;
 
+#endif
+
 static int      ad_format_mask[8 /*devc->model */ ] =
 {
   0,
@@ -107,19 +116,22 @@ static ad1848_info dev_info[MAX_AUDIO_DEV];
 
 static int      ad1848_open (int dev, int mode);
 static void     ad1848_close (int dev);
-static int      ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg, int local);
-static void     ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
-static void     ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static int      ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg);
+static void     ad1848_output_block (int dev, unsigned long buf, int count, int intrflag);
+static void     ad1848_start_input (int dev, unsigned long buf, int count, int intrflag);
 static int      ad1848_prepare_for_output (int dev, int bsize, int bcount);
-static int      ad1848_prepare_for_IO (int dev, int bsize, int bcount);
-static void     ad1848_reset (int dev);
+static int      ad1848_prepare_for_input (int dev, int bsize, int bcount);
 static void     ad1848_halt (int dev);
 static void     ad1848_halt_input (int dev);
 static void     ad1848_halt_output (int dev);
 static void     ad1848_trigger (int dev, int bits);
+
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 static int      ad1848_tmr_install (int dev);
 static void     ad1848_tmr_reprogram (int dev);
 
+#endif
+
 static int
 ad_read (ad1848_info * devc, int reg)
 {
@@ -134,7 +146,7 @@ ad_read (ad1848_info * devc, int reg)
   cli ();
   outb (((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr (devc));
   x = inb (io_Indexed_Data (devc));
-  /*  printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
+/* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
   restore_flags (flags);
 
   return x;
@@ -188,6 +200,7 @@ wait_for_calibration (ad1848_info * devc)
   if (ad_read (devc, 11) & 0x20)
     if (devc->model != MD_1845)
       printk ("ad1848: Auto calibration timed out(3).\n");
+  ad_write (devc, 9, ad_read (devc, 9) & ~0x18);       /* Disable autocalibration */
 }
 
 static void
@@ -205,24 +218,18 @@ ad_mute (ad1848_info * devc)
       ad_write (devc, i, prev | 0x80);
     }
 
-/*
- * Let's have some delay
- */
-
-  for (i = 0; i < 1000; i++)
-    inb (devc->base);
 }
 
 static void
 ad_unmute (ad1848_info * devc)
 {
-  int             i;
+  int             i, dummy;
 
 /*
  * Let's have some delay
  */
   for (i = 0; i < 1000; i++)
-    inb (devc->base);
+    dummy = inb (devc->base);
 
   /*
    * Restore back old volume registers (unmute)
@@ -262,7 +269,7 @@ static void
 ad_leave_MCE (ad1848_info * devc)
 {
   unsigned long   flags;
-  unsigned char   prev;
+  unsigned char   prev, acal;
   int             timeout = 1000;
 
   while (timeout > 0 && inb (devc->base) == 0x80)      /*Are we initializing */
@@ -271,6 +278,8 @@ ad_leave_MCE (ad1848_info * devc)
   save_flags (flags);
   cli ();
 
+  acal = ad_read (devc, 9);
+
   devc->MCE_bit = 0x00;
   prev = inb (io_Index_Addr (devc));
   outb ((0x00), io_Index_Addr (devc)); /* Clear the MCE bit */
@@ -282,7 +291,8 @@ ad_leave_MCE (ad1848_info * devc)
     }
 
   outb ((0x00), io_Index_Addr (devc)); /* Clear the MCE bit */
-  wait_for_calibration (devc);
+  if (acal & 0x08)             /* Auto calibration is enabled */
+    wait_for_calibration (devc);
   restore_flags (flags);
 }
 
@@ -526,10 +536,10 @@ ad1848_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
     {
       int             val;
 
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
 
       if (val == 0xffff)
-       return ioctl_out (arg, devc->mixer_output_port);
+       return (*(int *) arg = devc->mixer_output_port);
 
       val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
 
@@ -542,24 +552,24 @@ ad1848_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
       else
        ad_write (devc, 26, ad_read (devc, 26) | 0x40);         /* Mute mono out */
 
-      return ioctl_out (arg, devc->mixer_output_port);
+      return (*(int *) arg = devc->mixer_output_port);
     }
 
   if (((cmd >> 8) & 0xff) == 'M')
     {
       int             val;
 
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
        switch (cmd & 0xff)
          {
          case SOUND_MIXER_RECSRC:
-           get_user (val, (int *) arg);
-           return ioctl_out (arg, ad1848_set_recmask (devc, val));
+           val = *(int *) arg;
+           return (*(int *) arg = ad1848_set_recmask (devc, val));
            break;
 
          default:
-           get_user (val, (int *) arg);
-           return ioctl_out (arg, ad1848_mixer_set (devc, cmd & 0xff, val));
+           val = *(int *) arg;
+           return (*(int *) arg = ad1848_mixer_set (devc, cmd & 0xff, val));
          }
       else
        switch (cmd & 0xff)     /*
@@ -568,30 +578,30 @@ ad1848_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
          {
 
          case SOUND_MIXER_RECSRC:
-           return ioctl_out (arg, devc->recmask);
+           return (*(int *) arg = devc->recmask);
            break;
 
          case SOUND_MIXER_DEVMASK:
-           return ioctl_out (arg, devc->supported_devices);
+           return (*(int *) arg = devc->supported_devices);
            break;
 
          case SOUND_MIXER_STEREODEVS:
            if (devc->model == MD_C930)
-             return ioctl_out (arg, devc->supported_devices);
+             return (*(int *) arg = devc->supported_devices);
            else
-             return ioctl_out (arg, devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
+             return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
            break;
 
          case SOUND_MIXER_RECMASK:
-           return ioctl_out (arg, devc->supported_rec_devices);
+           return (*(int *) arg = devc->supported_rec_devices);
            break;
 
          case SOUND_MIXER_CAPS:
-           return ioctl_out (arg, SOUND_CAP_EXCL_INPUT);
+           return (*(int *) arg = SOUND_CAP_EXCL_INPUT);
            break;
 
          default:
-           return ioctl_out (arg, ad1848_mixer_get (devc, cmd & 0xff));
+           return (*(int *) arg = ad1848_mixer_get (devc, cmd & 0xff));
          }
     }
   else
@@ -602,6 +612,7 @@ static int
 ad1848_set_speed (int dev, int arg)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   /*
      * The sampling speed is encoded in the least significant nibble of I8. The
@@ -642,7 +653,7 @@ ad1848_set_speed (int dev, int arg)
   n = sizeof (speed_table) / sizeof (speed_struct);
 
   if (arg <= 0)
-    return devc->speed;
+    return portc->speed;
 
   if (devc->model == MD_1845)  /* AD1845 has different timer than others */
     {
@@ -651,9 +662,9 @@ ad1848_set_speed (int dev, int arg)
       if (arg > 50000)
        arg = 50000;
 
-      devc->speed = arg;
-      devc->speed_bits = speed_table[3].bits;
-      return devc->speed;
+      portc->speed = arg;
+      portc->speed_bits = speed_table[3].bits;
+      return portc->speed;
     }
 
   if (arg < speed_table[0].speed)
@@ -683,20 +694,20 @@ ad1848_set_speed (int dev, int arg)
       selected = 3;
     }
 
-  devc->speed = speed_table[selected].speed;
-  devc->speed_bits = speed_table[selected].bits;
-  return devc->speed;
+  portc->speed = speed_table[selected].speed;
+  portc->speed_bits = speed_table[selected].bits;
+  return portc->speed;
 }
 
 static short
 ad1848_set_channels (int dev, short arg)
 {
-  ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   if (arg != 1 && arg != 2)
-    return devc->channels;
+    return portc->channels;
 
-  devc->channels = arg;
+  portc->channels = arg;
   return arg;
 }
 
@@ -704,6 +715,7 @@ static unsigned int
 ad1848_set_bits (int dev, unsigned int arg)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   static struct format_tbl
     {
@@ -755,25 +767,25 @@ ad1848_set_bits (int dev, unsigned int arg)
   int             i, n = sizeof (format2bits) / sizeof (struct format_tbl);
 
   if (arg == 0)
-    return devc->audio_format;
+    return portc->audio_format;
 
   if (!(arg & ad_format_mask[devc->model]))
     arg = AFMT_U8;
 
-  devc->audio_format = arg;
+  portc->audio_format = arg;
 
   for (i = 0; i < n; i++)
     if (format2bits[i].format == arg)
       {
-       if ((devc->format_bits = format2bits[i].bits) == 0)
-         return devc->audio_format = AFMT_U8;  /* Was not supported */
+       if ((portc->format_bits = format2bits[i].bits) == 0)
+         return portc->audio_format = AFMT_U8;         /* Was not supported */
 
        return arg;
       }
 
   /* Still hanging here. Something must be terribly wrong */
-  devc->format_bits = 0;
-  return devc->audio_format = AFMT_U8;
+  portc->format_bits = 0;
+  return portc->audio_format = AFMT_U8;
 }
 
 static struct audio_driver ad1848_audio_driver =
@@ -783,9 +795,8 @@ static struct audio_driver ad1848_audio_driver =
   ad1848_output_block,
   ad1848_start_input,
   ad1848_ioctl,
-  ad1848_prepare_for_IO,
+  ad1848_prepare_for_input,
   ad1848_prepare_for_output,
-  ad1848_reset,
   ad1848_halt,
   NULL,
   NULL,
@@ -808,17 +819,18 @@ static int
 ad1848_open (int dev, int mode)
 {
   ad1848_info    *devc = NULL;
+  ad1848_port_info *portc;
   unsigned long   flags;
 
   if (dev < 0 || dev >= num_audiodevs)
     return -ENXIO;
 
   devc = (ad1848_info *) audio_devs[dev]->devc;
-
+  portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   save_flags (flags);
   cli ();
-  if (devc->opened)
+  if (portc->open_mode || (devc->open_mode & mode))
     {
       restore_flags (flags);
       return -EBUSY;
@@ -832,9 +844,15 @@ ad1848_open (int dev, int mode)
     }
 
   devc->intr_active = 0;
-  devc->opened = 1;
   devc->audio_mode = 0;
+  devc->open_mode |= mode;
+  portc->open_mode = mode;
   ad1848_trigger (dev, 0);
+
+  if (mode & OPEN_READ)
+    devc->record_dev = dev;
+  if (mode & OPEN_WRITE)
+    devc->playback_dev = dev;
   restore_flags (flags);
 /*
  * Mute output until the playback really starts. This decreases clicking (hope so).
@@ -849,6 +867,7 @@ ad1848_close (int dev)
 {
   unsigned long   flags;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   DEB (printk ("ad1848_close(void)\n"));
 
@@ -856,43 +875,41 @@ ad1848_close (int dev)
   cli ();
 
   devc->intr_active = 0;
-  ad1848_reset (dev);
-
+  ad1848_halt (dev);
 
-  devc->opened = 0;
   devc->audio_mode = 0;
+  devc->open_mode &= ~portc->open_mode;
+  portc->open_mode = 0;
 
   ad_unmute (devc);
   restore_flags (flags);
 }
 
 static int
-ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   return -EINVAL;
 }
 
 static void
-ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+ad1848_output_block (int dev, unsigned long buf, int count, int intrflag)
 {
   unsigned long   flags, cnt;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
-
-  if (!dma_restart)
-    return;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   cnt = count;
 
-  if (devc->audio_format == AFMT_IMA_ADPCM)
+  if (portc->audio_format == AFMT_IMA_ADPCM)
     {
       cnt /= 4;
     }
   else
     {
-      if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))    /* 16 bit data */
+      if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))   /* 16 bit data */
        cnt >>= 1;
     }
-  if (devc->channels > 1)
+  if (portc->channels > 1)
     cnt >>= 1;
   cnt--;
 
@@ -909,13 +926,9 @@ ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dm
   save_flags (flags);
   cli ();
 
-  ad1848_halt_output (dev);
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
-
   ad_write (devc, 15, (unsigned char) (cnt & 0xff));
   ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
 
-
   devc->xfer_count = cnt;
   devc->audio_mode |= PCM_ENABLE_OUTPUT;
   devc->intr_active = 1;
@@ -923,25 +936,23 @@ ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dm
 }
 
 static void
-ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+ad1848_start_input (int dev, unsigned long buf, int count, int intrflag)
 {
   unsigned long   flags, cnt;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
-
-  if (!dma_restart)
-    return;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   cnt = count;
-  if (devc->audio_format == AFMT_IMA_ADPCM)
+  if (portc->audio_format == AFMT_IMA_ADPCM)
     {
       cnt /= 4;
     }
   else
     {
-      if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))    /* 16 bit data */
+      if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))   /* 16 bit data */
        cnt >>= 1;
     }
-  if (devc->channels > 1)
+  if (portc->channels > 1)
     cnt >>= 1;
   cnt--;
 
@@ -958,12 +969,6 @@ ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma
   save_flags (flags);
   cli ();
 
-  if (dma_restart)
-    {
-      ad1848_halt_input (dev);
-      DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
-    }
-
   if (devc->model == MD_1848 || !devc->dual_dma)       /* Single DMA channel mode */
     {
       ad_write (devc, 15, (unsigned char) (cnt & 0xff));
@@ -987,62 +992,99 @@ ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma
 static int
 ad1848_prepare_for_output (int dev, int bsize, int bcount)
 {
+  int             timeout;
+  unsigned char   fs, old_fs, tmp = 0;
+  unsigned long   flags;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   ad_mute (devc);
-  if (devc->model != MD_4232)
+
+  save_flags (flags);
+  cli ();
+  fs = portc->speed_bits | (portc->format_bits << 5);
+
+  if (portc->channels > 1)
+    fs |= 0x10;
+
+  if (devc->model == MD_1845)  /* Use alternate speed select registers */
     {
-/*
- * This code fragment ensures that the playback FIFO is empty before
- * setting the codec for playback. Enabling playback for a moment should
- * be enough to do that.
- */
-      int             tmout;
+      fs &= 0xf0;              /* Mask off the rate select bits */
 
-      ad_write (devc, 9, ad_read (devc, 9) | 0x01);    /* Enable playback */
-      disable_dma (audio_devs[dev]->dmachan1);
-      for (tmout = 0; tmout < 1000000; tmout++)
-       if (ad_read (devc, 11) & 0x10)  /* DRQ active */
-         if (tmout > 10000)
-           break;
-      ad_write (devc, 9, ad_read (devc, 9) & ~0x01);   /* Stop playback */
+      ad_write (devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
+      ad_write (devc, 23, portc->speed & 0xff);                /* Speed LSB */
+    }
+
+  old_fs = ad_read (devc, 8);
 
-      enable_dma (audio_devs[dev]->dmachan1);
-      devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+  ad_enter_MCE (devc);         /* Enables changes to the format select reg */
+
+  if (devc->model == MD_4232)
+    {
+      tmp = ad_read (devc, 16);
+      ad_write (devc, 16, tmp | 0x30);
     }
 
-  return ad1848_prepare_for_IO (dev, bsize, bcount);
+  ad_write (devc, 8, fs);
+  /*
+   * Write to I8 starts resynchronization. Wait until it completes.
+   */
+  timeout = 0;
+  while (timeout < 100 && inb (devc->base) != 0x80)
+    timeout++;
+  timeout = 0;
+  while (timeout < 10000 && inb (devc->base) == 0x80)
+    timeout++;
+
+  if (devc->model == MD_4232)
+    ad_write (devc, 16, tmp & ~0x30);
+
+  ad_leave_MCE (devc);         /*
+                                * Starts the calibration process.
+                                */
+  restore_flags (flags);
+  devc->xfer_count = 0;
+
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
+  if (dev == timer_installed && devc->timer_running)
+    if ((fs & 0x01) != (old_fs & 0x01))
+      {
+       ad1848_tmr_reprogram (dev);
+      }
+#endif
+  ad1848_halt_output (dev);
+  return 0;
 }
 
 static int
-ad1848_prepare_for_IO (int dev, int bsize, int bcount)
+ad1848_prepare_for_input (int dev, int bsize, int bcount)
 {
   int             timeout;
   unsigned char   fs, old_fs, tmp = 0;
   unsigned long   flags;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   if (devc->audio_mode)
     return 0;
 
   save_flags (flags);
   cli ();
-  fs = devc->speed_bits | (devc->format_bits << 5);
+  fs = portc->speed_bits | (portc->format_bits << 5);
 
-  if (devc->channels > 1)
+  if (portc->channels > 1)
     fs |= 0x10;
 
   if (devc->model == MD_1845)  /* Use alternate speed select registers */
     {
       fs &= 0xf0;              /* Mask off the rate select bits */
 
-      ad_write (devc, 22, (devc->speed >> 8) & 0xff);  /* Speed MSB */
-      ad_write (devc, 23, devc->speed & 0xff); /* Speed LSB */
+      ad_write (devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
+      ad_write (devc, 23, portc->speed & 0xff);                /* Speed LSB */
     }
 
   old_fs = ad_read (devc, 8);
 
-
   ad_enter_MCE (devc);         /* Enables changes to the format select reg */
 
   if (devc->model == MD_4232)
@@ -1090,33 +1132,29 @@ ad1848_prepare_for_IO (int dev, int bsize, int bcount)
   restore_flags (flags);
   devc->xfer_count = 0;
 
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
   if (dev == timer_installed && devc->timer_running)
     if ((fs & 0x01) != (old_fs & 0x01))
       {
        ad1848_tmr_reprogram (dev);
       }
 #endif
+  ad1848_halt_input (dev);
   return 0;
 }
 
-static void
-ad1848_reset (int dev)
-{
-  ad1848_halt (dev);
-}
-
 static void
 ad1848_halt (int dev)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   unsigned char   bits = ad_read (devc, 9);
 
-  if (bits & 0x01)
+  if (bits & 0x01 && portc->open_mode & OPEN_WRITE)
     ad1848_halt_output (dev);
 
-  if (bits & 0x02)
+  if (bits & 0x02 && portc->open_mode & OPEN_READ)
     ad1848_halt_input (dev);
   devc->audio_mode = 0;
 }
@@ -1138,18 +1176,17 @@ ad1848_halt_input (int dev)
   {
     int             tmout;
 
-    disable_dma (audio_devs[dev]->dmachan1);
+    disable_dma (audio_devs[dev]->dmap_out->dma);
 
     for (tmout = 0; tmout < 100000; tmout++)
       if (ad_read (devc, 11) & 0x10)
        break;
     ad_write (devc, 9, ad_read (devc, 9) & ~0x02);     /* Stop capture */
 
-    enable_dma (audio_devs[dev]->dmachan1);
+    enable_dma (audio_devs[dev]->dmap_out->dma);
     devc->audio_mode &= ~PCM_ENABLE_INPUT;
   }
 
-
   outb ((0), io_Status (devc));        /* Clear interrupt status */
   outb ((0), io_Status (devc));        /* Clear interrupt status */
 
@@ -1174,18 +1211,17 @@ ad1848_halt_output (int dev)
   {
     int             tmout;
 
-    disable_dma (audio_devs[dev]->dmachan1);
+    disable_dma (audio_devs[dev]->dmap_out->dma);
 
     for (tmout = 0; tmout < 100000; tmout++)
       if (ad_read (devc, 11) & 0x10)
        break;
     ad_write (devc, 9, ad_read (devc, 9) & ~0x01);     /* Stop playback */
 
-    enable_dma (audio_devs[dev]->dmachan1);
+    enable_dma (audio_devs[dev]->dmap_out->dma);
     devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
   }
 
-
   outb ((0), io_Status (devc));        /* Clear interrupt status */
   outb ((0), io_Status (devc));        /* Clear interrupt status */
 
@@ -1198,6 +1234,7 @@ static void
 ad1848_trigger (int dev, int state)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
   unsigned long   flags;
   unsigned char   tmp, old;
 
@@ -1205,22 +1242,35 @@ ad1848_trigger (int dev, int state)
   cli ();
   state &= devc->audio_mode;
 
-  tmp = (old = ad_read (devc, 9)) & ~0x03;
-  if (state & PCM_ENABLE_INPUT)
-    tmp |= 0x02;
-  if (state & PCM_ENABLE_OUTPUT)
-    tmp |= 0x01;
+  tmp = old = ad_read (devc, 9);
 
-  ad_mute (devc);
+  if (portc->open_mode & OPEN_READ)
+    {
+      if (state & PCM_ENABLE_INPUT)
+       tmp |= 0x02;
+      else
+       tmp &= ~0x02;
+    }
 
-  ad_write (devc, 9, tmp);
+  if (portc->open_mode & OPEN_WRITE)
+    {
+      if (state & PCM_ENABLE_OUTPUT)
+       tmp |= 0x01;
+      else
+       tmp &= ~0x01;
+    }
 
-  ad_unmute (devc);
+  /* ad_mute(devc); */
+  if (tmp != old)
+    {
+      ad_write (devc, 9, tmp);
+      ad_unmute (devc);
+    }
 
   restore_flags (flags);
 }
 
-void
+static void
 ad1848_init_hw (ad1848_info * devc)
 {
   int             i;
@@ -1332,7 +1382,7 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
   devc->timer_running = 0;
   devc->MCE_bit = 0x40;
   devc->irq = 0;
-  devc->opened = 0;
+  devc->open_mode = 0;
   devc->chip_name = "AD1848";
   devc->model = MD_1848;       /* AD1848 or CS4248 */
   devc->levels = NULL;
@@ -1348,12 +1398,10 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
      * If the I/O address is unused, it typically returns 0xff.
    */
 
-  DDB (printk ("ad1848_detect() - step A\n"));
-
 /*
  * Wait for the device to stop initialization
  */
-  /* outb(( 0x0b),  devc->base); */
+  DDB (printk ("ad1848_detect() - step 0\n"));
 
   for (i = 0; i < 10000000; i++)
     {
@@ -1363,10 +1411,12 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
        break;
     }
 
+  DDB (printk ("ad1848_detect() - step A\n"));
+
   if ((inb (devc->base) & 0x80) != 0x00)       /* Not a AD1848 */
     {
       DDB (printk ("ad1848 detect error - step A (%02x)\n",
-                  inb (devc->base)));
+                  (int) inb (devc->base)));
       return 0;
     }
 
@@ -1391,7 +1441,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"));
@@ -1404,7 +1454,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; */
       }
 
   /*
@@ -1505,7 +1555,7 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
          ad_write (devc, 25, ~tmp1);   /* Invert all bits */
          if ((ad_read (devc, 25) & 0xe7) == (tmp1 & 0xe7))
            {
-             int             id;
+             int             id, full_id;
 
              /*
               *      It's at least CS4231
@@ -1520,8 +1570,14 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
               * while the CS4231A reports different.
               */
 
-             DDB (printk ("ad1848_detect() - step I\n"));
              id = ad_read (devc, 25) & 0xe7;
+             full_id = ad_read (devc, 25);
+             if (id == 0x80)   /* Device busy??? */
+               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)));
 
              switch (id)
                {
@@ -1547,6 +1603,11 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
                  devc->model = MD_4232;
                  break;
 
+               case 0x41:
+                 devc->chip_name = "CS4236B";
+                 devc->model = MD_4232;
+                 break;
+
                case 0x80:
                  {
                    /* 
@@ -1559,23 +1620,25 @@ ad1848_detect (int io_base, int *ad_flags, int *osp)
                    unsigned char   tmp = ad_read (devc, 23);
 
                    ad_write (devc, 23, ~tmp);
-                   if (ad_read (devc, 23) != tmp)      /* AD1845 ? */
-                     {
-                       devc->chip_name = "AD1845";
-                       devc->model = MD_1845;
-                     }
-                   else if (interwave)
+                   if (interwave)
                      {
                        devc->model = MD_IWAVE;
                        devc->chip_name = "IWave";
                      }
+                   else if (ad_read (devc, 23) != tmp)         /* AD1845 ? */
+                     {
+                       devc->chip_name = "AD1845";
+                       devc->model = MD_1845;
+                     }
 
                    ad_write (devc, 23, tmp);   /* Restore */
                  }
                  break;
 
                default:        /* Assume CS4231 or OPTi 82C930 */
-                 DDB (printk ("ad1848: I25 = %02x\n", ad_read (devc, 25)));
+                 DDB (printk ("ad1848: I25 = %02x/%02x\n",
+                              ad_read (devc, 25),
+                              ad_read (devc, 25) & 0xe7));
                  if (optiC930)
                    {
                      devc->chip_name = "82C930";
@@ -1625,16 +1688,17 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
 
   ad1848_info    *devc = &dev_info[nr_ad1848_devs];
 
-
+  ad1848_port_info *portc = NULL;
 
   request_region (devc->base, 4, devc->chip_name);
 
   devc->irq = (irq > 0) ? irq : 0;
-  devc->opened = 0;
+  devc->open_mode = 0;
   devc->timer_ticks = 0;
   devc->dma1 = dma_playback;
   devc->dma2 = dma_capture;
   devc->audio_flags = DMA_AUTOMODE;
+  devc->playback_dev = devc->record_dev = 0;
 
   if (name != NULL && name[0] != 0)
     sprintf (dev_name,
@@ -1670,6 +1734,14 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
       return;
     }
 
+
+  portc = (ad1848_port_info *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (ad1848_port_info)));
+  sound_mem_sizes[sound_nblocks] = sizeof (ad1848_port_info);
+  if (sound_nblocks < 1024)
+    sound_nblocks++;;
+  audio_devs[my_dev]->portc = portc;
+  memset ((char *) portc, 0, sizeof (*portc));
+
   nr_ad1848_devs++;
 
   ad1848_init_hw (devc);
@@ -1712,7 +1784,7 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
   else if (irq < 0)
     irq2dev[-irq] = devc->dev_no = my_dev;
 
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
   if (devc->model != MD_1848 &&
       devc->model != MD_C930 && devc->irq_ok)
     ad1848_tmr_install (my_dev);
@@ -1736,6 +1808,7 @@ ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture
     {
       audio_devs[my_dev]->mixer_dev = num_mixers - 1;
     }
+
 }
 
 void
@@ -1813,10 +1886,10 @@ ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int shar
          if (irq > 0)
            snd_release_irq (devc->irq);
 
-         sound_free_dma (audio_devs[dev]->dmachan1);
+         sound_free_dma (audio_devs[dev]->dmap_out->dma);
 
-         if (audio_devs[dev]->dmachan2 != audio_devs[dev]->dmachan1)
-           sound_free_dma (audio_devs[dev]->dmachan2);
+         if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma)
+           sound_free_dma (audio_devs[dev]->dmap_in->dma);
        }
     }
   else
@@ -1832,6 +1905,7 @@ ad1848_interrupt (int irq, void *dev_id, struct pt_regs *dummy)
   int             dev;
   int             alt_stat = 0xff;
   unsigned char   c930_stat = 0;
+  int             cnt = 0;
 
   if (irq < 0 || irq > 15)
     {
@@ -1864,6 +1938,8 @@ interrupt_again:          /* Jump back here if int status doesn't reset */
 
   if (status == 0x80)
     printk ("ad1848_interrupt: Why?\n");
+  if (devc->model == MD_1848)
+    outb ((0), io_Status (devc));      /* Clear interrupt status */
 
   if (status & 0x01)
     {
@@ -1888,74 +1964,52 @@ interrupt_again:                /* Jump back here if int status doesn't reset */
       else if (devc->model != MD_1848)
        alt_stat = ad_read (devc, 24);
 
-      if (devc->opened && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20)
+      /* Acknowledge the intr before proceeding */
+      if (devc->model == MD_C930)
+       {                       /* 82C930 has interrupt status register in MAD16 register MC11 */
+         unsigned long   flags;
+
+         save_flags (flags);
+         cli ();
+
+         outb ((11), 0xe0e);
+         outb ((~c930_stat), 0xe0f);
+         restore_flags (flags);
+       }
+      else if (devc->model != MD_1848)
+       ad_write (devc, 24, ad_read (devc, 24) & ~alt_stat);    /* Selective ack */
+
+      if (devc->open_mode & OPEN_READ && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20)
        {
-         DMAbuf_inputintr (dev);
+         DMAbuf_inputintr (devc->record_dev);
        }
 
-      if (devc->opened && devc->audio_mode & PCM_ENABLE_OUTPUT &&
+      if (devc->open_mode & OPEN_WRITE && devc->audio_mode & PCM_ENABLE_OUTPUT &&
          alt_stat & 0x10)
        {
-         DMAbuf_outputintr (dev, 1);
+         DMAbuf_outputintr (devc->playback_dev, 1);
        }
 
       if (devc->model != MD_1848 && alt_stat & 0x40)   /* Timer interrupt */
        {
          devc->timer_ticks++;
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
          if (timer_installed == dev && devc->timer_running)
            sound_timer_interrupt ();
 #endif
        }
     }
 
-  if (devc->model == MD_C930)
-    {                          /* 82C930 has interrupt status register in MAD16 register MC11 */
-      unsigned long   flags;
-
-      save_flags (flags);
-      cli ();
-
-      outb ((11), 0xe0e);
-      outb ((~c930_stat), 0xe0f);
-      restore_flags (flags);
-    }
-  else if (devc->model != MD_1848)
-    ad_write (devc, 24, ad_read (devc, 24) & ~alt_stat);       /* Selective ack */
-  else
-    outb ((0), io_Status (devc));      /* Clear interrupt status */
-
 /*
  * Sometimes playback or capture interrupts occur while a timer interrupt
  * is being handled. The interrupt will not be retriggered if we don't
  * handle it now. Check if an interrupt is still pending and restart
  * the handler in this case.
  */
-  if (inb (io_Status (devc)) & 0x01)
-    goto interrupt_again;
-}
-
-/*
- * Some extra code for the MS Sound System
- */
-
-void
-check_opl3 (int base, struct address_info *hw_config)
-{
-
-#ifdef CONFIG_YM3812
-  if (check_region (base, 4))
+  if (inb (io_Status (devc)) & 0x01 && cnt++ < 4)
     {
-      printk ("\n\nopl3.c: I/O port %x already in use\n\n", base);
-      return;
+      goto interrupt_again;
     }
-
-  if (!opl3_detect (base, hw_config->osp))
-    return;
-
-  opl3_init (base, hw_config->osp);
-  request_region (base, 4, "OPL3/OPL2");
-#endif
 }
 
 #ifdef DESKPROXL
@@ -2217,11 +2271,12 @@ 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, inb (hw_config->io_base + 3)));
+                  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;
 
+      hw_config->card_subtype = 1;
       return 1;
     }
 
@@ -2276,7 +2331,6 @@ attach_ms_sound (struct address_info *hw_config)
   int             dma = hw_config->dma;
   int             dma2 = hw_config->dma2;
 
-
   if (hw_config->card_subtype == 1)    /* Has no IRQ/DMA registers */
     {
       ad1848_init ("MS Sound System", hw_config->io_base + 4,
@@ -2350,37 +2404,7 @@ unload_ms_sound (struct address_info *hw_config)
   release_region (hw_config->io_base, 4);
 }
 
-/*
- * WSS compatible PnP codec support
- */
-
-int
-probe_pnp_ad1848 (struct address_info *hw_config)
-{
-  return ad1848_detect (hw_config->io_base, NULL, hw_config->osp);
-}
-
-void
-attach_pnp_ad1848 (struct address_info *hw_config)
-{
-
-  ad1848_init (hw_config->name, hw_config->io_base,
-              hw_config->irq,
-              hw_config->dma,
-              hw_config->dma2, 0, hw_config->osp);
-}
-
-void
-unload_pnp_ad1848 (struct address_info *hw_config)
-{
-  ad1848_unload (hw_config->io_base,
-                hw_config->irq,
-                hw_config->dma,
-                hw_config->dma2, 0);
-  release_region (hw_config->io_base, 4);
-}
-
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 /*
  * Timer stuff (for /dev/music).
  */
@@ -2477,6 +2501,7 @@ ad1848_tmr_restart (int dev)
 static struct sound_lowlev_timer ad1848_tmr =
 {
   0,
+  2,
   ad1848_tmr_start,
   ad1848_tmr_disable,
   ad1848_tmr_restart
index 25b7455a6edab8cff55cd73cf146accf58931cc0..8267aed08ca08895d07839e954cf53f329b3a38d 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -78,7 +78,7 @@ typedef mixer_ent mixer_ents[2];
 #define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r)        \
        {{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}}
 
-mixer_ents ad1848_mix_devices[32] = {
+static mixer_ents ad1848_mix_devices[32] = {
 MIX_ENT(SOUND_MIXER_VOLUME,    27, 1, 0, 4,    29, 1, 0, 4),
 MIX_ENT(SOUND_MIXER_BASS,       0, 0, 0, 0,     0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,     0, 0, 0, 0,     0, 0, 0, 0),
@@ -98,7 +98,7 @@ MIX_ENT(SOUND_MIXER_LINE2,     4, 1, 0, 5,     5, 1, 0, 5),
 MIX_ENT(SOUND_MIXER_LINE3,     18, 1, 0, 5,    19, 1, 0, 5)
 };
 
-mixer_ents iwave_mix_devices[32] = {
+static mixer_ents iwave_mix_devices[32] = {
 MIX_ENT(SOUND_MIXER_VOLUME,    25, 1, 0, 5,    27, 1, 0, 5),
 MIX_ENT(SOUND_MIXER_BASS,       0, 0, 0, 0,     0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,     0, 0, 0, 0,     0, 0, 0, 0),
@@ -108,7 +108,7 @@ MIX_ENT(SOUND_MIXER_SPEAKER,        26, 1, 0, 4,     0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_LINE,      18, 1, 0, 5,    19, 1, 0, 5),
 MIX_ENT(SOUND_MIXER_MIC,        0, 0, 5, 1,     1, 0, 5, 1),
 MIX_ENT(SOUND_MIXER_CD,                 2, 1, 0, 5,     3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_IMIX,      13, 1, 2, 6,     0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_IMIX,      16, 1, 0, 5,    17, 1, 0, 5),
 MIX_ENT(SOUND_MIXER_ALTPCM,     0, 0, 0, 0,     0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_RECLEV,     0, 0, 0, 0,     0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_IGAIN,      0, 0, 0, 4,     1, 0, 0, 4),
@@ -123,7 +123,7 @@ MIX_ENT(SOUND_MIXER_LINE3,  18, 1, 0, 5,    19, 1, 0, 5)
  * VOLUME, SYNTH, LINE, CD are not enabled above.
  * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc.
  */
-mixer_ents c930_mix_devices[32] = {
+static mixer_ents c930_mix_devices[32] = {
 MIX_ENT(SOUND_MIXER_VOLUME,    22, 1, 0, 5,    23, 1, 0, 5),
 MIX_ENT(SOUND_MIXER_BASS,       0, 0, 0, 0,     0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,     0, 0, 0, 0,     0, 0, 0, 0),
index a99891f930eff286c0585d75e6dc0b4bdd4eaec0..227f140b673da756cd8832503e1f0b99dc6656f4 100644 (file)
@@ -5,7 +5,7 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
diff --git a/drivers/sound/aedsp16.c b/drivers/sound/aedsp16.c
deleted file mode 100644 (file)
index 6449ac8..0000000
+++ /dev/null
@@ -1,869 +0,0 @@
-/*
-   sound/aedsp16.c
-
-   Audio Excel DSP 16 software configuration routines
-
-   Copyright (C) 1995  Riccardo Facchetti (riccardo@cdc8g5.cdc.polimi.it)
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met: 1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer. 2.
-   Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-
-   THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
-   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-   DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
-   ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-   SUCH DAMAGE.
- */
-/*
- * Include the main voxware header file. It include all the os/voxware/etc
- * headers needed by this source.
- */
-#include <linux/config.h>
-#include "sound_config.h"
-
-#ifndef AEDSP16_BASE
-#undef CONFIG_AEDSP16
-#endif
-
-#if defined(CONFIG_AEDSP16)
-/*
-
-   READ THIS
-
-   This module is intended for Audio Excel DSP 16 Sound Card.
-
-   Audio Excel DSP 16 is an SB pro II, Microsoft Sound System
-   and MPU-401 compatible card.
-   It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
-   so before this module, the only way to configure the DSP under linux was
-   boot the MS-BAU loading the sound.sys device driver (this driver soft-
-   configure the sound board hardware by massaging someone of its registers),
-   and then ctrl-alt-del to boot linux with the DSP configured by the DOG
-   driver.
-
-   This module works configuring your Audio Excel DSP 16's
-   irq, dma and mpu-401-irq. The voxware probe routines rely on the
-   fact that if the hardware is there, they can detect it. The problem
-   with AEDSP16 is that no hardware can be found by the probe routines
-   if the sound card is not well configured. Sometimes the kernel probe
-   routines can find an SBPRO even when the card is not configured (this
-   is the standard setup of the card), but the SBPRO emulation don't work
-   well if the card is not properly initialized. For this reason
-
-   InitAEDSP16_...()
-
-   routines are called before the voxware probe routines try to detect the
-   hardware.
-
-   NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
-
-   The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
-   the voxware sound driver can be configured for SBPRO and MSS cards
-   at the same time, but the AEDSP16 can't be two cards!!
-   When we configure it, we have to choose the SBPRO or the MSS emulation
-   for AEDSP16. We also can install a *REAL* card of the other type
-   (see [1], not tested but I can't see any reason for it to fail).
-
-   NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
-   please let me know if it works.
-
-   The MPU-401 support can be compiled in together with one of the other
-   two operating modes.
-
-   The board configuration calls, are in the probe_...() routines because
-   we have to configure the board before probing it for a particular
-   hardware. After card configuration, we can probe the hardware.
-
-   NOTE: This is something like plug-and-play: we have only to plug
-   the AEDSP16 board in the socket, and then configure and compile
-   a kernel that uses the AEDSP16 software configuration capability.
-   No jumper setting is needed!
-
-   For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
-   you have just to make config the voxware package, configuring
-   the SBPro sound card with that parameters, then when configure
-   asks if you have an AEDSP16, answer yes. That's it.
-   Compile the kernel and run it.
-
-   NOTE: This means that you can choose irq and dma, but not the
-   I/O addresses. To change I/O addresses you have to set them
-   with jumpers.
-
-   NOTE: InitAEDSP16_...() routines get as parameter the hw_config,
-   the hardware configuration of the - to be configured - board.
-   The InitAEDSP16() routine, configure the board following our
-   wishes, that are in the hw_config structure.
-
-   You can change the irq/dma/mirq settings WITHOUT THE NEED to open
-   your computer and massage the jumpers (there are no irq/dma/mirq
-   jumpers to be configured anyway, only I/O port ones have to be
-   configured with jumpers)
-
-   For some ununderstandable reason, the card default of irq 7, dma 1,
-   don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
-   HDD work, the kernel start to erupt out a lot of messages like:
-
-   'Sound: DMA timed out - IRQ/DRQ config error?'
-
-   For what I can say, I have NOT any conflict at irq 7 (under linux I'm
-   using the lp polling driver), and dma line 1 is unused as stated by
-   /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
-   I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
-   Anyway a setting of irq 10, dma 3 works really fine.
-
-   NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
-   the emulation mode, all the installed hardware and the hardware
-   configuration (irq and dma settings of all the hardware).
-
-   This init module should work with SBPRO+MSS, when one of the two is
-   the AEDSP16 emulation and the other the real card. (see [1])
-   For example:
-
-   AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
-   AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
-
-   MPU401 should work. (see [1])
-
-   [1] Not tested by me for lack of hardware.
-
-   TODO, WISHES AND TECH
-
-   May be there's lot of redundant delays, but for now I want to leave it
-   this way.
-
-   Should be interesting eventually write down a new ioctl for the
-   AEDSP16, to let the suser() change the irq/dma/mirq on the fly.
-   The thing is not trivial.
-   In the real world, there's no need to have such an ioctl because
-   when we configure the kernel for compile, we can choose the config
-   parameters. If we change our mind, we can easily re-config the kernel
-   and re-compile.
-   Why let the suser() change the config parameters on the fly ?
-   If anyone have a reasonable answer to this question, I will write down
-   the code to do it.
-
-   More integration with voxware, using voxware low level routines to
-   read-write DSP is not possible because you may want to have MSS
-   support and in that case we can not rely on the functions included
-   in sb_dsp.c to control 0x2yy I/O ports. I will continue to use my
-   own I/O functions.
-
-   - About I/O ports allocation -
-
-   The request_region should be done at device probe in every sound card
-   module. This module is not the best site for requesting regions.
-   When the request_region code will be added to the main modules such as
-   sb, adlib, gus, ad1848, etc, the requesting code in this module should
-   go away.
-
-   I think the request regions should be done this way:
-
-   if (check_region(...))
-   return ERR; // I/O region already reserved
-   device_probe(...);
-   device_attach(...);
-   request_region(...); // reserve only when we are sure all is okay
-
-   Request the 2x0h region in any case if we are using this card.
-
-   NOTE: the "(SBPro)" string with which we are requesting the AEDSP16 region
-   (see code) does not mean necessarily that we are emulating SBPro.
-   It mean that the region is the SBPro I/O ports region. We use this
-   region to access the control registers of the card, and if emulating
-   SBPro, I/O SBPro registers too. If we are emulating MSS, the SBPro
-   registers are not used, in no way, to emulate an SBPro: they are
-   used only for configuration purposes.
-
-   Someone pointed out that should be possible use both the SBPRO and MSS
-   modes because the sound card have all the two chipsets, supposing that
-   the card is really two cards. I have tried something to have the two
-   modes work together, but, for some reason unknown to me, without success.
-
-   I think all the soft-config only cards have an init sequence similar to
-   this. If you have a card that is not an AEDSP16, you can try to start
-   with this module changing it (mainly in the CMD? I think) to fit your
-   needs.
-
-   Started Fri Mar 17 16:13:18 MET 1995
-
-   v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c)
-   - Initial code.
-   v0.2 (ALPHA)
-   - Cleanups.
-   - Integrated with Linux voxware v 2.90-2 kernel sound driver.
-   - Sound Blaster Pro mode configuration.
-   - Microsoft Sound System mode configuration.
-   - MPU-401 mode configuration.
-   v0.3 (ALPHA)
-   - Cleanups.
-   - Rearranged the code to let InitAEDSP16 be more general.
-   - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
-   inclusion too. We rely on os.h
-   - Used the  to get a variable
-   len string (we are not sure about the len of Copyright string).
-   This works with any SB and compatible.
-   - Added the code to request_region at device init (should go in
-   the main body of voxware).
-   v0.4 (BETA)
-   - Better configure.c patch for AEDSP16 configuration (better
-   logic of inclusion of AEDSP16 support)
-   - Modified the conditional compilation to better support more than
-   one sound card of the emulated type (read the NOTES above)
-   - Moved the sb init routine from the attach to the very first
-   probe in sb_card.c
-   - Rearrangements and cleanups
-   - Wiped out some unnecessary code and variables: this is kernel
-   code so it is better save some TEXT and DATA
-   - Fixed the request_region code. We must allocate the AEDSP16 (SBPro)
-   I/O ports in any case because they are used to access the DSP
-   configuration registers and we can not allow anyone to get them.
-   v0.5
-   - cleanups on comments
-   - prep for diffs against v3.0-proto-950402
-   v0.6
-   - removed the request_region()s when compiling the MODULE sound.o
-   because we are not allowed (by the actual voxware structure) to
-   release_region()
-
- */
-
-
-#define VERSION "0.6"          /* Version of Audio Excel DSP 16 driver */
-
-#undef AEDSP16_DEBUG           /* Define this to enable debug code     */
-/* Actually no debug code is activated  */
-
-/*
- * Hardware related defaults
- */
-#define IRQ  7                 /* 5 7(default) 9 10 11                 */
-#define MIRQ 0                 /* 5 7 9 10 0(default), 0 means disable */
-#define DMA  1                 /* 0 1(default) 3                       */
-
-/*
- * Commands of AEDSP16's DSP (SBPRO+special).
- * For now they are CMDn, in the future they may change.
- */
-#define CMD1 0xe3              /* Get DSP Copyright                    */
-#define CMD2 0xe1              /* Get DSP Version                      */
-#define CMD3 0x88              /*                                      */
-#define CMD4 0x5c              /*                                      */
-#define CMD5 0x50              /* Set M&I&DRQ mask (the real config)   */
-#define CMD6 0x8c              /* Enable Microsoft Sound System mode   */
-
-/*
- * Offsets of AEDSP16 DSP I/O ports. The offset is added to portbase
- * to have the actual I/O port.
- * Register permissions are:
- * (wo) == Write Only
- * (ro) == Read  Only
- * (w-) == Write
- * (r-) == Read
- */
-#define DSP_RESET    0x06      /* offset of DSP RESET             (wo) */
-#define DSP_READ     0x0a      /* offset of DSP READ              (ro) */
-#define DSP_WRITE    0x0c      /* offset of DSP WRITE             (w-) */
-#define DSP_COMMAND  0x0c      /* offset of DSP COMMAND           (w-) */
-#define DSP_STATUS   0x0c      /* offset of DSP STATUS            (r-) */
-#define DSP_DATAVAIL 0x0e      /* offset of DSP DATA AVAILABLE    (ro) */
-
-
-#define RETRY           10     /* Various retry values on I/O opera-   */
-#define STATUSRETRY   1000     /* tions. Sometimes we have to          */
-#define HARDRETRY   500000     /* wait for previous cmd to complete    */
-
-/*
- * Size of character arrays that store name and version of sound card
- */
-#define CARDNAMELEN 15         /* Size of the card's name in chars     */
-#define CARDVERLEN  2          /* Size of the card's version in chars  */
-
-/*
- * Bit mapped flags for calling InitAEDSP16(), and saving the current
- * emulation mode.
- */
-#define INIT_NONE   (0   )
-#define INIT_SBPRO  (1<<0)
-#define INIT_MSS    (1<<1)
-#define INIT_MPU401 (1<<2)
-#define RESET_DSP16 (1<<3)
-
-/* Base HW Port for Audio Card          */
-static int      portbase = AEDSP16_BASE;
-static int      irq = IRQ;     /* irq for DSP I/O                      */
-static int      mirq = MIRQ;   /* irq for MPU-401 I/O                  */
-static int      dma = DMA;     /* dma for DSP I/O                      */
-
-/* Init status of the card              */
-static int      ae_init = INIT_NONE;   /* (bitmapped variable)                 */
-static int      oredparams = 0;        /* Will contain or'ed values of params  */
-static int      gc = 0;                /* generic counter (utility counter)    */
-struct orVals
-  {                            /* Contain the values to be or'ed       */
-    int             val;       /* irq|mirq|dma                         */
-    int             or;                /* oredparams |= TheStruct.or           */
-  };
-
-/*
- * Magic values that the DSP will eat when configuring irq/mirq/dma
- */
-/* DSP IRQ conversion array             */
-static struct orVals orIRQ[] =
-{
-  {0x05, 0x28},
-  {0x07, 0x08},
-  {0x09, 0x10},
-  {0x0a, 0x18},
-  {0x0b, 0x20},
-  {0x00, 0x00}
-};
-
-/* MPU-401 IRQ conversion array         */
-static struct orVals orMIRQ[] =
-{
-  {0x05, 0x04},
-  {0x07, 0x44},
-  {0x09, 0x84},
-  {0x0a, 0xc4},
-  {0x00, 0x00}
-};
-
-/* DMA Channels conversion array        */
-static struct orVals orDMA[] =
-{
-  {0x00, 0x01},
-  {0x01, 0x02},
-  {0x03, 0x03},
-  {0x00, 0x00}
-};
-
-/*
- * Buffers to store audio card informations
- */
-static char     AudioExcelName[CARDNAMELEN + 1];
-static char     AudioExcelVersion[CARDVERLEN + 1];
-
-static void
-tenmillisec (void)
-{
-
-  for (gc = 0; gc < 1000; gc++)
-    tenmicrosec ();
-}
-
-static int
-WaitForDataAvail (int port)
-{
-  int             loop = STATUSRETRY;
-  unsigned char   ret = 0;
-
-  do
-    {
-      ret = inb (port + DSP_DATAVAIL);
-      /*
-         * Wait for data available (bit 7 of ret == 1)
-       */
-    }
-  while (!(ret & 0x80) && loop--);
-
-  if (ret & 0x80)
-    return 0;
-
-  return -1;
-}
-
-static int
-ReadData (int port)
-{
-  if (WaitForDataAvail (port))
-    return -1;
-  return inb (port + DSP_READ);
-}
-
-static int
-CheckDSPOkay (int port)
-{
-  return ((ReadData (port) == 0xaa) ? 0 : -1);
-}
-
-static int
-ResetBoard (int port)
-{
-  /*
-     * Reset DSP
-   */
-  outb ((1), (port + DSP_RESET));
-  tenmicrosec ();
-  outb ((0), (port + DSP_RESET));
-  tenmicrosec ();
-  tenmicrosec ();
-  return CheckDSPOkay (port);
-}
-
-static int
-WriteDSPCommand (int port, int cmd)
-{
-  unsigned char   ret;
-  int             loop = HARDRETRY;
-
-  do
-    {
-      ret = inb (port + DSP_STATUS);
-      /*
-         * DSP ready to receive data if bit 7 of ret == 0
-       */
-      if (!(ret & 0x80))
-       {
-         outb ((cmd), port + DSP_COMMAND);
-         return 0;
-       }
-    }
-  while (loop--);
-
-  printk ("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
-  return -1;
-}
-
-int
-InitMSS (int port)
-{
-
-  tenmillisec ();
-
-  if (WriteDSPCommand (port, CMD6))
-    {
-      printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD6);
-      return -1;
-    }
-
-  tenmillisec ();
-
-  return 0;
-}
-
-static int
-SetUpBoard (int port)
-{
-  int             loop = RETRY;
-
-  do
-    {
-      if (WriteDSPCommand (portbase, CMD3))
-       {
-         printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD3);
-         return -1;
-       }
-
-      tenmillisec ();
-
-    }
-  while (WaitForDataAvail (port) && loop--);
-
-#if defined(THIS_SHOULD_GO_AWAY)
-  if (CheckDSPOkay (port))
-    {
-      printk ("[AEDSP16]     CheckDSPOkay: failed\n");
-      return -1;
-    }
-#else
-  if (ReadData (port) == -1)
-    {
-      printk ("[AEDSP16] ReadData after CMD 0x%x: failed\n", CMD3);
-      return -1;
-    }
-#endif
-
-  if (WriteDSPCommand (portbase, CMD4))
-    {
-      printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD4);
-      return -1;
-    }
-
-  if (WriteDSPCommand (portbase, CMD5))
-    {
-      printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD5);
-      return -1;
-    }
-
-  if (WriteDSPCommand (portbase, oredparams))
-    {
-      printk ("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
-      return -1;
-    }
-  return 0;
-}
-
-static int
-GetCardVersion (int port)
-{
-  int             len = 0;
-  int             ret;
-  int             ver[3];
-
-  do
-    {
-      if ((ret = ReadData (port)) == -1)
-       return -1;
-      /*
-         * We already know how many int are stored (2), so we know when the
-         * string is finished.
-       */
-      ver[len++] = ret;
-    }
-  while (len < CARDVERLEN);
-  sprintf (AudioExcelVersion, "%d.%d", ver[0], ver[1]);
-  return 0;
-}
-
-static int
-GetCardName (int port)
-{
-  int             len = 0;
-  int             ret;
-
-  do
-    {
-      if ((ret = ReadData (port)) == -1)
-       /*
-          * If no more data available, return to the caller, no error if len>0.
-          * We have no other way to know when the string is finished.
-        */
-       return (len ? 0 : -1);
-
-      AudioExcelName[len++] = ret;
-
-    }
-  while (len < CARDNAMELEN);
-  return 0;
-}
-
-static void
-InitializeHardParams (void)
-{
-
-  memset (AudioExcelName, 0, CARDNAMELEN + 1);
-  memset (AudioExcelVersion, 0, CARDVERLEN + 1);
-
-  for (gc = 0; orIRQ[gc].or; gc++)
-    if (orIRQ[gc].val == irq)
-      oredparams |= orIRQ[gc].or;
-
-  for (gc = 0; orMIRQ[gc].or; gc++)
-    if (orMIRQ[gc].or == mirq)
-      oredparams |= orMIRQ[gc].or;
-
-  for (gc = 0; orDMA[gc].or; gc++)
-    if (orDMA[gc].val == dma)
-      oredparams |= orDMA[gc].or;
-}
-
-static int
-InitAEDSP16 (int which)
-{
-  static char    *InitName = NULL;
-
-  InitializeHardParams ();
-
-  if (ResetBoard (portbase))
-    {
-      printk ("[AEDSP16] ResetBoard: failed!\n");
-      return -1;
-    }
-
-#if defined(THIS_SHOULD_GO_AWAY)
-  if (CheckDSPOkay (portbase))
-    {
-      printk ("[AEDSP16] CheckDSPOkay: failed!\n");
-      return -1;
-    }
-#endif
-
-  if (WriteDSPCommand (portbase, CMD1))
-    {
-      printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD1);
-      return -1;
-    }
-
-  if (GetCardName (portbase))
-    {
-      printk ("[AEDSP16] GetCardName: failed!\n");
-      return -1;
-    }
-
-  /*
-     * My AEDSP16 card return SC-6000 in AudioExcelName, so
-     * if we have something different, we have to be warned.
-   */
-  if (strcmp ("SC-6000", AudioExcelName))
-    printk ("[AEDSP16] Warning: non SC-6000 audio card!\n");
-
-  if (WriteDSPCommand (portbase, CMD2))
-    {
-      printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD2);
-      return -1;
-    }
-
-  if (GetCardVersion (portbase))
-    {
-      printk ("[AEDSP16] GetCardVersion: failed!\n");
-      return -1;
-    }
-
-  if (SetUpBoard (portbase))
-    {
-      printk ("[AEDSP16] SetUpBoard: failed!\n");
-      return -1;
-    }
-
-  if (which == INIT_MSS)
-    {
-      if (InitMSS (portbase))
-       {
-         printk ("[AEDSP16] Can't initialize Microsoft Sound System mode.\n");
-         return -1;
-       }
-    }
-
-  /*
-     * If we are resetting, do not print any message because we may be
-     * in playing and we do not want lost too much time.
-   */
-  if (!(which & RESET_DSP16))
-    {
-      if (which & INIT_MPU401)
-       InitName = "MPU401";
-      else if (which & INIT_SBPRO)
-       InitName = "SBPro";
-      else if (which & INIT_MSS)
-       InitName = "MSS";
-      else
-       InitName = "None";
-
-      printk ("Audio Excel DSP 16 init v%s (%s %s) [%s]\n",
-             VERSION, AudioExcelName,
-             AudioExcelVersion, InitName);
-    }
-
-  tenmillisec ();
-
-  return 0;
-}
-
-#if defined(AEDSP16_SBPRO)
-
-int
-InitAEDSP16_SBPRO (struct address_info *hw_config)
-{
-  /*
-     * If the card is already init'ed MSS, we can not init it to SBPRO too
-     * because the board can not emulate simultaneously MSS and SBPRO.
-   */
-  if (ae_init & INIT_MSS)
-    return -1;
-  if (ae_init & INIT_SBPRO)
-    return 0;
-
-  /*
-     * For now we will leave this
-     * code included only when INCLUDE_AEDSP16 is configured in, but it should
-     * be better include it every time.
-   */
-  if (!(ae_init & INIT_MPU401))
-    {
-      if (check_region (hw_config->io_base, 0x0f))
-       {
-         printk ("AEDSP16/SBPRO I/O port region is already in use.\n");
-         return -1;
-       }
-    }
-
-  /*
-     * Set up the internal hardware parameters, to let the driver reach
-     * the Sound Card.
-   */
-  portbase = hw_config->io_base;
-  irq = hw_config->irq;
-  dma = hw_config->dma;
-
-  if (InitAEDSP16 (INIT_SBPRO))
-    return -1;
-
-#if !defined(MODULE)
-  /*
-     * If we are compiling sound.o (MODULAR version) we can not
-     * request any region because there is not a uninit routine that
-     * can allow me to release the requested region.
-   */
-  if (!(ae_init & INIT_MPU401))
-    request_region (hw_config->io_base, 0x0f, "aedsp16 (sbpro)");
-#endif
-
-  ae_init |= INIT_SBPRO;
-  return 0;
-}
-
-#endif /* AEDSP16_SBPRO */
-
-#if defined(AEDSP16_MSS)
-
-int
-InitAEDSP16_MSS (struct address_info *hw_config)
-{
-  /*
-     * If the card is already init'ed SBPRO, we can not init it to MSS too
-     * because the board can not emulate simultaneously MSS and SBPRO.
-   */
-  if (ae_init & INIT_SBPRO)
-    return -1;
-  if (ae_init & INIT_MSS)
-    return 0;
-
-  /*
-     * For now we will leave this
-     * code included only when INCLUDE_AEDSP16 is configured in, but it should
-     * be better include it every time.
-   */
-  if (check_region (hw_config->io_base, 0x08))
-    {
-      printk ("MSS I/O port region is already in use.\n");
-      return -1;
-    }
-
-  /*
-     * We must allocate the AEDSP16 region too because these are the I/O ports
-     * to access card's control registers.
-   */
-  if (!(ae_init & INIT_MPU401))
-    {
-      if (check_region (AEDSP16_BASE, 0x0f))
-       {
-         printk ("AEDSP16 I/O port region is already in use.\n");
-         return -1;
-       }
-    }
-
-
-  /*
-     * If we are configuring the card for MSS, the portbase for card
-     * configuration is the default one (0x220 unless you have changed the
-     * factory default with board switches), so no need to modify the
-     * portbase variable.
-     * The default is AEDSP16_BASE, that is the right value.
-   */
-  irq = hw_config->irq;
-  dma = hw_config->dma;
-
-  if (InitAEDSP16 (INIT_MSS))
-    return -1;
-
-#if !defined(MODULE)
-  /*
-     * If we are compiling sound.o (MODULAR version) we can not
-     * request any region because there is not a uninit routine that
-     * can allow me to release the requested region. So when unloading
-     * and then reloading it, we are going to have some nice Oops!
-   */
-  request_region (hw_config->io_base, 0x08, "aedsp16 (mss)");
-#endif
-
-  if (!(ae_init & INIT_MPU401))
-    request_region (AEDSP16_BASE, 0x0f, "aedsp16 (sbpro)");
-
-  ae_init |= INIT_MSS;
-  return 0;
-}
-
-#endif /* AEDSP16_MSS */
-
-#if defined(AEDSP16_MPU401)
-
-int
-InitAEDSP16_MPU401 (struct address_info *hw_config)
-{
-  if (ae_init & INIT_MPU401)
-    return 0;
-
-  /*
-     * For now we will leave this
-     * code included only when INCLUDE_AEDSP16 is configured in, but it should
-     * be better include it every time.
-   */
-  if (check_region (hw_config->io_base, 0x02))
-    {
-      printk ("SB I/O port region is already in use.\n");
-      return -1;
-    }
-
-  /*
-     * We must allocate the AEDSP16 region too because these are the I/O ports
-     * to access card's control registers.
-   */
-  if (!(ae_init & (INIT_MSS | INIT_SBPRO)))
-    {
-      if (check_region (AEDSP16_BASE, 0x0f))
-       {
-         printk ("AEDSP16 I/O port region is already in use.\n");
-         return -1;
-       }
-    }
-
-  /*
-     * If mpu401, the irq and dma are not important, do not touch it
-     * because we may use the default if SBPro is not yet configured,
-     * we may use the SBPro ones if configured, and nothing wrong
-     * should happen.
-     *
-     * The mirq default is 0, but once set it to non-0 value, we should
-     * not touch it anymore (unless I write an ioctl to do it, of course).
-   */
-  mirq = hw_config->irq;
-  if (InitAEDSP16 (INIT_MPU401))
-    return -1;
-
-#if !defined(MODULE)
-  /*
-     * If we are compiling sound.o (MODULAR version) we can not
-     * request any region because there is not a uninit routine that
-     * can allow me to release the requested region.
-   */
-  request_region (hw_config->io_base, 0x02, "aedsp16 (mpu401)");
-#endif
-
-  if (!(ae_init & (INIT_MSS | INIT_SBPRO)))
-    request_region (AEDSP16_BASE, 0x0f, "aedsp16 (sbpro)");
-
-  ae_init |= INIT_MPU401;
-  return 0;
-}
-
-#endif /* AEDSP16_MPU401 */
-
-#if 0                          /* Leave it out for now. We are not using this portion of code. */
-
-/*
- * Entry point for a reset function.
- * May be I will write the infamous ioctl :)
- */
-int
-ResetAEDSP16 (void)
-{
-#if defined(AEDSP16_DEBUG)
-  printk ("[AEDSP16] ResetAEDSP16 called.\n");
-#endif
-  return InitAEDSP16 (RESET_DSP16);
-}
-
-#endif /* 0 */
-
-#endif /* CONFIG_AEDSP16 */
index d94a7b164987ad052a0245c836e1d17e02518040..0ce7c0a9ddf10f42cafc151a4adf31bae6aad2ff 100644 (file)
@@ -5,14 +5,14 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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 <linux/poll.h>
+
 
 #include "sound_config.h"
 
@@ -24,6 +24,9 @@
 #define ON             1
 #define OFF            0
 
+#define NEUTRAL8       0x80
+#define NEUTRAL16      0x00
+
 static int      audio_mode[MAX_AUDIO_DEV];
 static int      dev_nblock[MAX_AUDIO_DEV];     /* 1 if in nonblocking mode */
 
@@ -95,9 +98,6 @@ audio_open (int dev, struct fileinfo *file)
 
   local_conversion[dev] = 0;
 
-  if (audio_devs[dev]->d->set_bits (dev, bits) != bits)
-    {
-    }
 
   if (dev_type == SND_DEV_AUDIO)
     {
@@ -113,23 +113,33 @@ audio_open (int dev, struct fileinfo *file)
   return ret;
 }
 
-void
+static void
 sync_output (int dev)
 {
-  int             buf_no, buf_ptr, buf_size, p, i;
-  char           *dma_buf;
+  int             p, i;
+  int             l;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-  if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0)
+  if (dmap->fragment_size <= 0)
+    return;
+  dmap->flags |= DMA_POST;
+
+  /* Align the write pointer with fragment boundaries */
+  if ((l = dmap->user_counter % dmap->fragment_size) > 0)
     {
-      int             i, n = buf_size & 3;
+      char           *ptr;
+      int             err, dummylen, len = dmap->fragment_size - l;
 
-      if (n)                   /* Not 4 byte aligned */
-       {
-         for (i = 0; i < n; i++)
-           dma_buf[buf_ptr++] = dmap->neutral_byte;
-       }
-      DMAbuf_start_output (dev, buf_no, buf_ptr);
+      if ((err = DMAbuf_getwrbuffer (dev, &ptr, &dummylen, 1)) >= 0)
+       if (dummylen >= len && ((long) ptr % dmap->fragment_size) == l)
+         {
+           if ((ptr + len) > (dmap->raw_buf + audio_devs[dev]->buffsize))
+             printk ("audio: Buffer error 1\n");
+           if (ptr < dmap->raw_buf)
+             printk ("audio: Buffer error 11\n");
+           memset (ptr, dmap->neutral_byte, len);
+           DMAbuf_move_wrpointer (dev, len);
+         }
     }
 
 /*
@@ -137,16 +147,21 @@ sync_output (int dev)
  */
 
   p = dmap->qtail;
+  dmap->flags |= DMA_POST;
 
   for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
     {
       p = (p + 1) % dmap->nbufs;
+      if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
+         (dmap->raw_buf + audio_devs[dev]->buffsize))
+       printk ("audio: Buffer error 2\n");
+
       memset (dmap->raw_buf + p * dmap->fragment_size,
              dmap->neutral_byte,
              dmap->fragment_size);
     }
 
-  dmap->flags |= DMA_CLEAN;
+  dmap->flags |= DMA_DIRTY;
 }
 
 void
@@ -201,7 +216,7 @@ translate_bytes (const void *table, void *buff, int n)
 int
 audio_write (int dev, struct fileinfo *file, const char *buf, int count)
 {
-  int             c, p, l, buf_no, buf_ptr, buf_size;
+  int             c, p, l, buf_size;
   int             err;
   char           *dma_buf;
 
@@ -210,9 +225,8 @@ audio_write (int dev, struct fileinfo *file, const char *buf, int count)
   p = 0;
   c = count;
 
-  if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
-    {                          /* Direction change */
-    }
+  if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+    return -EPERM;
 
   if (audio_devs[dev]->flags & DMA_DUPLEX)
     audio_mode[dev] |= AM_WRITE;
@@ -227,34 +241,33 @@ audio_write (int dev, struct fileinfo *file, const char *buf, int count)
 
   while (c)
     {
-      if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) < 0)
+      if ((err = DMAbuf_getwrbuffer (dev, &dma_buf, &buf_size, dev_nblock[dev]) < 0))
        {
-         if ((buf_no = DMAbuf_getwrbuffer (dev, &dma_buf,
-                                           &buf_size,
-                                           dev_nblock[dev])) < 0)
-           {
-             /* Handle nonblocking mode */
-             if (dev_nblock[dev] && buf_no == -EAGAIN)
-               return p;       /* No more space. Return # of accepted bytes */
-             return buf_no;
-           }
-         buf_ptr = 0;
+         /* Handle nonblocking mode */
+         if (dev_nblock[dev] && err == -EAGAIN)
+           return p;           /* No more space. Return # of accepted bytes */
+         return err;
        }
 
       l = c;
-      if (l > (buf_size - buf_ptr))
-       l = (buf_size - buf_ptr);
-
+      if (l > buf_size)
+       l = buf_size;
 
       if (!audio_devs[dev]->d->copy_user)
-       {                       /*
-                                * No device specific copy routine
-                                */
-         copy_from_user (&dma_buf[buf_ptr], &(buf)[p], l);
+       {
+         if ((dma_buf + l) >
+          (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->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]->buffsize);
+         if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
+           printk ("audio: Buffer error 13\n");
+         copy_from_user (dma_buf, &(buf)[p], l);
        }
       else
        audio_devs[dev]->d->copy_user (dev,
-                                      dma_buf, buf_ptr, buf, p, l);
+                                      dma_buf, 0, buf, p, l);
 
       if (local_conversion[dev] & CNV_MU_LAW)
        {
@@ -262,23 +275,12 @@ audio_write (int dev, struct fileinfo *file, const char *buf, int count)
           * This just allows interrupts while the conversion is running
           */
          sti ();
-         translate_bytes (ulaw_dsp, (unsigned char *) &dma_buf[buf_ptr], l);
+         translate_bytes (ulaw_dsp, (unsigned char *) dma_buf, l);
        }
 
       c -= l;
       p += l;
-      buf_ptr += l;
-
-      if (buf_ptr >= buf_size)
-       {
-         if ((err = DMAbuf_start_output (dev, buf_no, buf_ptr)) < 0)
-           {
-             return err;
-           }
-
-       }
-      else
-       DMAbuf_set_count (dev, buf_no, buf_ptr);
+      DMAbuf_move_wrpointer (dev, l);
 
     }
 
@@ -296,6 +298,9 @@ audio_read (int dev, struct fileinfo *file, char *buf, int count)
   p = 0;
   c = count;
 
+  if (!(audio_devs[dev]->open_mode & OPEN_READ))
+    return -EPERM;
+
   if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
     {
       sync_output (dev);
@@ -352,12 +357,12 @@ audio_read (int dev, struct fileinfo *file, char *buf, int count)
 }
 
 int
-audio_ioctl (int dev, struct fileinfo *file,
+audio_ioctl (int dev, struct fileinfo *file_must_not_be_used,
             unsigned int cmd, caddr_t arg)
 {
   int             val;
 
-/* printk("audio_ioctl(%x, %x)\n", cmd, arg); */
+  /* printk("audio_ioctl(%x, %x)\n", (int)cmd, (int)arg); */
 
   dev = dev >> 4;
 
@@ -377,6 +382,8 @@ audio_ioctl (int dev, struct fileinfo *file,
        if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
          return 0;
 
+       if (audio_devs[dev]->dmap_out->fragment_size == 0)
+         return 0;
        sync_output (dev);
        DMAbuf_sync (dev);
        DMAbuf_reset (dev);
@@ -386,9 +393,11 @@ audio_ioctl (int dev, struct fileinfo *file,
       case SNDCTL_DSP_POST:
        if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
          return 0;
-       audio_devs[dev]->dmap_out->flags |= DMA_POST;
+       if (audio_devs[dev]->dmap_out->fragment_size == 0)
+         return 0;
+       audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
        sync_output (dev);
-       DMAbuf_ioctl (dev, SNDCTL_DSP_POST, 0, 1);
+       dma_ioctl (dev, SNDCTL_DSP_POST, (caddr_t) 0);
        return 0;
        break;
 
@@ -399,12 +408,12 @@ audio_ioctl (int dev, struct fileinfo *file,
        break;
 
       case SNDCTL_DSP_GETFMTS:
-       return ioctl_out (arg, audio_devs[dev]->format_mask);
+       return (*(int *) arg = audio_devs[dev]->format_mask);
        break;
 
       case SNDCTL_DSP_SETFMT:
-       get_user (val, (int *) arg);
-       return ioctl_out (arg, set_format (dev, val));
+       val = *(int *) arg;
+       return (*(int *) arg = set_format (dev, val));
 
       case SNDCTL_DSP_GETISPACE:
        if (!(audio_devs[dev]->open_mode & OPEN_READ))
@@ -415,16 +424,12 @@ audio_ioctl (int dev, struct fileinfo *file,
        {
          audio_buf_info  info;
 
-         int             err = DMAbuf_ioctl (dev, cmd, (caddr_t) & info, 1);
+         int             err = dma_ioctl (dev, cmd, (caddr_t) & info);
 
          if (err < 0)
            return err;
 
-         {
-           char           *fixit = (char *) &info;
-
-           copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-         };
+         memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
          return 0;
        }
 
@@ -436,22 +441,13 @@ audio_ioctl (int dev, struct fileinfo *file,
 
        {
          audio_buf_info  info;
-         char           *dma_buf;
-         int             buf_no, buf_ptr, buf_size;
 
-         int             err = DMAbuf_ioctl (dev, cmd, (caddr_t) & info, 1);
+         int             err = dma_ioctl (dev, cmd, (caddr_t) & info);
 
          if (err < 0)
            return err;
 
-         if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0)
-           info.bytes -= buf_ptr;
-
-         {
-           char           *fixit = (char *) &info;
-
-           copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-         };
+         memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
          return 0;
        }
 
@@ -464,7 +460,8 @@ audio_ioctl (int dev, struct fileinfo *file,
        {
          int             info = 1;     /* Revision level of this ioctl() */
 
-         if (audio_devs[dev]->flags & DMA_DUPLEX)
+         if (audio_devs[dev]->flags & DMA_DUPLEX &&
+             audio_devs[dev]->open_mode == OPEN_READWRITE)
            info |= DSP_CAP_DUPLEX;
 
          if (audio_devs[dev]->coproc)
@@ -478,59 +475,65 @@ audio_ioctl (int dev, struct fileinfo *file,
 
          info |= DSP_CAP_MMAP;
 
-         {
-           char           *fixit = (char *) &info;
-
-           copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-         };
+         memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
          return 0;
        }
        break;
 
       case SOUND_PCM_WRITE_RATE:
-       get_user (val, (int *) arg);
-       return ioctl_out (arg, audio_devs[dev]->d->set_speed (dev, val));
+       val = *(int *) arg;
+       return (*(int *) arg = audio_devs[dev]->d->set_speed (dev, val));
 
       case SOUND_PCM_READ_RATE:
-       return ioctl_out (arg, audio_devs[dev]->d->set_speed (dev, 0));
+       return (*(int *) arg = audio_devs[dev]->d->set_speed (dev, 0));
 
       case SNDCTL_DSP_STEREO:
        {
          int             n;
 
-         get_user (n, (int *) arg);
+         n = *(int *) arg;
          if (n > 1)
            {
              printk ("sound: SNDCTL_DSP_STEREO called with invalid argument %d\n",
                      n);
-             return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, n));
+             return -EINVAL;
            }
 
          if (n < 0)
            return -EINVAL;
 
-         return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, n + 1) - 1);
+         return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, n + 1) - 1);
        }
 
       case SOUND_PCM_WRITE_CHANNELS:
-       get_user (val, (int *) arg);
-       return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, val));
+       val = *(int *) arg;
+       return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, val));
 
       case SOUND_PCM_READ_CHANNELS:
-       return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, 0));
+       return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, 0));
 
       case SOUND_PCM_READ_BITS:
-       return ioctl_out (arg, audio_devs[dev]->d->set_bits (dev, 0));
+       return (*(int *) arg = audio_devs[dev]->d->set_bits (dev, 0));
 
       case SNDCTL_DSP_SETDUPLEX:
+       if (audio_devs[dev]->open_mode != OPEN_READWRITE)
+         return -EPERM;
        if (audio_devs[dev]->flags & DMA_DUPLEX)
          return 0;
        else
          return -EIO;
        break;
 
+      case SNDCTL_DSP_PROFILE:
+       if (audio_devs[dev]->open_mode & OPEN_WRITE)
+         audio_devs[dev]->dmap_out->applic_profile = *(int *) arg;
+       if (audio_devs[dev]->open_mode & OPEN_READ)
+         audio_devs[dev]->dmap_in->applic_profile = *(int *) arg;
+       return 0;
+       break;
+
       default:
-       return DMAbuf_ioctl (dev, cmd, arg, 0);
+       return dma_ioctl (dev, cmd, arg);
       }
 }
 
@@ -542,32 +545,499 @@ audio_init_devices (void)
    */
 }
 
-unsigned int
-audio_poll (kdev_t dev, struct fileinfo *file, poll_table * wait)
+int
+audio_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
 {
-  char           *dma_buf;
-  unsigned int   mask = 0;
-  int             buf_no, buf_ptr, buf_size;
-
   dev = dev >> 4;
 
-  mask = DMAbuf_poll (dev, file, wait);
+  switch (sel_type)
+    {
+    case SEL_IN:
+      if (!(audio_devs[dev]->open_mode & OPEN_READ))
+       return 0;
+      if (audio_mode[dev] & AM_WRITE && !(audio_devs[dev]->flags & DMA_DUPLEX))
+       {
+         return 0;             /* Not recording */
+       }
+
+      return DMAbuf_select (dev, file, sel_type, wait);
+      break;
+
+    case SEL_OUT:
+      if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+       return 0;
+      if (audio_mode[dev] & AM_READ && !(audio_devs[dev]->flags & DMA_DUPLEX))
+       {
+         return 0;             /* Wrong direction */
+       }
+
+      return DMAbuf_select (dev, file, sel_type, wait);
+      break;
+
+    case SEL_EX:
+      return 0;
+    }
+
+  return 0;
+}
+
+
+#endif
+
+void
+reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
+{
+  /*
+   * This routine breaks the physical device buffers to logical ones.
+   */
+
+  struct audio_operations *dsp_dev = audio_devs[dev];
+
+  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;
+  else
+    dmap->neutral_byte = NEUTRAL16;
+
+  if (sr < 1 || nc < 1 || sz < 1)
+    {
+      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;
+    }
+
+  sz = sr * nc * sz;
+
+  sz /= 8;                     /* #bits -> #bytes */
+  dmap->data_rate = sz;
+
+  if (dmap->fragment_size == 0)
+    {                          /* Compute the fragment size using the default algorithm */
+
+      /*
+         * Compute a buffer size for time not exceeding 1 second.
+         * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
+         * of sound (using the current speed, sample size and #channels).
+       */
+
+      bsz = dsp_dev->buffsize;
+      while (bsz > sz)
+       bsz /= 2;
+
+      if (bsz == dsp_dev->buffsize)
+       bsz /= 2;               /* Needs at least 2 buffers */
+
+/*
+ *    Split the computed fragment to smaller parts. After 3.5a9
+ *      the default subdivision is 4 which should give better
+ *      results when recording.
+ */
+
+      if (dmap->subdivision == 0)      /* Not already set */
+       {
+         dmap->subdivision = 4;        /* Init to the default value */
+
+         if ((bsz / dmap->subdivision) > 4096)
+           dmap->subdivision *= 2;
+         if ((bsz / dmap->subdivision) < 4096)
+           dmap->subdivision = 1;
+       }
+
+      bsz /= dmap->subdivision;
+
+      if (bsz < 16)
+       bsz = 16;               /* Just a sanity check */
+
+      dmap->fragment_size = bsz;
+    }
+  else
+    {
+      /*
+         * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
+         * the buffer size computation has already been done.
+       */
+      if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2))
+       dmap->fragment_size = (audio_devs[dev]->buffsize / 2);
+      bsz = dmap->fragment_size;
+    }
+
+  bsz &= ~0x03;                        /* Force size which is multiple of 4 bytes */
+#ifdef OS_DMA_ALIGN_CHECK
+  OS_DMA_ALIGN_CHECK (bsz);
+#endif
+
+  n = dsp_dev->buffsize / bsz;
+  if (n > MAX_SUB_BUFFERS)
+    n = MAX_SUB_BUFFERS;
+  if (n > dmap->max_fragments)
+    n = dmap->max_fragments;
+
+  if (n < 2)
+    {
+      n = 2;
+      bsz /= 2;
+    }
+
+  dmap->nbufs = n;
+  dmap->bytes_in_use = n * bsz;
+  dmap->fragment_size = bsz;
+
+  if (dmap->raw_buf)
+    {
+      memset (dmap->raw_buf,
+             dmap->neutral_byte,
+             dmap->bytes_in_use);
+    }
+
+  for (i = 0; i < dmap->nbufs; i++)
+    {
+      dmap->counts[i] = 0;
+    }
+
+  dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
+}
+
+static int
+dma_subdivide (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+{
+  if (fact == 0)
+    {
+      fact = dmap->subdivision;
+      if (fact == 0)
+       fact = 1;
+      return (*(int *) arg = fact);
+    }
 
-/* sel_in */
-  if (audio_mode[dev] & AM_WRITE && !(audio_devs[dev]->flags & DMA_DUPLEX))
-    mask &= ~(POLLIN | POLLRDNORM); /* Wrong direction */
+  if (dmap->subdivision != 0 ||
+      dmap->fragment_size)     /* Too late to change */
+    return -EINVAL;
 
-/* sel_out */
-  if (audio_mode[dev] & AM_READ && !(audio_devs[dev]->flags & DMA_DUPLEX)) {
-    mask &= ~(POLLOUT | POLLWRNORM); /* Wrong direction */
-    goto sel_ex;
-  }
-  if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0)
-    mask |= POLLOUT | POLLWRNORM;
+  if (fact > MAX_REALTIME_FACTOR)
+    return -EINVAL;
 
- sel_ex:
-  return mask;
+  if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+    return -EINVAL;
+
+  dmap->subdivision = fact;
+  return (*(int *) arg = fact);
 }
 
+static int
+dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+{
+  int             bytes, count;
+
+  if (fact == 0)
+    return -EIO;
+
+  if (dmap->subdivision != 0 ||
+      dmap->fragment_size)     /* Too late to change */
+    return -EINVAL;
+
+  bytes = fact & 0xffff;
+  count = (fact >> 16) & 0x7fff;
+
+  if (count == 0)
+    count = MAX_SUB_BUFFERS;
+
+  if (bytes < 4 || bytes > 17) /* <16 || > 512k */
+    return -EINVAL;
+
+  if (count < 2)
+    return -EINVAL;
 
+  if (audio_devs[dev]->min_fragment > 0)
+    if (bytes < audio_devs[dev]->min_fragment)
+      bytes = audio_devs[dev]->min_fragment;
+
+#ifdef OS_DMA_MINBITS
+  if (bytes < OS_DMA_MINBITS)
+    bytes = OS_DMA_MINBITS;
 #endif
+
+  dmap->fragment_size = (1 << bytes);
+  dmap->max_fragments = count;
+
+  if (dmap->fragment_size > audio_devs[dev]->buffsize)
+    dmap->fragment_size = audio_devs[dev]->buffsize;
+
+  if (dmap->fragment_size == audio_devs[dev]->buffsize &&
+      audio_devs[dev]->flags & DMA_AUTOMODE)
+    dmap->fragment_size /= 2;  /* Needs at least 2 buffers */
+
+  dmap->subdivision = 1;       /* Disable SNDCTL_DSP_SUBDIVIDE */
+  if (arg)
+    return (*(int *) arg = bytes | (count << 16));
+  else
+    return 0;
+}
+
+static int
+dma_ioctl (int dev, unsigned int cmd, caddr_t arg)
+{
+
+  struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
+  struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
+
+  switch (cmd)
+    {
+
+    case SNDCTL_DSP_SUBDIVIDE:
+      {
+       int             fact;
+       int             ret;
+
+       fact = *(int *) arg;
+
+       ret = dma_subdivide (dev, dmap_out, arg, fact);
+       if (ret < 0)
+         return ret;
+
+       if (audio_devs[dev]->flags & DMA_DUPLEX &&
+           audio_devs[dev]->open_mode & OPEN_READ)
+         ret = dma_subdivide (dev, dmap_in, arg, fact);
+
+       return ret;
+      }
+      break;
+
+    case SNDCTL_DSP_GETISPACE:
+    case SNDCTL_DSP_GETOSPACE:
+      {
+       struct dma_buffparms *dmap = dmap_out;
+
+       audio_buf_info *info = (audio_buf_info *) arg;
+
+       if (cmd == SNDCTL_DSP_GETISPACE &&
+           !(audio_devs[dev]->open_mode & OPEN_READ))
+         return -EINVAL;
+
+       if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
+         dmap = dmap_in;
+
+       if (dmap->mapping_flags & DMA_MAP_MAPPED)
+         return -EINVAL;
+
+       if (!(dmap->flags & DMA_ALLOC_DONE))
+         reorganize_buffers (dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+
+       info->fragstotal = dmap->nbufs;
+
+       if (cmd == SNDCTL_DSP_GETISPACE)
+         info->fragments = dmap->qlen;
+       else
+         {
+           if (!DMAbuf_space_in_queue (dev))
+             info->fragments = 0;
+           else
+             {
+               info->fragments = dmap->nbufs - dmap->qlen;
+               if (audio_devs[dev]->d->local_qlen)
+                 {
+                   int             tmp = audio_devs[dev]->d->local_qlen (dev);
+
+                   if (tmp && info->fragments)
+                     tmp--;    /*
+                                  * This buffer has been counted twice
+                                */
+                   info->fragments -= tmp;
+                 }
+             }
+         }
+
+       if (info->fragments < 0)
+         info->fragments = 0;
+       else if (info->fragments > dmap->nbufs)
+         info->fragments = dmap->nbufs;
+
+       info->fragsize = dmap->fragment_size;
+       info->bytes = info->fragments * dmap->fragment_size;
+
+       if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
+         info->bytes -= dmap->counts[dmap->qhead];
+      }
+      return 0;
+
+    case SNDCTL_DSP_SETTRIGGER:
+      {
+       unsigned long   flags;
+
+       int             bits;
+       int             changed;
+
+       bits = *(int *) arg;
+       bits &= audio_devs[dev]->open_mode;
+
+       if (audio_devs[dev]->d->trigger == NULL)
+         return -EINVAL;
+
+       if (!(audio_devs[dev]->flags & DMA_DUPLEX))
+         if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT))
+           {
+             printk ("Sound: Device doesn't have full duplex capability\n");
+             return -EINVAL;
+           }
+
+       save_flags (flags);
+       cli ();
+       changed = audio_devs[dev]->enable_bits ^ bits;
+
+       if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
+         {
+           int             err;
+
+           reorganize_buffers (dev, dmap_in, 1);
+
+           if ((err = audio_devs[dev]->d->prepare_for_input (dev,
+                              dmap_in->fragment_size, dmap_in->nbufs)) < 0)
+             return -err;
+
+           audio_devs[dev]->enable_bits = bits;
+           DMAbuf_activate_recording (dev, dmap_in);
+         }
+
+
+       if ((changed & bits) & PCM_ENABLE_OUTPUT &&
+        (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
+           audio_devs[dev]->go)
+         {
+
+           if (!(dmap_out->flags & DMA_ALLOC_DONE))
+             {
+               reorganize_buffers (dev, dmap_out, 0);
+             }
+
+           ;
+           dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
+           DMAbuf_launch_output (dev, dmap_out);
+           ;
+         }
+
+       audio_devs[dev]->enable_bits = bits;
+       if (changed && audio_devs[dev]->d->trigger)
+         {
+           audio_devs[dev]->d->trigger (dev, bits * audio_devs[dev]->go);
+         }
+       restore_flags (flags);
+      }
+    case SNDCTL_DSP_GETTRIGGER:
+      return (*(int *) arg = audio_devs[dev]->enable_bits);
+      break;
+
+    case SNDCTL_DSP_SETSYNCRO:
+
+      if (!audio_devs[dev]->d->trigger)
+       return -EINVAL;
+
+      audio_devs[dev]->d->trigger (dev, 0);
+      audio_devs[dev]->go = 0;
+      return 0;
+      break;
+
+    case SNDCTL_DSP_GETIPTR:
+      {
+       count_info      info;
+       unsigned long   flags;
+
+       if (!(audio_devs[dev]->open_mode & OPEN_READ))
+         return -EINVAL;
+
+       save_flags (flags);
+       cli ();
+       info.bytes = audio_devs[dev]->dmap_in->byte_counter;
+       info.ptr = DMAbuf_get_buffer_pointer (dev, audio_devs[dev]->dmap_in) & ~3;
+       info.blocks = audio_devs[dev]->dmap_in->qlen;
+       info.bytes += info.ptr;
+       memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
+
+       if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
+         audio_devs[dev]->dmap_in->qlen = 0;   /* Reset interrupt counter */
+       restore_flags (flags);
+       return 0;
+      }
+      break;
+
+    case SNDCTL_DSP_GETOPTR:
+      {
+       count_info      info;
+       unsigned long   flags;
+
+       if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+         return -EINVAL;
+
+       save_flags (flags);
+       cli ();
+       info.bytes = audio_devs[dev]->dmap_out->byte_counter;
+       info.ptr = DMAbuf_get_buffer_pointer (dev, audio_devs[dev]->dmap_out) & ~3;
+       info.blocks = audio_devs[dev]->dmap_out->qlen;
+       info.bytes += info.ptr;
+       memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
+
+       if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
+         audio_devs[dev]->dmap_out->qlen = 0;  /* Reset interrupt counter */
+
+       restore_flags (flags);
+       return 0;
+      }
+      break;
+
+
+    case SNDCTL_DSP_POST:
+      ;
+      if (audio_devs[dev]->dmap_out->qlen > 0)
+       if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
+         DMAbuf_launch_output (dev, audio_devs[dev]->dmap_out);
+      ;
+      return 0;
+      break;
+
+    case SNDCTL_DSP_GETBLKSIZE:
+      if (!(dmap_out->flags & DMA_ALLOC_DONE))
+       {
+         if (audio_devs[dev]->open_mode & OPEN_WRITE)
+           reorganize_buffers (dev, dmap_out,
+                               (audio_devs[dev]->open_mode == OPEN_READ));
+         if (audio_devs[dev]->flags & DMA_DUPLEX &&
+             audio_devs[dev]->open_mode & OPEN_READ)
+           reorganize_buffers (dev, dmap_in,
+                               (audio_devs[dev]->open_mode == OPEN_READ));
+       }
+
+      return (*(int *) arg = dmap_out->fragment_size);
+      break;
+
+    case SNDCTL_DSP_SETFRAGMENT:
+      {
+       int             fact;
+       int             ret;
+
+       fact = *(int *) arg;
+       ret = dma_set_fragment (dev, dmap_out, arg, fact);
+       if (ret < 0)
+         return ret;
+
+       if (audio_devs[dev]->flags & DMA_DUPLEX &&
+           audio_devs[dev]->open_mode & OPEN_READ)
+         ret = dma_set_fragment (dev, dmap_in, arg, fact);
+
+       return ret;
+      }
+      break;
+
+    default:
+      return audio_devs[dev]->d->ioctl (dev, cmd, arg);
+    }
+
+}
index 765f948bc9a10ff8effcaf262983d96e88c3790a..f5d2a834437ea9569fb08c0b52509dbf0fa726a9 100644 (file)
@@ -1,14 +1,14 @@
 /*
  *     PnP soundcard support is not included in this version.
  *
- *     There is a separately distributed patch available for AEDSP16.
+ *       AESDP16 driver is now included in the lowlevel directory.
  */
-#define DISABLED_OPTIONS       (B(OPT_SPNP)|B(OPT_AEDSP16)|B(OPT_UNUSED1)|B(OPT_UNUSED2)|B(OPT_UNUSED3)|B(OPT_UNUSED4)|B(OPT_UNUSED5))
+#define DISABLED_OPTIONS       (B(OPT_SPNP)|B(OPT_AEDSP16)|B(OPT_UNUSED1)|B(OPT_UNUSED2)|B(OPT_UNUSED3)|B(OPT_UNUSED4)|B(OPT_UNUSED5)|B(OPT_UART6850))
 /*
  * sound/configure.c  - Configuration program for the Linux Sound Driver
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -75,7 +75,7 @@
                     B(OPT_SSCAPE)|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_CS4232)|\
                     B(OPT_SPNP))
 #define SBDSP_DEVS (B(OPT_SB)|B(OPT_SPNP)|B(OPT_MAD16)|B(OPT_TRIX))
-#define SEQUENCER_DEVS (MIDI_CARDS|B(OPT_YM3812)|B(OPT_ADLIB)|B(OPT_GUS)|B(OPT_MAUI))
+#define SEQUENCER_DEVS 0x7fffffff
 /*
  * Options that have been disabled for some reason (incompletely implemented
  * and/or tested). Don't remove from this list before looking at file
@@ -145,7 +145,7 @@ hw_entry        hw_table[] =
 char           *questions[] =
 {
   "ProAudioSpectrum 16 support",
-  "Sound Blaster (SB, SBPro, SB16, clones) support",
+  "_TRUE_ Sound Blaster (SB, SBPro, SB16/32/64, ESS, Jazz16) support",
   "Generic OPL2/OPL3 FM synthesizer support",
   "Gravis Ultrasound support",
   "MPU-401 support (NOT for SB16)",
@@ -991,6 +991,12 @@ ask_parameters (void)
                  3,
                  "0, 1 or 3");
 
+  ask_int_choice (B (OPT_MSS), "MSS_DMA2",
+                 "MSS/WSS second DMA (if possible)",
+                 FMT_INT,
+                 -1,
+                 "0, 1 or 3");
+
   ask_int_choice (B (OPT_SSCAPE), "SSCAPE_BASE",
                  "SoundScape MIDI I/O base",
                  FMT_HEX,
@@ -1037,7 +1043,7 @@ ask_parameters (void)
     }
 
   if (dump_only)
-    show_comment (B (OPT_MAUI),
+    show_comment (B (OPT_TRIX),
     "ERROR! You have to use old sound configuration method with AudioTrix.");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_BASE",
index 2e7e9acc643cc0f2f99bb28ce8710d6e36d29835..1946cd427dd90ee2f03f79bc54af06489ad21ec7 100644 (file)
@@ -7,9 +7,14 @@
  * interfaces. This is just a temporary driver until full PnP support
  * gets implemented. Just the WSS codec, FM synth and the MIDI ports are
  * supported. Other interfaces are left uninitialized.
+ *
+ * Supported chips are:
+ *      CS4232
+ *      CS4236
+ *      CS4236B
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -25,8 +30,6 @@
 #define KEY_PORT       0x279   /* Same as LPT1 status port */
 #define CSN_NUM                0x99    /* Just a random number */
 
-static int     *osp;
-
 static void 
 CS_OUT (unsigned char a)
 {
@@ -48,7 +51,7 @@ probe_cs4232_mpu (struct address_info *hw_config)
   mpu_base = hw_config->io_base;
   mpu_irq = hw_config->irq;
 
-  return 0;
+  return 1;
 }
 
 void
@@ -181,7 +184,7 @@ probe_cs4232 (struct address_info *hw_config)
  * Initialize logical device 3 (MPU)
  */
 
-#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
       if (mpu_base != 0 && mpu_irq != 0)
        {
          CS_OUT2 (0x15, 0x03); /* Select logical device 3 (MPU) */
@@ -253,7 +256,7 @@ attach_cs4232 (struct address_info *hw_config)
   if (dma2 == -1)
     dma2 = dma1;
 
-  ad1848_init ("CS4232", base,
+  ad1848_init ("Crystal audio controller", base,
               irq,
               dma1,            /* Playback DMA */
               dma2,            /* Capture DMA */
@@ -267,7 +270,7 @@ attach_cs4232 (struct address_info *hw_config)
       AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH);   /* FM synth */
     }
 
-#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
   if (mpu_base != 0 && mpu_irq != 0)
     {
       static struct address_info hw_config2 =
@@ -283,10 +286,10 @@ attach_cs4232 (struct address_info *hw_config)
       hw_config2.driver_use_2 = 0;
       hw_config2.card_subtype = 0;
 
-      if (probe_mpu401 (&hw_config2))
+      if (probe_uart401 (&hw_config2))
        {
          mpu_detected = 1;
-         attach_mpu401 (&hw_config2);
+         attach_uart401 (&hw_config2);
        }
       else
        {
@@ -311,7 +314,7 @@ unload_cs4232 (struct address_info *hw_config)
                 dma2,          /* Capture DMA */
                 0);
 
-#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
   if (mpu_base != 0 && mpu_irq != 0 && mpu_detected)
     {
       static struct address_info hw_config2 =
@@ -327,7 +330,7 @@ unload_cs4232 (struct address_info *hw_config)
       hw_config2.driver_use_2 = 0;
       hw_config2.card_subtype = 0;
 
-      unload_mpu401 (&hw_config2);
+      unload_uart401 (&hw_config2);
     }
 #endif
 }
index 080ec3641e8ab275f8a25bea15e02d1be123931a..7f24da62aa173f0fc71a292ecd4e788d3b97b6a6 100644 (file)
@@ -4,7 +4,7 @@
  * Device call tables.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -131,6 +131,7 @@ sndtable_init (void)
   start_cards ();
 }
 
+
 void
 sound_unload_drivers (void)
 {
@@ -143,14 +144,19 @@ sound_unload_drivers (void)
   if (trace_init)
     printk ("Sound unload started\n");
 
+
   for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
     if (snd_installed_cards[i].enabled)
-      if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1)
-       if (sound_drivers[drv].unload)
+      {
+       if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1)
          {
-           sound_drivers[drv].unload (&snd_installed_cards[i].config);
-           snd_installed_cards[i].enabled = 0;
+           if (sound_drivers[drv].unload)
+             {
+               sound_drivers[drv].unload (&snd_installed_cards[i].config);
+               snd_installed_cards[i].enabled = 0;
+             }
          }
+      }
 
   if (trace_init)
     printk ("Sound unload complete\n");
@@ -163,6 +169,7 @@ sound_unload_driver (int type)
 
   unsigned long   flags;
 
+
   DEB (printk ("unload driver %d: ", type));
 
   for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
@@ -450,11 +457,13 @@ sound_install_audiodrv (int vers,
 
 
   d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct audio_driver)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct audio_driver);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
 
   op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct audio_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct audio_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -480,8 +489,6 @@ sound_install_audiodrv (int vers,
   op->flags = flags;
   op->format_mask = format_mask;
   op->devc = devc;
-  op->dmachan1 = dma1;
-  op->dmachan2 = dma2;
 
 /*
  *    Hardcoded defaults
@@ -491,6 +498,11 @@ sound_install_audiodrv (int vers,
   audio_devs[num_audiodevs] = op;
   num = num_audiodevs++;
 
+  DMAbuf_init ();
+
+  op->dmap_out->dma = dma1;
+  op->dmap_in->dma = dma2;
+
   DMAbuf_init ();
   audio_init_devices ();
   return num;
@@ -524,6 +536,7 @@ sound_install_mixer (int vers,
 
 
   op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct mixer_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct mixer_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
index c6eb980d0e79b0b418dc6d8abd4d9e84a7df8e45..cef6dc113f251b7d080c0ed163b9bb6b99574492 100644 (file)
@@ -4,7 +4,7 @@
  *     Global definitions for device call tables
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -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)
@@ -84,7 +83,7 @@ struct dma_buffparms {
 #define DMA_EMPTY      0x00000010      
 #define DMA_ALLOC_DONE 0x00000020
 #define DMA_SYNCING    0x00000040
-#define DMA_CLEAN      0x00000080
+#define DMA_DIRTY      0x00000080
 #define DMA_POST       0x00000100
 
        int      open_mode;
@@ -102,20 +101,25 @@ struct dma_buffparms {
        int      subdivision;
 
        int      fragment_size;
+        int     needs_reorg;
        int      max_fragments;
 
        int      bytes_in_use;
 
        int      underrun_count;
-       int      byte_counter;
+       unsigned long    byte_counter;
+       unsigned long    user_counter;
        int      data_rate; /* Bytes/second */
 
        int      mapping_flags;
 #define                        DMA_MAP_MAPPED          0x00000001
        char    neutral_byte;
+       int     dma;            /* DMA channel */
+
 #ifdef OS_DMA_PARMS
        OS_DMA_PARMS
 #endif
+       int     applic_profile; /* Application profile (APF_*) */
 };
 
 /*
@@ -136,14 +140,13 @@ struct audio_driver {
        int (*open) (int dev, int mode);
        void (*close) (int dev);
        void (*output_block) (int dev, unsigned long buf, 
-                             int count, int intrflag, int dma_restart);
+                             int count, int intrflag);
        void (*start_input) (int dev, unsigned long buf, 
-                            int count, int intrflag, int dma_restart);
-       int (*ioctl) (int dev, unsigned int cmd, caddr_t arg, int local);
+                            int count, int intrflag);
+       int (*ioctl) (int dev, unsigned int cmd, caddr_t arg);
        int (*prepare_for_input) (int dev, int bufsize, int nbufs);
        int (*prepare_for_output) (int dev, int bufsize, int nbufs);
-       void (*reset) (int dev);
-       void (*halt_xfer) (int dev);
+       void (*halt_io) (int dev);
        int (*local_qlen)(int dev);
         void (*copy_user)(int dev, char *localbuf, int localoffs,
                                const char *userbuf, int useroffs, int len);
@@ -156,7 +159,7 @@ struct audio_driver {
 };
 
 struct audio_operations {
-        char name[64];
+        char name[128];
        int flags;
 #define NOTHING_SPECIAL        0x00
 #define NEEDS_RESTART          0x01
@@ -165,11 +168,12 @@ struct audio_operations {
 #define DMA_PSEUDO_AUTOMODE    0x08
 #define DMA_HARDSTOP           0x10
 #define DMA_NODMA              0x20
+#define DMA_EXACT              0x40
        int  format_mask;       /* Bitmask for supported audio formats */
        void *devc;             /* Driver specific info */
        struct audio_driver *d;
+       void *portc;            /* Driver spesific info */
        long buffsize;
-       int dmachan1, dmachan2;
        struct dma_buffparms *dmap_in, *dmap_out;
        struct coproc_operations *coproc;
        int mixer_dev;
@@ -177,6 +181,7 @@ struct audio_operations {
        int open_mode;
        int go;
        int min_fragment;       /* 0 == unlimited */
+       int parent_dev;         /* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
 };
 
 int *load_mixer_volumes(char *name, int *levels, int present);
@@ -191,6 +196,7 @@ struct mixer_operations {
 };
 
 struct synth_operations {
+       char *id;       /* Unique identifier (ASCII) max 29 char */
        struct synth_info *info;
        int midi_dev;
        int synth_type;
@@ -217,6 +223,12 @@ struct synth_operations {
 
        struct voice_alloc_info alloc;
        struct channel_info chn_info[16];
+       int emulation;
+#define        EMU_GM                  1       /* General MIDI */
+#define        EMU_XG                  2       /* Yamaha XG */
+#define MAX_SYSEX_BUF  64
+       unsigned char sysex_buf[MAX_SYSEX_BUF];
+       int sysex_ptr;
 };
 
 struct midi_input_info { /* MIDI input scanner variables */
@@ -255,6 +267,7 @@ struct midi_operations {
 
 struct sound_lowlev_timer {
                int dev;
+               int priority;
                unsigned int (*tmr_start)(int dev, unsigned int usecs);
                void (*tmr_disable)(int dev);
                void (*tmr_restart)(int dev);
@@ -279,7 +292,7 @@ struct sound_timer_operations {
        struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0;
        struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
 
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
        extern struct sound_timer_operations default_sound_timer;
        struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = 
                {&default_sound_timer, NULL}; 
@@ -369,8 +382,6 @@ struct sound_timer_operations {
 
        int num_sound_drivers =
            sizeof(sound_drivers) / sizeof (struct driver_info);
-       int max_sound_drivers =
-           sizeof(sound_drivers) / sizeof (struct driver_info);
 
 
 #ifndef FULL_SOUND
@@ -429,12 +440,12 @@ struct sound_timer_operations {
 
 #ifdef CONFIG_MSS
 #      ifdef DESKPROXL
-               {SNDCARD_DESKPROXL, {MSS_BASE, MSS_IRQ, MSS_DMA, -1}, SND_DEFAULT_ENABLE},
+               {SNDCARD_DESKPROXL, {MSS_BASE, MSS_IRQ, MSS_DMA, MSS_DMA2}, SND_DEFAULT_ENABLE},
 #      else
-               {SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA, -1}, SND_DEFAULT_ENABLE},
+               {SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA, MSS_DMA2}, SND_DEFAULT_ENABLE},
 #      endif
 #      ifdef MSS2_BASE
-               {SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA, -1}, SND_DEFAULT_ENABLE},
+               {SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA, MSS2_DMA2}, SND_DEFAULT_ENABLE},
 #      endif
 #endif
 
@@ -497,23 +508,20 @@ struct sound_timer_operations {
 
        int num_sound_cards =
            sizeof(snd_installed_cards) / sizeof (struct card_info);
-       int max_sound_cards =
+       static int max_sound_cards =
            sizeof(snd_installed_cards) / sizeof (struct card_info);
 
 #else
        int num_sound_cards = 0;
        struct card_info snd_installed_cards[20] = {{0}};
-       int max_sound_cards = 20;
+       static int max_sound_cards = 20;
 #endif
 
 #if defined(MODULE) || (!defined(linux) && !defined(_AIX))
        int trace_init = 0;
-#else
+#   else
        int trace_init = 1;
-#endif
-#ifdef MODULE_PARM
-MODULE_PARM(trace_init, "i");
-#endif
+#   endif
 
 #else
        extern struct audio_operations * audio_devs[MAX_AUDIO_DEV]; extern int num_audiodevs;
@@ -524,10 +532,8 @@ MODULE_PARM(trace_init, "i");
 
        extern struct driver_info sound_drivers[];
        extern int num_sound_drivers;
-       extern int max_sound_drivers;
        extern struct card_info snd_installed_cards[];
        extern int num_sound_cards;
-       extern int max_sound_cards;
 
        extern int trace_init;
 #endif /* _DEV_TABLE_C_ */
index 3fcc7b08cfb57f7e53e551a5d6592250bb2131b0..6be1cfe643a8f3290c4ad59f20149c47683dcc0b 100644 (file)
@@ -4,14 +4,15 @@
  * The DMA buffer manager for digitized voice applications
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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 <linux/poll.h>
+
+#undef  BE_CONSERVATIVE
 
 #include "sound_config.h"
 
@@ -28,9 +29,6 @@ static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] =
 {
   {0}};
 
-#define NEUTRAL8       0x80
-#define NEUTRAL16      0x00
-
 static int      ndmaps = 0;
 
 #define MAX_DMAP (MAX_AUDIO_DEV*2)
@@ -39,130 +37,9 @@ static struct dma_buffparms dmaps[MAX_DMAP] =
 {
   {0}};
 
-static int      space_in_queue (int dev);
-
 static void     dma_reset_output (int dev);
 static void     dma_reset_input (int dev);
-static int      dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact);
-
-static void
-reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
-{
-  /*
-   * This routine breaks the physical device buffers to logical ones.
-   */
-
-  struct audio_operations *dsp_dev = audio_devs[dev];
-
-  unsigned        i, n;
-  unsigned        sr, nc, sz, bsz;
-
-  sr = dsp_dev->d->set_speed (dev, 0);
-  nc = dsp_dev->d->set_channels (dev, 0);
-  sz = dsp_dev->d->set_bits (dev, 0);
-
-  if (sz == 8)
-    dmap->neutral_byte = NEUTRAL8;
-  else
-    dmap->neutral_byte = NEUTRAL16;
-
-  if (sr < 1 || nc < 1 || sz < 1)
-    {
-      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;
-    }
-
-  sz = sr * nc * sz;
-
-  sz /= 8;                     /* #bits -> #bytes */
-  dmap->data_rate = sz;
-
-  if (dmap->fragment_size == 0)
-    {                          /* Compute the fragment size using the default algorithm */
-
-      /*
-         * Compute a buffer size for time not exceeding 1 second.
-         * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
-         * of sound (using the current speed, sample size and #channels).
-       */
-
-      bsz = dsp_dev->buffsize;
-      while (bsz > sz)
-       bsz /= 2;
-
-      if (bsz == dsp_dev->buffsize)
-       bsz /= 2;               /* Needs at least 2 buffers */
-
-/*
- *    Split the computed fragment to smaller parts. After 3.5a9
- *      the default subdivision is 4 which should give better
- *      results when recording.
- */
-
-      if (dmap->subdivision == 0)      /* Not already set */
-       {
-         dmap->subdivision = 4;        /* Init to the default value */
-
-         if ((bsz / dmap->subdivision) > 4096)
-           dmap->subdivision *= 2;
-         if ((bsz / dmap->subdivision) < 4096)
-           dmap->subdivision = 1;
-       }
-
-      bsz /= dmap->subdivision;
-
-      if (bsz < 16)
-       bsz = 16;               /* Just a sanity check */
-
-      dmap->fragment_size = bsz;
-    }
-  else
-    {
-      /*
-         * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
-         * the buffer size computation has already been done.
-       */
-      if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2))
-       dmap->fragment_size = (audio_devs[dev]->buffsize / 2);
-      bsz = dmap->fragment_size;
-    }
-
-  bsz &= ~0x03;                        /* Force size which is multiple of 4 bytes */
-#ifdef OS_DMA_ALIGN_CHECK
-  OS_DMA_ALIGN_CHECK (bsz);
-#endif
-
-  n = dsp_dev->buffsize / bsz;
-  if (n > MAX_SUB_BUFFERS)
-    n = MAX_SUB_BUFFERS;
-  if (n > dmap->max_fragments)
-    n = dmap->max_fragments;
-
-  if (n < 2)
-    {
-      n = 2;
-      bsz /= 2;
-    }
-
-  dmap->nbufs = n;
-  dmap->bytes_in_use = n * bsz;
-  dmap->fragment_size = bsz;
-
-  if (dmap->raw_buf)
-    memset (dmap->raw_buf,
-           dmap->neutral_byte,
-           dmap->bytes_in_use);
-
-  for (i = 0; i < dmap->nbufs; i++)
-    {
-      dmap->counts[i] = 0;
-    }
-
-  dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
-}
+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)
@@ -176,17 +53,18 @@ dma_init_buffers (int dev, struct dma_buffparms *dmap)
       in_sleep_flag[dev].opts = WK_NONE;
     }
 
-  dmap->flags = DMA_BUSY;      /* Other flags off */
-  dmap->qlen = dmap->qhead = dmap->qtail = 0;
-  dmap->nbufs = 1;
+  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+  dmap->byte_counter = 0;
   dmap->bytes_in_use = audio_devs[dev]->buffsize;
 
   dmap->dma_mode = DMODE_NONE;
   dmap->mapping_flags = 0;
-  dmap->neutral_byte = NEUTRAL8;
+  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
@@ -219,6 +97,8 @@ open_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan)
   dmap->fragment_size = 0;
   dmap->max_fragments = 65536; /* Just a large value */
   dmap->byte_counter = 0;
+  dmap->applic_profile = APF_NORMAL;
+  dmap->needs_reorg = 1;
 
 
   dma_init_buffers (dev, dmap);
@@ -235,20 +115,20 @@ close_dmap (int dev, struct dma_buffparms *dmap, int chan)
     dmap->dma_mode = DMODE_NONE;
   dmap->flags &= ~DMA_BUSY;
 
-  disable_dma (chan);
-  sound_free_dmap (dev, dmap, chan);
+  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, 1);
+  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, 1);
+  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) & speed);
 }
 
 static short
@@ -256,7 +136,7 @@ default_set_channels (int dev, short channels)
 {
   int             c = channels;
 
-  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) c, 1);
+  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) & c);
 }
 
 static void
@@ -279,50 +159,66 @@ DMAbuf_open (int dev, int mode)
 
   if (dev >= num_audiodevs)
     {
-      /*  printk ("PCM device %d not installed.\n", dev); */
       return -ENXIO;
     }
 
   if (!audio_devs[dev])
     {
-      /* printk ("PCM device %d not initialized\n", dev); */
       return -ENXIO;
     }
 
   if (!(audio_devs[dev]->flags & DMA_DUPLEX))
     {
       audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out;
-      audio_devs[dev]->dmachan2 = audio_devs[dev]->dmachan1;
+      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;
 
-  check_driver (audio_devs[dev]->d);
-
   dmap_out = audio_devs[dev]->dmap_out;
   dmap_in = audio_devs[dev]->dmap_in;
 
-  if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmachan1)) < 0)
+  if (dmap_in == dmap_out)
+    audio_devs[dev]->flags &= ~DMA_DUPLEX;
+
+  if (mode & OPEN_WRITE)
     {
-      audio_devs[dev]->d->close (dev);
-      return retval;
+      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 &&
-      audio_devs[dev]->flags & DMA_DUPLEX &&
-      dmap_out != dmap_in)
-    if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmachan2)) < 0)
-      {
-       audio_devs[dev]->d->close (dev);
-       close_dmap (dev, dmap_out, audio_devs[dev]->dmachan1);
-       return retval;
-      }
+
+  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;
-  in_sleep_flag[dev].opts = WK_NONE;
-  out_sleep_flag[dev].opts = WK_NONE;
+
+  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);
@@ -334,17 +230,10 @@ DMAbuf_open (int dev, int mode)
 void
 DMAbuf_reset (int dev)
 {
-  unsigned long   flags;
-
-  save_flags (flags);
-  cli ();
-  audio_devs[dev]->d->reset (dev);
-  restore_flags (flags);
-
-  dma_reset_output (dev);
+  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+    dma_reset_output (dev);
 
-  if (audio_devs[dev]->flags & DMA_DUPLEX &&
-      audio_devs[dev]->open_mode & OPEN_READ)
+  if (audio_devs[dev]->open_mode & OPEN_READ)
     dma_reset_input (dev);
 }
 
@@ -356,8 +245,6 @@ dma_reset_output (int dev)
 
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-  if (dmap->dma_mode != DMODE_OUTPUT)
-    return;
 
   if (!(dmap->flags & DMA_STARTED))    /* DMA is not active */
     return;
@@ -404,7 +291,6 @@ dma_reset_output (int dev)
       };
     }
   audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
-  audio_devs[dev]->dmap_out->flags |= DMA_RESTART;
 
 /*
  * Finally shut the device off
@@ -412,68 +298,74 @@ dma_reset_output (int dev)
 
   if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
       !audio_devs[dev]->d->halt_output)
-    audio_devs[dev]->d->reset (dev);
+    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);
 
-  dma_init_buffers (dev, audio_devs[dev]->dmap_out);
+  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->reset (dev);
+    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);
 
-  dma_init_buffers (dev, audio_devs[dev]->dmap_in);
+  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+  dmap->byte_counter = 0;
   reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1);
 }
 
-static void
-launch_output (int dev, struct dma_buffparms *dmap)
+void
+DMAbuf_launch_output (int dev, struct dma_buffparms *dmap)
 {
-  dmap->flags |= DMA_ACTIVE;
-
-  if (dmap->dma_mode == DMODE_NONE || dmap->flags & DMA_RESTART)
+  if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE))
     {
-      reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
-      audio_devs[dev]->d->prepare_for_output (dev,
-                                         dmap->fragment_size, dmap->nbufs);
-    }
+      if (!(dmap->flags & DMA_STARTED))
+       {
+         reorganize_buffers (dev, dmap, 0);
 
-  dmap->dma_mode |= DMODE_OUTPUT;
+         if (audio_devs[dev]->d->prepare_for_output (dev,
+                                         dmap->fragment_size, dmap->nbufs))
+           return;
 
-  if (dmap->counts[dmap->qhead] == 0)
-    dmap->counts[dmap->qhead] = dmap->fragment_size;
+         local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+                          DMA_MODE_WRITE);
+       }
+      if (dmap->counts[dmap->qhead] == 0)
+       dmap->counts[dmap->qhead] = dmap->fragment_size;
 
-  audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
-                                   dmap->qhead * dmap->fragment_size,
-                                   dmap->counts[dmap->qhead], 0,
-                                !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
-                                   !(dmap->flags & DMA_STARTED));
-  dmap->flags |= DMA_STARTED;
-  if (audio_devs[dev]->d->trigger)
-    audio_devs[dev]->d->trigger (dev,
+      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;
+  int             tmout, n = 0;
 
   if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
     return 0;
@@ -499,13 +391,14 @@ DMAbuf_sync (int dev)
       ;
       if (dmap->qlen > 0)
        if (!(dmap->flags & DMA_ACTIVE))
-         launch_output (dev, dmap);
+         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)
        {
@@ -534,7 +427,6 @@ DMAbuf_sync (int dev)
            }
        }
       audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
-      audio_devs[dev]->dmap_out->flags |= DMA_RESTART;
       restore_flags (flags);
       /*
        * Some devices such as GUS have huge amount of on board RAM for the
@@ -578,32 +470,39 @@ DMAbuf_release (int dev, int mode)
 {
   unsigned long   flags;
 
-  audio_devs[dev]->dmap_out->closing = 1;
-  audio_devs[dev]->dmap_in->closing = 1;
+  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]->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]->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);
+    {
+      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 ();
 
-  audio_devs[dev]->d->halt_xfer (dev);
+  DMAbuf_reset (dev);
   audio_devs[dev]->d->close (dev);
 
-  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+  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]->flags & DMA_DUPLEX)
-    close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2);
+  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);
@@ -611,21 +510,15 @@ DMAbuf_release (int dev, int mode)
   return 0;
 }
 
-static int
-activate_recording (int dev, struct dma_buffparms *dmap)
+int
+DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap)
 {
-  int             prepare = 0;
+  if (!(audio_devs[dev]->open_mode & OPEN_READ))
+    return 0;
 
   if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT))
     return 0;
 
-  if (dmap->flags & DMA_RESTART)
-    {
-      dma_reset_input (dev);
-      dmap->flags &= ~DMA_RESTART;
-      prepare = 1;
-    }
-
   if (dmap->dma_mode == DMODE_OUTPUT)  /* Direction change */
     {
       DMAbuf_sync (dev);
@@ -633,8 +526,7 @@ activate_recording (int dev, struct dma_buffparms *dmap)
       dmap->dma_mode = DMODE_NONE;
     }
 
-
-  if (prepare || !dmap->dma_mode)
+  if (!dmap->dma_mode)
     {
       int             err;
 
@@ -649,12 +541,14 @@ activate_recording (int dev, struct dma_buffparms *dmap)
 
   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,
-                                !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
-                                      !(dmap->flags & DMA_STARTED));
-      dmap->flags |= DMA_ACTIVE | DMA_STARTED;
+                                      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);
@@ -669,6 +563,11 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
   int             err = EIO;
   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)
@@ -687,7 +586,7 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
          return -EAGAIN;
        }
 
-      if ((err = activate_recording (dev, dmap)) < 0)
+      if ((err = DMAbuf_activate_recording (dev, dmap)) < 0)
        {
          restore_flags (flags);
          return err;
@@ -737,7 +636,7 @@ DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
        {
          printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
          err = EIO;
-         audio_devs[dev]->d->reset (dev);
+         dma_reset_input (dev);
          ;
        }
       else
@@ -781,83 +680,8 @@ DMAbuf_rmchars (int dev, int buff_no, int c)
   return 0;
 }
 
-static int
-dma_subdivide (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
-{
-  if (fact == 0)
-    {
-      fact = dmap->subdivision;
-      if (fact == 0)
-       fact = 1;
-      return ioctl_out (arg, fact);
-    }
-
-  if (dmap->subdivision != 0 ||
-      dmap->fragment_size)     /* Too late to change */
-    return -EINVAL;
-
-  if (fact > MAX_REALTIME_FACTOR)
-    return -EINVAL;
-
-  if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
-    return -EINVAL;
-
-  dmap->subdivision = fact;
-  return ioctl_out (arg, fact);
-}
-
-static int
-dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
-{
-  int             bytes, count;
-
-  if (fact == 0)
-    return -EIO;
-
-  if (dmap->subdivision != 0 ||
-      dmap->fragment_size)     /* Too late to change */
-    return -EINVAL;
-
-  bytes = fact & 0xffff;
-  count = (fact >> 16) & 0x7fff;
-
-  if (count == 0)
-    count = MAX_SUB_BUFFERS;
-
-  if (bytes < 4 || bytes > 17) /* <16 || > 512k */
-    return -EINVAL;
-
-  if (count < 2)
-    return -EINVAL;
-
-  if (audio_devs[dev]->min_fragment > 0)
-    if (bytes < audio_devs[dev]->min_fragment)
-      bytes = audio_devs[dev]->min_fragment;
-
-#ifdef OS_DMA_MINBITS
-  if (bytes < OS_DMA_MINBITS)
-    bytes = OS_DMA_MINBITS;
-#endif
-
-  dmap->fragment_size = (1 << bytes);
-  dmap->max_fragments = count;
-
-  if (dmap->fragment_size > audio_devs[dev]->buffsize)
-    dmap->fragment_size = audio_devs[dev]->buffsize;
-
-  if (dmap->fragment_size == audio_devs[dev]->buffsize &&
-      audio_devs[dev]->flags & DMA_AUTOMODE)
-    dmap->fragment_size /= 2;  /* Needs at least 2 buffers */
-
-  dmap->subdivision = 1;       /* Disable SNDCTL_DSP_SUBDIVIDE */
-  if (arg)
-    return ioctl_out (arg, bytes | (count << 16));
-  else
-    return 0;
-}
-
-static int
-get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap)
+int
+DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap)
 {
 /*
  * Try to approximate the active byte position of the DMA pointer within the
@@ -865,6 +689,7 @@ get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap)
  */
   int             pos;
   unsigned long   flags;
+  int             chan = dmap->dma;
 
   save_flags (flags);
   cli ();
@@ -873,16 +698,26 @@ get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap)
   else
     {
       clear_dma_ff (chan);
-      disable_dma (chan);
+      disable_dma (dmap->dma);
       pos = get_dma_residue (chan);
-      if (chan > 3)            /* Word count */
-       pos *= 2;
-      pos = dmap->bytes_in_use - pos - 1;
+      pos = dmap->bytes_in_use - pos;
+
+      if (dmap->flags & DMODE_OUTPUT)
+       {
+         if (dmap->qhead == 0)
+           pos %= dmap->bytes_in_use;
+       }
+      else
+       {
+         if (dmap->qtail == 0)
+           pos %= dmap->bytes_in_use;
+       }
+
       if (pos < 0)
        pos = 0;
       if (pos > dmap->bytes_in_use)
        pos = dmap->bytes_in_use;
-      enable_dma (chan);
+      enable_dma (dmap->dma);
     }
   restore_flags (flags);
   /* printk ("%04x ", pos); */
@@ -890,353 +725,200 @@ get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap)
   return pos;
 }
 
-
-int
-DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+/*
+ * 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)
 {
-  struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
-  struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
-
-  switch (cmd)
-    {
-    case SNDCTL_DSP_GETBLKSIZE:
-      if (!(dmap_out->flags & DMA_ALLOC_DONE))
-       {
-         reorganize_buffers (dev, dmap_out,
-                             (audio_devs[dev]->open_mode == OPEN_READ));
-         if (audio_devs[dev]->flags & DMA_DUPLEX &&
-             audio_devs[dev]->open_mode & OPEN_READ)
-           reorganize_buffers (dev, dmap_in,
-                               (audio_devs[dev]->open_mode == OPEN_READ));
-       }
-
-      if (local)
-       return dmap_out->fragment_size;
-      else
-       return ioctl_out (arg, dmap_out->fragment_size);
-      break;
-
-    case SNDCTL_DSP_SUBDIVIDE:
+  if (audio_devs[dev]->open_mode != 0)
+    if (!audio_devs[dev]->go)
       {
-       int             fact;
-       int             ret;
-
-       get_user (fact, (int *) arg);
-
-       ret = dma_subdivide (dev, dmap_out, arg, fact);
-       if (ret < 0)
-         return ret;
-
-       if (audio_devs[dev]->flags & DMA_DUPLEX &&
-           audio_devs[dev]->open_mode & OPEN_READ)
-         ret = dma_subdivide (dev, dmap_in, arg, fact);
+       /* OK to start the device */
+       audio_devs[dev]->go = 1;
 
-       return ret;
+       if (audio_devs[dev]->d->trigger)
+         audio_devs[dev]->d->trigger (dev,
+                       audio_devs[dev]->enable_bits * audio_devs[dev]->go);
       }
-      break;
-
-    case SNDCTL_DSP_SETFRAGMENT:
-      {
-       int             fact;
-       int             ret;
-
-       get_user (fact, (int *) arg);
-       ret = dma_set_fragment (dev, dmap_out, arg, fact);
-       if (ret < 0)
-         return ret;
-
-       if (audio_devs[dev]->flags & DMA_DUPLEX &&
-           audio_devs[dev]->open_mode & OPEN_READ)
-         ret = dma_set_fragment (dev, dmap_in, arg, fact);
+}
 
-       return ret;
-      }
-      break;
+void
+DMAbuf_start_devices (unsigned int devmask)
+{
+  int             dev;
 
-    case SNDCTL_DSP_GETISPACE:
-    case SNDCTL_DSP_GETOSPACE:
-      if (!local)
-       return -EINVAL;
-      else
-       {
-         struct dma_buffparms *dmap = dmap_out;
+  for (dev = 0; dev < num_audiodevs; dev++)
+    if (devmask & (1 << dev))
+      DMAbuf_start_device (dev);
+}
 
-         audio_buf_info *info = (audio_buf_info *) arg;
+int
+DMAbuf_space_in_queue (int dev)
+{
+  int             len, max, tmp;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-         if (cmd == SNDCTL_DSP_GETISPACE &&
-             !(audio_devs[dev]->open_mode & OPEN_READ))
-           return -EINVAL;
+  /* Don't allow touching pages too close to the playing ones */
+  int             lim = dmap->nbufs - 1;
 
-         if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
-           dmap = dmap_in;
+  if (lim < 2)
+    lim = 2;
 
-         if (dmap->mapping_flags & DMA_MAP_MAPPED)
-           return -EINVAL;
+  if (dmap->qlen >= lim)       /* No space at all */
+    return 0;
 
-         if (!(dmap->flags & DMA_ALLOC_DONE))
-           reorganize_buffers (dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+  /*
+   * Verify that there are no more pending buffers than the limit
+   * defined by the process.
+   */
 
-         info->fragstotal = dmap->nbufs;
+  max = dmap->max_fragments;
+  len = dmap->qlen;
 
-         if (cmd == SNDCTL_DSP_GETISPACE)
-           info->fragments = dmap->qlen;
-         else
-           {
-             if (!space_in_queue (dev))
-               info->fragments = 0;
-             else
-               {
-                 info->fragments = dmap->nbufs - dmap->qlen;
-                 if (audio_devs[dev]->d->local_qlen)
-                   {
-                     int             tmp = audio_devs[dev]->d->local_qlen (dev);
-
-                     if (tmp && info->fragments)
-                       tmp--;  /*
-                                  * This buffer has been counted twice
+  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
                                 */
-                     info->fragments -= tmp;
-                   }
-               }
-           }
-
-         if (info->fragments < 0)
-           info->fragments = 0;
-         else if (info->fragments > dmap->nbufs)
-           info->fragments = dmap->nbufs;
+      len += tmp;
+    }
 
-         info->fragsize = dmap->fragment_size;
-         info->bytes = info->fragments * dmap->fragment_size;
+  if (len >= max)
+    return 0;
+  return 1;
+}
 
-         if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
-           info->bytes -= dmap->counts[dmap->qhead];
-       }
-      return 0;
+static int
+output_sleep (int dev, int dontblock)
+{
+  int             tmout;
+  int             err = 0;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-    case SNDCTL_DSP_SETTRIGGER:
-      {
-       unsigned long   flags;
+  if (dontblock)
+    {
+      return -EAGAIN;
+    }
 
-       int             bits;
-       int             changed;
+  if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
+    {
+      return -EAGAIN;
+    }
 
-       get_user (bits, (int *) arg);
-       bits &= audio_devs[dev]->open_mode;
+  /*
+   * Wait for free space
+   */
+  if (!audio_devs[dev]->go)
+    tmout = 0;
+  else
+    {
+      tmout =
+       (dmap->fragment_size * HZ) / dmap->data_rate;
 
-       if (audio_devs[dev]->d->trigger == NULL)
-         return -EINVAL;
+      tmout += HZ / 10;                /* Some safety distance */
 
-       if (!(audio_devs[dev]->flags & DMA_DUPLEX))
-         if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT))
-           {
-             printk ("Sound: Device doesn't have full duplex capability\n");
-             return -EINVAL;
-           }
-
-       save_flags (flags);
-       cli ();
-       changed = audio_devs[dev]->enable_bits ^ bits;
-
-       if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
-         {
-           int             err;
-
-           reorganize_buffers (dev, dmap_in, 1);
-
-           if ((err = audio_devs[dev]->d->prepare_for_input (dev,
-                              dmap_in->fragment_size, dmap_in->nbufs)) < 0)
-             return -err;
-
-           audio_devs[dev]->enable_bits = bits;
-           activate_recording (dev, dmap_in);
-         }
-
-
-       if ((changed & bits) & PCM_ENABLE_OUTPUT &&
-           dmap_out->mapping_flags & DMA_MAP_MAPPED &&
-           audio_devs[dev]->go)
-         {
-
-           if (!(dmap_out->flags & DMA_ALLOC_DONE))
-             {
-               reorganize_buffers (dev, dmap_out, 0);
-             }
-
-           ;
-           dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
-           launch_output (dev, dmap_out);
-           ;
-         }
-
-       audio_devs[dev]->enable_bits = bits;
-       if (changed && audio_devs[dev]->d->trigger)
-         {
-           audio_devs[dev]->d->trigger (dev, bits * audio_devs[dev]->go);
-         }
-       restore_flags (flags);
-      }
-    case SNDCTL_DSP_GETTRIGGER:
-      return ioctl_out (arg, audio_devs[dev]->enable_bits);
-      break;
-
-    case SNDCTL_DSP_SETSYNCRO:
-
-      if (!audio_devs[dev]->d->trigger)
-       return -EINVAL;
-
-      audio_devs[dev]->d->trigger (dev, 0);
-      audio_devs[dev]->go = 0;
-      return 0;
-      break;
-
-    case SNDCTL_DSP_GETIPTR:
-      {
-       count_info      info;
-       unsigned long   flags;
-
-       if (!(audio_devs[dev]->open_mode & OPEN_READ))
-         return -EINVAL;
-
-       save_flags (flags);
-       cli ();
-       info.bytes = audio_devs[dev]->dmap_in->byte_counter;
-       info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan2, audio_devs[dev]->dmap_in);
-       info.blocks = audio_devs[dev]->dmap_in->qlen;
-       info.bytes += info.ptr;
-       {
-         char           *fixit = (char *) &info;
+      if (tmout < (HZ / 2))
+       tmout = HZ / 2;
+      if (tmout > 20 * HZ)
+       tmout = 20 * HZ;
+    }
 
-         copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-       };
+  if ((current->signal & ~current->blocked))
+    return -EIO;
 
-       if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
-         audio_devs[dev]->dmap_in->qlen = 0;   /* Acknowledge interrupts */
-       restore_flags (flags);
-       return 0;
-      }
-      break;
 
-    case SNDCTL_DSP_GETOPTR:
+  {
+    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))
       {
-       count_info      info;
-       unsigned long   flags;
-
-       if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-         return -EINVAL;
-
-       save_flags (flags);
-       cli ();
-       info.bytes = audio_devs[dev]->dmap_out->byte_counter;
-       info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan1, audio_devs[dev]->dmap_out);
-       info.blocks = audio_devs[dev]->dmap_out->qlen;
-       info.bytes += info.ptr;
-       {
-         char           *fixit = (char *) &info;
-
-         copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-       };
-
-       if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
-         audio_devs[dev]->dmap_out->qlen = 0;  /* Acknowledge interrupts */
-       restore_flags (flags);
-       return 0;
+       if (jiffies >= tlimit)
+         out_sleep_flag[dev].opts |= WK_TIMEOUT;
       }
-      break;
-
-
-    case SNDCTL_DSP_POST:
-      ;
-      if (audio_devs[dev]->dmap_out->qlen > 0)
-       if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
-         launch_output (dev, audio_devs[dev]->dmap_out);
+    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");
+      err = EIO;
       ;
-      return 0;
-      break;
-
-    default:
-      return audio_devs[dev]->d->ioctl (dev, cmd, arg, local);
+      dma_reset_output (dev);
+    }
+  else if ((current->signal & ~current->blocked))
+    {
+      err = EINTR;
     }
 
-}
-
-/*
- * DMAbuf_start_devices() is called by the /dev/music driver to start
- * one or more audio devices at desired moment.
- */
-
-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);
+  return err;
 }
 
 static int
-space_in_queue (int dev)
+find_output_space (int dev, char **buf, int *size)
 {
-  int             len, max, tmp;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+  unsigned long   flags;
+  unsigned long   offs, active_offs;
+  long            len;
 
-  /* Don't allow touching pages too close to the playing ones */
-  int             lim = dmap->nbufs - 1;
+  if (!DMAbuf_space_in_queue (dev))
+    return 0;
 
-  if (lim < 2)
-    lim = 2;
+  save_flags (flags);
+  cli ();
 
-  if (dmap->qlen >= lim)       /* No space at all */
-    return 0;
+#ifdef BE_CONSERVATIVE
+  active_offs = dmap->byte_counter + (dmap->qhead + 1) * dmap->fragment_size;
+#else
+  active_offs = ((dmap->byte_counter + DMAbuf_get_buffer_pointer (dev, dmap)));
+  /* / dmap->fragment_size) * dmap->fragment_size; */
 
-  /*
-   * Verify that there are no more pending buffers than the limit
-   * defined by the process.
-   */
+#endif
 
-  max = dmap->max_fragments;
-  len = dmap->qlen;
+  offs = (dmap->user_counter % dmap->bytes_in_use) & ~3;
+  *buf = dmap->raw_buf + offs;
 
-  if (audio_devs[dev]->d->local_qlen)
+  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)
     {
-      tmp = audio_devs[dev]->d->local_qlen (dev);
-      if (tmp && len)
-       tmp--;                  /*
-                                * This buffer has been counted twice
-                                */
-      len += tmp;
+      restore_flags (flags);
+      return 0;
     }
 
-  if (len >= max)
-    return 0;
-  return 1;
+  if ((offs + len) > dmap->bytes_in_use)
+    len = dmap->bytes_in_use - offs;
+
+  *size = len & ~3;
+
+  restore_flags (flags);
+
+  return (len > 0);
 }
 
 int
 DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
 {
   unsigned long   flags;
-  int             abort, err = EIO;
+  int             err = EIO;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-  dmap->flags &= ~DMA_CLEAN;
+  if (dmap->needs_reorg)
+    reorganize_buffers (dev, dmap, 0);
 
-  if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
+  if (dmap->mapping_flags & DMA_MAP_MAPPED)
     {
       printk ("Sound: Can't write to mmapped device (3)\n");
       return -EINVAL;
@@ -1247,204 +929,77 @@ DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
       DMAbuf_reset (dev);
       dmap->dma_mode = DMODE_NONE;
     }
-  else if (dmap->flags & DMA_RESTART)  /* Restart buffering */
-    {
-      DMAbuf_sync (dev);
-      dma_reset_output (dev);
-      dmap->dma_mode = DMODE_NONE;
-    }
-
-  dmap->flags &= ~(DMA_RESTART | DMA_EMPTY);
-
-  if (!dmap->dma_mode)
-    {
-      int             err;
 
-      reorganize_buffers (dev, dmap, 0);
-      dmap->dma_mode = DMODE_OUTPUT;
-      if ((err = audio_devs[dev]->d->prepare_for_output (dev,
-                                    dmap->fragment_size, dmap->nbufs)) < 0)
-       return err;
-    }
+  dmap->dma_mode = DMODE_OUTPUT;
 
   save_flags (flags);
   cli ();
 
-  abort = 0;
-  while (!space_in_queue (dev) &&
-        !abort)
+  while (!find_output_space (dev, buf, size))
     {
-      int             tmout;
-
-      if (dontblock)
+      if ((err = output_sleep (dev, dontblock)) < 0)
        {
          restore_flags (flags);
-         return -EAGAIN;
-       }
-
-      if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
-       {
-         restore_flags (flags);
-         return -EAGAIN;
-       }
-
-      /*
-       * Wait for free space
-       */
-      if (!audio_devs[dev]->go)
-       tmout = 0;
-      else
-       {
-         tmout =
-           (dmap->fragment_size * HZ) / dmap->data_rate;
-
-         tmout += HZ / 10;     /* 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;
-       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");
-         err = EIO;
-         abort = 1;
-         ;
-         if (audio_devs[dev]->flags & DMA_AUTOMODE)
-           dmap->flags |= DMA_RESTART;
-         else
-           dmap->flags &= ~DMA_RESTART;
-         audio_devs[dev]->d->reset (dev);
-       }
-      else if ((current->signal & ~current->blocked))
-       {
-         err = EINTR;
-         abort = 1;
+         return err;
        }
     }
-  restore_flags (flags);
 
-  if (!space_in_queue (dev))
+  if (!find_output_space (dev, buf, size))
     {
-      return -err;             /* Caught a signal ? */
+      restore_flags (flags);
+      return -EIO;             /* Caught a signal ? */
     }
+  restore_flags (flags);
 
-  *buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size;
-  *size = dmap->fragment_size;
-  dmap->counts[dmap->qtail] = 0;
-
-  return dmap->qtail;
-}
-
-int
-DMAbuf_get_curr_buffer (int dev, int *buf_no, char **dma_buf, int *buf_ptr, int *buf_size)
-{
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-  if (dmap->cfrag < 0)
-    return -1;
-
-  *dma_buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size;
-  *buf_ptr = dmap->counts[dmap->qtail];
-  *buf_size = dmap->fragment_size;
-  return *buf_no = dmap->cfrag;
-}
-
-int
-DMAbuf_set_count (int dev, int buff_no, int l)
-{
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-  if (buff_no == dmap->qtail)
-    {
-      dmap->cfrag = buff_no;
-      dmap->counts[buff_no] = l;
-    }
-  else
-    dmap->cfrag = -1;
+  dmap->flags |= DMA_DIRTY;
   return 0;
 }
 
 int
-DMAbuf_start_output (int dev, int buff_no, int l)
+DMAbuf_move_wrpointer (int dev, int l)
 {
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-  int             restart = 0;
-  int             post = dmap->flags & DMA_POST;
+  unsigned long   ptr = (dmap->user_counter / dmap->fragment_size)
+  * dmap->fragment_size;
+
+  unsigned long   end_ptr, p, prev_count;
+  int             post = (dmap->flags & DMA_POST);
 
   ;
 
   dmap->flags &= ~DMA_POST;
 
   dmap->cfrag = -1;
-  if (dmap->flags & DMA_RESTART)
-    restart = 1;
 
-/*
- * Bypass buffering if using mmapped access
- */
+  prev_count = dmap->user_counter;
+  dmap->user_counter += l;
 
-  if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
-    {
-      l = dmap->fragment_size;
-      dmap->counts[dmap->qtail] = l;
-      dmap->flags &= ~DMA_RESTART;
-      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+  if (dmap->user_counter < prev_count) /* Wrap? */
+    {                          /* Wrap the device counter too */
+      dmap->byte_counter %= dmap->bytes_in_use;
     }
-  else
-    {
 
-      dmap->qlen++;
+  end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
 
-      dmap->counts[dmap->qtail] = l;
-      if (l < dmap->fragment_size)
-       {
-         int             p = dmap->fragment_size * dmap->qtail;
-
-#if defined(PPC) || defined(sparc) || defined(HPPA)
-         dmap->neutral_byte = dmap->raw_buf[p + l - 2];
-#else
-         dmap->neutral_byte = dmap->raw_buf[p + l - 1];
-#endif
-
-         memset (dmap->raw_buf + p + l,
-                 dmap->neutral_byte,
-                 dmap->fragment_size - l);
-       }
-      else
-       dmap->neutral_byte =
-         dmap->raw_buf[dmap->fragment_size * dmap->qtail - 1];
+  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;
     }
 
-  /* Delay playback until there are at least two fragments (to prevent hiccup) */
-  if (dmap->qlen > 1 || post ||
-      (dmap->qlen > 0 && dmap->nbufs <= 2) ||
-      (dmap->qlen > 0 && dmap->flags & DMA_SYNCING) ||
-      restart || l != dmap->fragment_size)
-    if (!(dmap->flags & DMA_ACTIVE))
+  dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
+
+  if (!(dmap->flags & DMA_ACTIVE))
+    if (dmap->qlen > 1 ||
+       (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
       {
-       launch_output (dev, dmap);
+       DMAbuf_launch_output (dev, dmap);
       }
 
   ;
@@ -1459,26 +1014,23 @@ DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
 
   if (dma_mode == DMA_MODE_WRITE)
     {
-      chan = audio_devs[dev]->dmachan1;
+      chan = audio_devs[dev]->dmap_out->dma;
       dmap = audio_devs[dev]->dmap_out;
     }
   else
     {
-      chan = audio_devs[dev]->dmachan2;
+      chan = audio_devs[dev]->dmap_in->dma;
       dmap = audio_devs[dev]->dmap_in;
     }
 
   if (dmap->raw_buf == NULL)
     {
-      printk ("sound: DMA buffer == NULL\n");
+      printk ("sound: DMA buffer(1) == NULL\n");
+      printk ("Device %d, chn=%s\n", dev,
+             (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
       return 0;
     }
 
-  /* Handle cards with non automode DMA in new way */
-  if (physaddr != dmap->raw_buf_phys)  /* Not fragment 0 */
-    return count;
-  count = dmap->bytes_in_use;
-
   if (chan < 0)
     return 0;
 
@@ -1487,79 +1039,55 @@ DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
    * set_dma_addr()
    */
 
-  if (audio_devs[dev]->flags & DMA_AUTOMODE)
-    {                          /*
-                                * Auto restart mode. Transfer the whole *
-                                * buffer
-                                */
-      unsigned long   flags;
+  sound_start_dma (dev, dmap, chan, physaddr, count, dma_mode, 0);
 
-      save_flags (flags);
-      cli ();
-      disable_dma (chan);
-      clear_dma_ff (chan);
-      set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
-      set_dma_addr (chan, dmap->raw_buf_phys);
-      set_dma_count (chan, dmap->bytes_in_use);
-      enable_dma (chan);
-      restore_flags (flags);
+  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
     {
-      unsigned long   flags;
-
-      save_flags (flags);
-      cli ();
-      disable_dma (chan);
-      clear_dma_ff (chan);
-      set_dma_mode (chan, dma_mode);
-      set_dma_addr (chan, physaddr);
-      set_dma_count (chan, count);
-      enable_dma (chan);
-      restore_flags (flags);
+      chan = audio_devs[dev]->dmap_in->dma;
+      dmap = audio_devs[dev]->dmap_in;
     }
 
-  return count;
-}
-
-void
-DMAbuf_init (void)
-{
-  int             dev;
+  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 (chan < 0)
+    return 0;
 
   /*
-     * NOTE! This routine could be called several times.
+   * The count must be one less than the actual size. This is handled by
+   * set_dma_addr()
    */
 
-  for (dev = 0; dev < num_audiodevs; dev++)
-    if (audio_devs[dev]->dmap_out == NULL)
-      {
-       audio_devs[dev]->dmap_out =
-         audio_devs[dev]->dmap_in =
-         &dmaps[ndmaps++];
+  sound_start_dma (dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1);
+  dmap->flags |= DMA_STARTED;
 
-       if (audio_devs[dev]->flags & DMA_DUPLEX)
-         audio_devs[dev]->dmap_in =
-           &dmaps[ndmaps++];
-      }
+  return count;
 }
 
 static void
-force_restart (int dev, struct dma_buffparms *dmap)
+finish_output_interrupt (int dev, struct dma_buffparms *dmap)
 {
   unsigned long   flags;
 
-  if ((audio_devs[dev]->flags & DMA_DUPLEX) &&
-      audio_devs[dev]->d->halt_output)
-    audio_devs[dev]->d->halt_output (dev);
-  else
-    audio_devs[dev]->d->halt_xfer (dev);
-
-  dmap->flags &= ~(DMA_ACTIVE | DMA_STARTED);
-  dmap->flags |= DMA_RESTART;
-  dmap->qlen = dmap->qhead = dmap->qtail = 0;
-
   save_flags (flags);
   cli ();
   if ((out_sleep_flag[dev].opts & WK_SLEEP))
@@ -1590,111 +1118,98 @@ DMAbuf_outputintr (int dev, int event_type)
   int             this_fragment;
 
 #ifdef OS_DMA_INTR
-  if (audio_devs[dev]->dmachan1 >= 0)
-    sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+  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 after freeing buffers.\n");
+      printk ("Sound: Fatal error. Audio interrupt (%d) after freeing buffers.\n", dev);
       return;
     }
 
   if (dmap->mapping_flags & DMA_MAP_MAPPED)
     {
+      unsigned long   prev_counter = dmap->byte_counter;
+
       /* mmapped access */
       dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
       if (dmap->qhead == 0)    /* Wrapped */
-       dmap->byte_counter += dmap->bytes_in_use;
-      dmap->qlen++;            /* Yes increment it (don't decrement) */
-      dmap->flags &= ~DMA_ACTIVE;
-      dmap->counts[dmap->qhead] = dmap->fragment_size;
+       {
+         dmap->byte_counter += dmap->bytes_in_use;
+         if (dmap->byte_counter < prev_counter)        /* Overflow */
+           {
+             dmap->user_counter %= dmap->bytes_in_use;
+           }
+       }
 
+      dmap->qlen++;            /* Yes increment it (don't decrement) */
       if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
-       {
-         if (dmap->counts[dmap->qhead] == 0)
-           dmap->counts[dmap->qhead] = dmap->fragment_size;
+       dmap->flags &= ~DMA_ACTIVE;
+      dmap->counts[dmap->qhead] = dmap->fragment_size;
 
-         audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
-                                         dmap->qhead * dmap->fragment_size,
-                                           dmap->counts[dmap->qhead], 1,
-                                 !(audio_devs[dev]->flags & DMA_AUTOMODE));
-         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;
+      DMAbuf_launch_output (dev, dmap);
+      finish_output_interrupt (dev, dmap);
+      return;
     }
-  else if (event_type != 2)
+
+  if (event_type == 2)
     {
-      if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
-       {
-         printk ("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n",
-                 dev, dmap->qlen, dmap->nbufs);
-         force_restart (dev, dmap);
-         return;
-       }
+      finish_output_interrupt (dev, dmap);
+      return;
+    }
 
-      save_flags (flags);
-      cli ();
+  if (dmap->qlen > dmap->nbufs)
+    dmap->qlen = dmap->nbufs;
 
-      dmap->qlen--;
-      this_fragment = dmap->qhead;
-      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+  if (dmap->qlen <= 0)
+    {
+      finish_output_interrupt (dev, dmap);
+      return;
+    }
 
-      if (dmap->qhead == 0)    /* Wrapped */
-       dmap->byte_counter += dmap->bytes_in_use;
-      dmap->flags &= ~DMA_ACTIVE;
+  save_flags (flags);
+  cli ();
 
-      if (event_type == 1 && dmap->qlen < 1)
-       {
-         dmap->underrun_count++;
+  dmap->qlen--;
+  this_fragment = dmap->qhead;
+  dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
 
-         dmap->qlen = 0;
-         force_restart (dev, dmap);
-       }
+  if (dmap->qhead == 0)                /* Wrapped */
+    {
+      unsigned long   prev_counter = dmap->byte_counter;
 
-      if (dmap->qlen)
+      dmap->byte_counter += dmap->bytes_in_use;
+      if (dmap->byte_counter < prev_counter)   /* Overflow */
        {
-         if (dmap->flags & DMA_CLEAN)
-           {
-             int             p = dmap->fragment_size * this_fragment;
-
-             memset (dmap->raw_buf + p,
-                     dmap->neutral_byte,
-                     dmap->fragment_size);
-           }
+         dmap->user_counter %= dmap->bytes_in_use;
+       }
+    }
 
-         if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
-           {
+  if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+    dmap->flags &= ~DMA_ACTIVE;
 
-             if (dmap->counts[dmap->qhead] == 0)
-               dmap->counts[dmap->qhead] = dmap->fragment_size;
+  if (event_type == 1 && dmap->qlen < 1)
+    {
+      dmap->underrun_count++;
 
-             audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
-                                         dmap->qhead * dmap->fragment_size,
-                                               dmap->counts[dmap->qhead], 1,
-                                 !(audio_devs[dev]->flags & DMA_AUTOMODE));
-             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;
+      dmap->qlen = 0;
+      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->bytes_in_use);
        }
+      dmap->user_counter += dmap->fragment_size;
+      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+    }
 
-      restore_flags (flags);
-    }                          /* event_type != 2 */
+  if (dmap->qlen > 0)
+    DMAbuf_launch_output (dev, dmap);
 
-  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);
+  finish_output_interrupt (dev, dmap);
 }
 
 void
@@ -1704,8 +1219,8 @@ DMAbuf_inputintr (int dev)
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
 
 #ifdef OS_DMA_INTR
-  if (audio_devs[dev]->dmachan2 >= 0)
-    sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2);
+  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)
@@ -1718,15 +1233,26 @@ DMAbuf_inputintr (int dev)
     {
       dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
       if (dmap->qtail == 0)    /* Wrapped */
-       dmap->byte_counter += dmap->bytes_in_use;
+       {
+         unsigned long   prev_counter = dmap->byte_counter;
+
+         dmap->byte_counter += dmap->bytes_in_use;
+         if (dmap->byte_counter < prev_counter)        /* Overflow */
+           {
+             dmap->user_counter %= dmap->bytes_in_use;
+           }
+       }
       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,
-                                 !(audio_devs[dev]->flags & DMA_AUTOMODE));
+                                          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);
@@ -1734,26 +1260,13 @@ DMAbuf_inputintr (int dev)
 
       dmap->flags |= DMA_ACTIVE;
     }
-  else if (dmap->qlen == (dmap->nbufs - 1))
+  else if (dmap->qlen >= (dmap->nbufs - 1))
     {
       /* printk ("Sound: Recording overrun\n"); */
       dmap->underrun_count++;
 
-      if (audio_devs[dev]->flags & DMA_AUTOMODE)
-       {
-         /* Force restart on next read */
-         if ((audio_devs[dev]->flags & DMA_DUPLEX) &&
-             audio_devs[dev]->d->halt_input)
-           audio_devs[dev]->d->halt_input (dev);
-         else
-           audio_devs[dev]->d->halt_xfer (dev);
-
-         dmap->flags &= ~DMA_ACTIVE;
-         if (audio_devs[dev]->flags & DMA_AUTOMODE)
-           dmap->flags |= DMA_RESTART;
-         else
-           dmap->flags &= ~DMA_RESTART;
-       }
+      /* Just throw away the oldest fragment but keep the engine running */
+      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
     }
   else
     {
@@ -1763,15 +1276,26 @@ DMAbuf_inputintr (int dev)
                dev, dmap->qlen, dmap->nbufs);
       dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
       if (dmap->qtail == 0)    /* Wrapped */
-       dmap->byte_counter += dmap->bytes_in_use;
+       {
+         unsigned long   prev_counter = dmap->byte_counter;
+
+         dmap->byte_counter += dmap->bytes_in_use;
+         if (dmap->byte_counter < prev_counter)        /* Overflow */
+           {
+             dmap->user_counter %= dmap->bytes_in_use;
+           }
+       }
     }
 
   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,
-                                 !(audio_devs[dev]->flags & DMA_AUTOMODE));
+                                      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);
@@ -1798,9 +1322,8 @@ DMAbuf_open_dma (int dev)
  *    NOTE!  This routine opens only the primary DMA channel (output).
  */
 
-  int             chan = audio_devs[dev]->dmachan1;
+  int             chan = audio_devs[dev]->dmap_out->dma;
   int             err;
-  unsigned long   flags;
 
   if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0)
     {
@@ -1812,6 +1335,8 @@ DMAbuf_open_dma (int dev)
 
   if (chan >= 0)
     {
+      unsigned long   flags;
+
       save_flags (flags);
       cli ();
       disable_dma (chan);
@@ -1825,167 +1350,147 @@ DMAbuf_open_dma (int dev)
 void
 DMAbuf_close_dma (int dev)
 {
-  DMAbuf_reset_dma (dev);
-  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
 }
 
 void
-DMAbuf_reset_dma (int dev)
+DMAbuf_init (void)
 {
+  int             dev;
+
+  /*
+     * NOTE! This routine could be called several times.
+   */
+
+  for (dev = 0; dev < num_audiodevs; dev++)
+    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++];
+
+           if (audio_devs[dev]->flags & DMA_DUPLEX)
+             audio_devs[dev]->dmap_in =
+               &dmaps[ndmaps++];
+         }
+      }
 }
 
-unsigned int
-DMAbuf_poll (kdev_t dev, struct fileinfo *file, poll_table * wait)
+int
+DMAbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
 {
-  unsigned int mask = 0;
   struct dma_buffparms *dmap;
   unsigned long   flags;
 
-  save_flags (flags);
-  cli ();
-
-  in_sleep_flag[dev].opts = WK_SLEEP;
-  poll_wait (&in_sleeper[dev], wait);
-  out_sleep_flag[dev].opts = WK_SLEEP;
-  poll_wait (&out_sleeper[dev], wait);
-
-  restore_flags (flags);
-
-/* sel_in */
-  dmap = audio_devs[dev]->dmap_in;
-  if (!(audio_devs[dev]->open_mode))
-    goto sel_out;
-  if (dmap->mapping_flags & DMA_MAP_MAPPED) {
-    if (dmap->qlen)
-      mask |= POLLIN | POLLRDNORM;
-    goto sel_out;
-  }
-  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;
+  switch (sel_type)
+    {
+    case SEL_IN:
+      if (!(audio_devs[dev]->open_mode))
+       return 0;
 
-      save_flags (flags);
-      cli ();
-      activate_recording (dev, dmap);
-      restore_flags (flags);
-    }
-    goto sel_out;
-  }
-  if (!dmap->qlen)
-    goto sel_out;
-  mask |= POLLIN | POLLRDNORM;
+      dmap = audio_devs[dev]->dmap_in;
 
- sel_out:
-  dmap = audio_devs[dev]->dmap_out;
+      if (dmap->mapping_flags & DMA_MAP_MAPPED)
+       {
+         if (dmap->qlen)
+           return 1;
 
-  if (dmap->mapping_flags & DMA_MAP_MAPPED) {
-    if (dmap->qlen)
-      mask |= POLLOUT | POLLWRNORM;
-    goto sel_ex;
-  }
-  if (dmap->dma_mode == DMODE_INPUT)
-    goto sel_ex;
-  if (dmap->dma_mode == DMODE_NONE) {
-    mask |= POLLOUT | POLLWRNORM;
-    goto sel_ex;
-  }
-  if (!space_in_queue (dev))
-    goto sel_ex;
-  mask |= POLLOUT | POLLWRNORM;
+         save_flags (flags);
+         cli ();
 
-sel_ex:
-  return mask;
-}
+         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;
 
-#else /* CONFIG_AUDIO */
-/*
- * Stub versions if audio services not included
- */
+             save_flags (flags);
+             cli ();
+             DMAbuf_activate_recording (dev, dmap);
+             restore_flags (flags);
+           }
+         return 0;
+       }
 
-int
-DMAbuf_open (int dev, int mode)
-{
-  return -ENXIO;
-}
+      if (!dmap->qlen)
+       {
+         save_flags (flags);
+         cli ();
 
-int
-DMAbuf_release (int dev, int mode)
-{
-  return 0;
-}
+         in_sleep_flag[dev].opts = WK_SLEEP;
+         poll_wait (&in_sleeper[dev], wait);
+         restore_flags (flags);
+         return 0;
+       }
+      return 1;
+      break;
 
-int
-DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
-{
-  return -EIO;
-}
+    case SEL_OUT:
+      dmap = audio_devs[dev]->dmap_out;
 
-int
-DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
-{
-  return -EIO;
-}
+      if (dmap->mapping_flags & DMA_MAP_MAPPED)
+       {
+         if (dmap->qlen)
+           return 1;
 
-int
-DMAbuf_rmchars (int dev, int buff_no, int c)
-{
-  return -EIO;
-}
+         save_flags (flags);
+         cli ();
 
-int
-DMAbuf_start_output (int dev, int buff_no, int l)
-{
-  return -EIO;
-}
+         out_sleep_flag[dev].opts = WK_SLEEP;
+         poll_wait (&out_sleeper[dev], wait);
+         restore_flags (flags);
+         return 0;
+       }
 
-int
-DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
-{
-  return -EIO;
-}
+      if (dmap->dma_mode == DMODE_INPUT)
+       {
+         return 0;
+       }
 
-void
-DMAbuf_init (void)
-{
-}
+      if (dmap->dma_mode == DMODE_NONE)
+       {
+         return 1;
+       }
 
-int
-DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
-{
-  return -EIO;
-}
+      if (!DMAbuf_space_in_queue (dev))
+       {
+         save_flags (flags);
+         cli ();
 
-int
-DMAbuf_open_dma (int dev)
-{
-  return -ENXIO;
-}
+         out_sleep_flag[dev].opts = WK_SLEEP;
+         poll_wait (&out_sleeper[dev], wait);
+         restore_flags (flags);
+         return 0;
+       }
+      return 1;
+      break;
 
-void
-DMAbuf_close_dma (int dev)
-{
-  return;
-}
+    case SEL_EX:
+      return 0;
+    }
 
-void
-DMAbuf_reset_dma (int dev)
-{
-  return;
+  return 0;
 }
 
-void
-DMAbuf_inputintr (int dev)
-{
-  return;
-}
 
-void
-DMAbuf_outputintr (int dev, int underrun_flag)
-{
-  return;
-}
 #endif
index fe6d497a7143313b9c33727b78493bfd4ff1bb92..f3253cab97d6850a7a07cd562d6a3f2f8806a8ef 100644 (file)
@@ -1,6 +1,6 @@
 #ifdef SEQUENCER_C
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
index 3c0ac10de9c47dca7d9417dc87e6aa2b4063b2a2..b844e43639e7d8459aa128eaa19a197f6ffcb149 100644 (file)
@@ -4,7 +4,7 @@
  * Detection routine for the Gravis Ultrasound.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
index f09edd6937a0d0a7c880206e23b0c081258f1b2d..61c6440075c9af52d3e0ec67450495020c551907 100644 (file)
@@ -4,7 +4,7 @@
  * The low level driver for the GUS Midi Interface.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
index 9b00b4e2197e06f0e770aea326adb48d1d74b1d0..5eaecc4e31d3a645d42639675c69f530698c9b6b 100644 (file)
@@ -2,7 +2,7 @@
  * gus_vol.c - Compute volume for GUS.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
index a4caf4f8aa2c62a770b38575ec14b68994d8b6a3..5c579b8468be0b7019d5f41710d52fb328aa1332 100644 (file)
@@ -4,7 +4,7 @@
  * Driver for the Gravis UltraSound wave table synth.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -33,6 +33,7 @@ struct voice_info
     unsigned long   orig_freq;
     unsigned long   current_freq;
     unsigned long   mode;
+    int             fixed_pitch;
     int             bender;
     int             bender_range;
     int             panning;
@@ -128,7 +129,7 @@ static int      pcm_current_intrflag;
 
 extern int     *gus_osp;
 
-struct voice_info voices[32];
+static struct voice_info voices[32];
 
 static int      freq_div_table[] =
 {
@@ -261,7 +262,7 @@ gus_write8 (int reg, unsigned int data)
   restore_flags (flags);
 }
 
-unsigned char
+static unsigned char
 gus_read8 (int reg)
 {                              /* Reads from an indirect register (8 bit). Offset 0x80. */
   unsigned long   flags;
@@ -276,7 +277,7 @@ gus_read8 (int reg)
   return val;
 }
 
-unsigned char
+static unsigned char
 gus_look8 (int reg)
 {                              /* Reads from an indirect register (8 bit). No additional offset. */
   unsigned long   flags;
@@ -291,7 +292,7 @@ gus_look8 (int reg)
   return val;
 }
 
-void
+static void
 gus_write16 (int reg, unsigned int data)
 {                              /* Writes to an indirect register (16 bit) */
   unsigned long   flags;
@@ -307,7 +308,7 @@ gus_write16 (int reg, unsigned int data)
   restore_flags (flags);
 }
 
-unsigned short
+static unsigned short
 gus_read16 (int reg)
 {                              /* Reads from an indirect register (16 bit). Offset 0x80. */
   unsigned long   flags;
@@ -326,7 +327,7 @@ gus_read16 (int reg)
   return ((hi << 8) & 0xff00) | lo;
 }
 
-unsigned short
+static unsigned short
 gus_look16 (int reg)
 {                              /* Reads from an indirect register (16 bit). No additional offset. */
   unsigned long   flags;
@@ -345,7 +346,7 @@ gus_look16 (int reg)
   return ((hi << 8) & 0xff00) | lo;
 }
 
-void
+static void
 gus_write_addr (int reg, unsigned long address, int frac, int is16bit)
 {                              /* Writes an 24 bit memory address */
   unsigned long   hold_address;
@@ -541,6 +542,7 @@ gus_voice_init2 (int voice)
   voices[voice].patch_vol = 127;
   voices[voice].expression_vol = 127;
   voices[voice].sample_pending = -1;
+  voices[voice].fixed_pitch = 0;
 }
 
 static void
@@ -1107,11 +1109,7 @@ guswave_ioctl (int dev,
     {
     case SNDCTL_SYNTH_INFO:
       gus_info.nr_voices = nr_voices;
-      {
-       char           *fixit = (char *) &gus_info;
-
-       copy_to_user (&((char *) arg)[0], fixit, sizeof (gus_info));
-      };
+      memcpy ((&((char *) arg)[0]), (char *) &gus_info, sizeof (gus_info));
       return 0;
       break;
 
@@ -1362,7 +1360,7 @@ guswave_controller (int dev, int voice, int ctrl_num, int value)
       if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
        {
          freq = compute_finetune (voices[voice].orig_freq, value,
-                                  voices[voice].bender_range);
+                                  voices[voice].bender_range, 0);
          voices[voice].current_freq = freq;
 
          save_flags (flags);
@@ -1494,10 +1492,17 @@ guswave_start_note2 (int dev, int voice, int note_num, int volume)
 
   sample_map[voice] = sample;
 
-  base_note = samples[sample].base_note / 100; /* Try to avoid overflows */
-  note_freq /= 100;
+  if (voices[voice].fixed_pitch)       /* Fixed pitch */
+    {
+      freq = samples[sample].base_freq;
+    }
+  else
+    {
+      base_note = samples[sample].base_note / 100;
+      note_freq /= 100;
 
-  freq = samples[sample].base_freq * note_freq / base_note;
+      freq = samples[sample].base_freq * note_freq / base_note;
+    }
 
   voices[voice].orig_freq = freq;
 
@@ -1507,7 +1512,7 @@ guswave_start_note2 (int dev, int voice, int note_num, int volume)
    */
 
   freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender,
-                          voices[voice].bender_range);
+                          voices[voice].bender_range, 0);
   voices[voice].current_freq = freq;
 
   pan = (samples[sample].panning + voices[voice].panning) / 32;
@@ -1894,7 +1899,6 @@ guswave_load_patch (int dev, int format, const char *addr,
            {
              get_user (data, (unsigned char *) &((addr)[sizeof_patch + i]));
              if (patch.mode & WAVE_UNSIGNED)
-
                if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
                  data ^= 0x80; /* Convert to signed */
              gus_poke (target + i, data);
@@ -1932,7 +1936,7 @@ guswave_load_patch (int dev, int format, const char *addr,
 
          address = target;
 
-         if (audio_devs[gus_devnum]->dmachan1 > 3)
+         if (audio_devs[gus_devnum]->dmap_out->dma > 3)
            {
              hold_address = address;
              address = address >> 1;
@@ -1951,7 +1955,7 @@ guswave_load_patch (int dev, int format, const char *addr,
            dma_command |= 0x80;        /* Invert MSB */
          if (patch.mode & WAVE_16_BITS)
            dma_command |= 0x40;        /* 16 bit _DATA_ */
-         if (audio_devs[gus_devnum]->dmachan1 > 3)
+         if (audio_devs[gus_devnum]->dmap_out->dma > 3)
            dma_command |= 0x04;        /* 16 bit DMA _channel_ */
 
          gus_write8 (0x41, dma_command);       /* Lets go luteet (=bugs) */
@@ -2217,63 +2221,49 @@ gus_audio_set_bits (int bits)
 }
 
 static int
-gus_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+gus_audio_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   int             val;
 
   switch (cmd)
     {
     case SOUND_PCM_WRITE_RATE:
-      if (local)
-       return gus_audio_set_speed ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, gus_audio_set_speed (val));
+      val = *(int *) arg;
+      return (*(int *) arg = gus_audio_set_speed (val));
       break;
 
     case SOUND_PCM_READ_RATE:
-      if (local)
-       return gus_audio_speed;
-      return ioctl_out (arg, gus_audio_speed);
+      return (*(int *) arg = gus_audio_speed);
       break;
 
     case SNDCTL_DSP_STEREO:
-      if (local)
-       return gus_audio_set_channels ((int) arg + 1) - 1;
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, gus_audio_set_channels (val + 1) - 1);
+      val = *(int *) arg;
+      return (*(int *) arg = gus_audio_set_channels (val + 1) - 1);
       break;
 
     case SOUND_PCM_WRITE_CHANNELS:
-      if (local)
-       return gus_audio_set_channels ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, gus_audio_set_channels (val));
+      val = *(int *) arg;
+      return (*(int *) arg = gus_audio_set_channels (val));
       break;
 
     case SOUND_PCM_READ_CHANNELS:
-      if (local)
-       return gus_audio_channels;
-      return ioctl_out (arg, gus_audio_channels);
+      return (*(int *) arg = gus_audio_channels);
       break;
 
     case SNDCTL_DSP_SETFMT:
-      if (local)
-       return gus_audio_set_bits ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, gus_audio_set_bits (val));
+      val = *(int *) arg;
+      return (*(int *) arg = gus_audio_set_bits (val));
       break;
 
     case SOUND_PCM_READ_BITS:
-      if (local)
-       return gus_audio_bits;
-      return ioctl_out (arg, gus_audio_bits);
+      return (*(int *) arg = gus_audio_bits);
 
     case SOUND_PCM_WRITE_FILTER:       /* NOT POSSIBLE */
-      return ioctl_out (arg, -EINVAL);
+      return (*(int *) arg = -EINVAL);
       break;
 
     case SOUND_PCM_READ_FILTER:
-      return ioctl_out (arg, -EINVAL);
+      return (*(int *) arg = -EINVAL);
       break;
 
     }
@@ -2530,7 +2520,7 @@ gus_transfer_output_block (int dev, unsigned long buf,
   address = this_one * pcm_bsize;
   address += chn * pcm_banksize;
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     {
       hold_address = address;
       address = address >> 1;
@@ -2547,7 +2537,7 @@ gus_transfer_output_block (int dev, unsigned long buf,
   else
     dma_command |= 0x80;       /* Invert MSB */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     dma_command |= 0x04;       /* 16 bit DMA channel */
 
   gus_write8 (0x41, dma_command);      /* Kick start */
@@ -2578,7 +2568,7 @@ gus_transfer_output_block (int dev, unsigned long buf,
 
 static void
 gus_audio_output_block (int dev, unsigned long buf, int total_count,
-                       int intrflag, int restart_dma)
+                       int intrflag)
 {
   pcm_current_buf = buf;
   pcm_current_count = total_count;
@@ -2589,7 +2579,7 @@ gus_audio_output_block (int dev, unsigned long buf, int total_count,
 
 static void
 gus_audio_start_input (int dev, unsigned long buf, int count,
-                      int intrflag, int restart_dma)
+                      int intrflag)
 {
   unsigned long   flags;
   unsigned char   mode;
@@ -2597,11 +2587,11 @@ gus_audio_start_input (int dev, unsigned long buf, int count,
   save_flags (flags);
   cli ();
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
   mode = 0xa0;                 /* DMA IRQ enabled, invert MSB */
 
-  if (audio_devs[dev]->dmachan2 > 3)
+  if (audio_devs[dev]->dmap_in->dma > 3)
     mode |= 0x04;              /* 16 bit DMA channel */
   if (gus_audio_channels > 1)
     mode |= 0x02;              /* Stereo */
@@ -2657,6 +2647,7 @@ gus_audio_prepare_for_output (int dev, int bsize, int bcount)
 
   if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024))
     pcm_nblk--;
+  gus_write8 (0x41, 0);                /* Disable GF1 DMA */
 
   return 0;
 }
@@ -2728,20 +2719,10 @@ static struct audio_driver gus_audio_driver =
   gus_audio_prepare_for_input,
   gus_audio_prepare_for_output,
   gus_audio_reset,
-  gus_audio_reset,
   gus_local_qlen,
   gus_copy_from_user
 };
 
-static struct audio_operations gus_audio_operations =
-{
-  "Gravis UltraSound",
-  NEEDS_RESTART,
-  AFMT_U8 | AFMT_S16_LE,
-  NULL,
-  &gus_audio_driver
-};
-
 static void
 guswave_setup_voice (int dev, int voice, int chn)
 {
@@ -2756,7 +2737,11 @@ guswave_setup_voice (int dev, int voice, int chn)
     (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128;
   voices[voice].panning =
     (info->controllers[CTL_PAN] * 2) - 128;
-  voices[voice].bender = info->bender_value;
+  voices[voice].bender = 0;
+  voices[voice].bender_range = info->bender_range;
+
+  if (chn == 9)
+    voices[voice].fixed_pitch = 1;
 }
 
 static void
@@ -2767,7 +2752,7 @@ guswave_bender (int dev, int voice, int value)
 
   voices[voice].bender = value - 8192;
   freq = compute_finetune (voices[voice].orig_freq, value - 8192,
-                          voices[voice].bender_range);
+                          voices[voice].bender_range, 0);
   voices[voice].current_freq = freq;
 
   save_flags (flags);
@@ -2825,6 +2810,7 @@ guswave_alloc (int dev, int chn, int note, struct voice_alloc_info *alloc)
 
 static struct synth_operations guswave_operations =
 {
+  "GUS",
   &gus_info,
   0,
   SYNTH_TYPE_SAMPLE,
@@ -2894,23 +2880,23 @@ gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
                         SOUND_MASK_SYNTH|SOUND_MASK_PCM)
   if (((cmd >> 8) & 0xff) == 'M')
     {
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
        switch (cmd & 0xff)
          {
          case SOUND_MIXER_RECSRC:
-           get_user (gus_recmask, (int *) arg);
+           gus_recmask = *(int *) arg;
            gus_recmask &= MIX_DEVS;
            if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
              gus_recmask = SOUND_MASK_MIC;
            /* Note! Input volumes are updated during next open for recording */
-           return ioctl_out (arg, gus_recmask);
+           return (*(int *) arg = gus_recmask);
            break;
 
          case SOUND_MIXER_MIC:
            {
              int             vol;
 
-             get_user (vol, (int *) arg);
+             vol = *(int *) arg;
              vol &= 0xff;
 
              if (vol < 0)
@@ -2919,7 +2905,7 @@ gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
                vol = 100;
              gus_mic_vol = vol;
              set_input_volumes ();
-             return ioctl_out (arg, vol | (vol << 8));
+             return (*(int *) arg = vol | (vol << 8));
            }
            break;
 
@@ -2927,7 +2913,7 @@ gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
            {
              int             vol;
 
-             get_user (vol, (int *) arg);
+             vol = *(int *) arg;
              vol &= 0xff;
 
              if (vol < 0)
@@ -2936,26 +2922,26 @@ gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
                vol = 100;
              gus_line_vol = vol;
              set_input_volumes ();
-             return ioctl_out (arg, vol | (vol << 8));
+             return (*(int *) arg = vol | (vol << 8));
            }
            break;
 
          case SOUND_MIXER_PCM:
-           get_user (gus_pcm_volume, (int *) arg);
+           gus_pcm_volume = *(int *) arg;
            gus_pcm_volume &= 0xff;
            if (gus_pcm_volume < 0)
              gus_pcm_volume = 0;
            if (gus_pcm_volume > 100)
              gus_pcm_volume = 100;
            gus_audio_update_volume ();
-           return ioctl_out (arg, gus_pcm_volume | (gus_pcm_volume << 8));
+           return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
            break;
 
          case SOUND_MIXER_SYNTH:
            {
              int             voice;
 
-             get_user (gus_wave_volume, (int *) arg);
+             gus_wave_volume = *(int *) arg;
              gus_wave_volume &= 0xff;
 
              if (gus_wave_volume < 0)
@@ -2967,7 +2953,7 @@ gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
                for (voice = 0; voice < nr_voices; voice++)
                  dynamic_volume_change (voice);        /* Apply the new vol */
 
-             return ioctl_out (arg, gus_wave_volume | (gus_wave_volume << 8));
+             return (*(int *) arg = gus_wave_volume | (gus_wave_volume << 8));
            }
            break;
 
@@ -2981,39 +2967,39 @@ gus_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
          {
 
          case SOUND_MIXER_RECSRC:
-           return ioctl_out (arg, gus_recmask);
+           return (*(int *) arg = gus_recmask);
            break;
 
          case SOUND_MIXER_DEVMASK:
-           return ioctl_out (arg, MIX_DEVS);
+           return (*(int *) arg = MIX_DEVS);
            break;
 
          case SOUND_MIXER_STEREODEVS:
-           return ioctl_out (arg, 0);
+           return (*(int *) arg = 0);
            break;
 
          case SOUND_MIXER_RECMASK:
-           return ioctl_out (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
+           return (*(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE);
            break;
 
          case SOUND_MIXER_CAPS:
-           return ioctl_out (arg, 0);
+           return (*(int *) arg = 0);
            break;
 
          case SOUND_MIXER_MIC:
-           return ioctl_out (arg, gus_mic_vol | (gus_mic_vol << 8));
+           return (*(int *) arg = gus_mic_vol | (gus_mic_vol << 8));
            break;
 
          case SOUND_MIXER_LINE:
-           return ioctl_out (arg, gus_line_vol | (gus_line_vol << 8));
+           return (*(int *) arg = gus_line_vol | (gus_line_vol << 8));
            break;
 
          case SOUND_MIXER_PCM:
-           return ioctl_out (arg, gus_pcm_volume | (gus_pcm_volume << 8));
+           return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
            break;
 
          case SOUND_MIXER_SYNTH:
-           return ioctl_out (arg, gus_wave_volume | (gus_wave_volume << 8));
+           return (*(int *) arg = gus_wave_volume | (gus_wave_volume << 8));
            break;
 
          default:
@@ -3214,6 +3200,7 @@ gus_wave_init (struct address_info *hw_config)
 
 
   samples = (struct patch_info *) (sound_mem_blocks[sound_nblocks] = vmalloc ((MAX_SAMPLE + 1) * sizeof (*samples)));
+  sound_mem_sizes[sound_nblocks] = (MAX_SAMPLE + 1) * sizeof (*samples);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (samples == NULL)
@@ -3224,7 +3211,7 @@ gus_wave_init (struct address_info *hw_config)
 
   conf_printf (tmp2, hw_config);
   tmp2[sizeof (gus_info.name) - 1] = 0;
-  strncpy (gus_info.name, tmp2, strlen (tmp2));
+  strcpy (gus_info.name, tmp2);
 
   if (num_synths >= MAX_SYNTH_DEV)
     printk ("GUS Error: Too many synthesizers\n");
@@ -3244,15 +3231,23 @@ gus_wave_init (struct address_info *hw_config)
   if (gus_mem_size > 0)
     if (num_audiodevs < MAX_AUDIO_DEV)
       {
-       audio_devs[gus_devnum = num_audiodevs++] = &gus_audio_operations;
-       audio_devs[gus_devnum]->dmachan1 = dma;
-       audio_devs[gus_devnum]->dmachan2 = dma2;
-       audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE;
+
+       if ((gus_devnum = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
+                                                 "Ultrasound",
+                                                 &gus_audio_driver,
+                                              sizeof (struct audio_driver),
+                                                 NEEDS_RESTART |
+                                             ((dma2 != dma && dma2 != -1) ?
+                                              DMA_DUPLEX : 0),
+                                                 AFMT_U8 | AFMT_S16_LE,
+                                                 NULL,
+                                                 dma,
+                                                 dma2)) < 0)
+                         return;
+
        audio_devs[gus_devnum]->min_fragment = 9;
        audio_devs[gus_devnum]->mixer_dev = num_mixers;         /* Next mixer# */
        audio_devs[gus_devnum]->flags |= DMA_HARDSTOP;
-       if (dma2 != dma && dma2 != -1)
-         audio_devs[gus_devnum]->flags |= DMA_DUPLEX;
       }
     else
       printk ("GUS: Too many audio devices available\n");
@@ -3518,12 +3513,14 @@ guswave_dma_irq (void)
        break;
 
       case GUS_DEV_PCM_CONTINUE:       /* Left channel data transferred */
+       gus_write8 (0x41, 0);   /* Disable GF1 DMA */
        gus_transfer_output_block (pcm_current_dev, pcm_current_buf,
                                   pcm_current_count,
                                   pcm_current_intrflag, 1);
        break;
 
       case GUS_DEV_PCM_DONE:   /* Right or mono channel data transferred */
+       gus_write8 (0x41, 0);   /* Disable GF1 DMA */
        if (pcm_qlen < pcm_nblk)
          {
            int             flag = (1 - dma_active) * 2;        /* 0 or 2 */
@@ -3641,6 +3638,7 @@ gus_tmr_restart (int dev)
 static struct sound_lowlev_timer gus_tmr =
 {
   0,
+  1,
   gus_tmr_start,
   gus_tmr_disable,
   gus_tmr_restart
index 732c1c431fb40fd02a12d36d9e0a2e5c4de3193a..07666ec29f02c93987b1963a61a8f71aeaeac3a9 100644 (file)
@@ -4,7 +4,7 @@
  * Driver for the ICS2101 mixer of GUS v3.7.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -120,11 +120,11 @@ ics2101_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   if (((cmd >> 8) & 0xff) == 'M')
     {
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
        {
          int             val;
 
-         get_user (val, (int *) arg);
+         val = *(int *) arg;
 
          switch (cmd & 0xff)
            {
@@ -133,23 +133,23 @@ ics2101_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
              break;
 
            case SOUND_MIXER_MIC:
-             return ioctl_out (arg, set_volumes (DEV_MIC, val));
+             return (*(int *) arg = set_volumes (DEV_MIC, val));
              break;
 
            case SOUND_MIXER_CD:
-             return ioctl_out (arg, set_volumes (DEV_CD, val));
+             return (*(int *) arg = set_volumes (DEV_CD, val));
              break;
 
            case SOUND_MIXER_LINE:
-             return ioctl_out (arg, set_volumes (DEV_LINE, val));
+             return (*(int *) arg = set_volumes (DEV_LINE, val));
              break;
 
            case SOUND_MIXER_SYNTH:
-             return ioctl_out (arg, set_volumes (DEV_GF1, val));
+             return (*(int *) arg = set_volumes (DEV_GF1, val));
              break;
 
            case SOUND_MIXER_VOLUME:
-             return ioctl_out (arg, set_volumes (DEV_VOL, val));
+             return (*(int *) arg = set_volumes (DEV_VOL, val));
              break;
 
            default:
@@ -167,39 +167,39 @@ ics2101_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
            break;
 
          case SOUND_MIXER_DEVMASK:
-           return ioctl_out (arg, MIX_DEVS);
+           return (*(int *) arg = MIX_DEVS);
            break;
 
          case SOUND_MIXER_STEREODEVS:
-           return ioctl_out (arg, SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC);
+           return (*(int *) arg = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC);
            break;
 
          case SOUND_MIXER_RECMASK:
-           return ioctl_out (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
+           return (*(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE);
            break;
 
          case SOUND_MIXER_CAPS:
-           return ioctl_out (arg, 0);
+           return (*(int *) arg = 0);
            break;
 
          case SOUND_MIXER_MIC:
-           return ioctl_out (arg, volumes[DEV_MIC]);
+           return (*(int *) arg = volumes[DEV_MIC]);
            break;
 
          case SOUND_MIXER_LINE:
-           return ioctl_out (arg, volumes[DEV_LINE]);
+           return (*(int *) arg = volumes[DEV_LINE]);
            break;
 
          case SOUND_MIXER_CD:
-           return ioctl_out (arg, volumes[DEV_CD]);
+           return (*(int *) arg = volumes[DEV_CD]);
            break;
 
          case SOUND_MIXER_VOLUME:
-           return ioctl_out (arg, volumes[DEV_VOL]);
+           return (*(int *) arg = volumes[DEV_VOL]);
            break;
 
          case SOUND_MIXER_SYNTH:
-           return ioctl_out (arg, volumes[DEV_GF1]);
+           return (*(int *) arg = volumes[DEV_GF1]);
            break;
 
          default:
index ae5afa5c85bd42285a6200976aa5096eb0a563ce..b96d5ed1b22ec04eaa35bfa029cb5815015e62e6 100644 (file)
@@ -4,7 +4,7 @@
  * DRAM size encoding table for AMD Interwave chip.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
index d8d69e773f0be130a86677830fe71006a95018c5..9ee76e4b656bcfac9c9bbd5fd4dac9ef50865f33 100644 (file)
@@ -1,3 +1,61 @@
+ver.0.3.1a
+       - Fix a bug to reset voice counter in awe_reset
+       - Fix voice balance on GUS mode
+       - Make symlink on /usr/include/asm in install script
+
+ver.0.3.1
+       - Remove zero size arrays from awe_voice.h
+       - Fix init_fm routine
+       - Remove all samples except primary samples in REMOVE_LAST_SAMPLES
+
+ver.0.3.0a
+       - Add AWE_NOTEOFF_ALL control
+       - Remove AWE_INIT_ATTEN control
+
+ver.0.3.0
+       - Fix decay time table
+       - Add exclusive sounds mode
+       - Add capability to get current status
+
+ver.0.2.99e
+       - Add #ifdef for all sounds/notes off controls.
+       - Fix bugs on searching the default drumset/preset.
+       - Fix usslite patch to modify the default Config.in.
+
+ver.0.2.99d
+       - Fix bugs of attack/hold parameters
+       - Fix attack & decay time table
+
+ver.0.2.99c
+       - Change volume control messages (main & expression volume)
+         to accesspt normal MIDI parameters in channel mode.
+       - Use channel mode in SEQ2 controls.
+
+ver.0.2.99b
+       - #ifdef patch manager functions (for OSS-3.7)
+
+ver.0.2.99a
+       - Fix sustain bug
+
+ver.0.2.99 (0.3 beta)
+       - Support multiple instruments
+
+ver.0.2.0c
+       - Add copyright notice
+       - FreeBSD 2.2-ALPHA integration
+
+ver.0.2.0b
+       - Remove buffered reading appended in v0.2.0a
+       - Remove SMAxW register check on writing
+       - Support Linux 2.1.x kernel
+       - Rewrite installation script
+
+ver.0.2.0a
+       - Define SEQUENCER_C for tuning.h for FreeBSD system
+       - Improvement of sample loading speed
+       - Fix installation script
+       - Add PnP driver functions for ISA PnP driver support
+
 ver.0.2.0
        - Includes FreeBSD port
        - Can load GUS compatible patches
index 0f175d1bb7d0f22140f939d0d11b9ec9e6e51123..ff107a819f0f4645f38b9d42446239d2dfed1e0a 100644 (file)
@@ -3,4 +3,55 @@ bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND
 if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
    bool 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER
    bool 'AWE32 synth' CONFIG_AWE32_SYNTH
+   bool 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16
+
+   if [ "$CONFIG_AEDSP16" = "y" ]; then
+      comment 'SC-6600 Audio Cards have no jumper switches at all'
+      bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
+
+      if [ "$CONFIG_SB" = "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+         bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO
+         if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then
+           comment 'Audio Excel DSP 16 [Sound Blaster Pro]'
+            hex 'I/O base for Audio Excel DSP 16 220 or 240' \
+                                                  AEDSP16_BASE $SBC_BASE
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+                                                  AEDSP16_SBC_IRQ $SBC_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_SBC_DMA $SBC_DMA
+         fi
+      fi
+   
+      if [ "$CONFIG_MSS" = "y" -a "$CONFIG_AEDSP16_SBPRO" != "y" ]; then
+         bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS
+         if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then
+           comment 'Audio Excel DSP 16 [Microsoft Sound System]'
+           hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+                                                  AEDSP16_MSS_IRQ $MSS_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_MSS_DMA $MSS_DMA
+         fi
+      fi
+
+      if [ "$CONFIG_MIDI" = "y" ]; then
+         bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
+         if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
+           comment 'Audio Excel DSP 16 [MPU-401]'
+           if [ "$CONFIG_AEDSP16_SBPRO" != "y" \
+                -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+              hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+           fi
+            int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' \
+                                                       AEDSP16_MPU_IRQ $MPU_IRQ
+         fi
+      fi
+
+      if [ "$CONFIG_SC6600" = "y" ]; then
+         comment 'SC-6600 specific configuration'
+         bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY
+         int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' \
+                                               CONFIG_SC6600_CDROM 4
+         hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
+      fi
+
+   fi
 fi
index 13bb1b95ae5cf0d4673d220e64cdbc330bba24ae..d4687017ff896efcf880b33f166d0a45529432d6 100644 (file)
@@ -1,6 +1,6 @@
 all:   lowlevel.o
 
-ALLOBJS = init.o aci.o awe_wave.o
+ALLOBJS = init.o aci.o awe_wave.o aedsp16.o
 OBJS = init.o
 
 ifdef CONFIG_LOWLEVEL_SOUND
@@ -10,6 +10,9 @@ endif
 ifdef CONFIG_AWE32_SYNTH
 OBJS := $(OBJS) awe_wave.o
 endif
+ifdef CONFIG_AEDSP16
+OBJS := $(OBJS) aedsp16.o
+endif
 endif
 
 lowlevel.o:    $(OBJS)
@@ -27,8 +30,20 @@ manual_config.h:
        @exit 1
 
 clean:
-       rm -f core x y z *~ *.o module
+       rm -f core x y z *~ *.o module .depend
+
+dep:
+       $(CPP) -M $(CFLAGS) -I. *.c > .depend
 
 ifdef HOSTCC
 include $(TOPDIR)/Rules.make
+else
+USE_DEPEND=y
+endif
+
+ifdef USE_DEPEND
+# include a dependency file if one exists
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
 endif
diff --git a/drivers/sound/lowlevel/README.aedsp16 b/drivers/sound/lowlevel/README.aedsp16
new file mode 100644 (file)
index 0000000..7b61363
--- /dev/null
@@ -0,0 +1,106 @@
+Driver
+------
+
+Informations about Audio Excel DSP 16 driver can be found in the source
+file lowlevel/aedsp16.c
+Please, read the head of the source before using it. It contain useful
+informations.
+
+Configuration
+-------------
+
+The Audio Excel configuration, is now done with the standard Linux setup.
+You have to configure the sound card (Sound Blaster or Microsoft Sound System)
+and, if you want it, the Roland MPU-401 (do not use the Sound Blaster MPU-401,
+SB-MPU401) in the main driver menu. Activate the lowlevel drivers then select
+the Audio Excel hardware that you want to initialize. Check the IRQ/DMA/MIRQ
+of the Audio Excel initialization: it must be the same as the SBPRO (or MSS)
+setup. If the parameters are different, correct it.
+I you own a Gallant's audio card based on SC-6600, activate the SC-6600 support.
+If you want to change the configuration of the sound board, be sure to
+check off all the configuration items before re-configure it.
+
+Sound cards supported
+---------------------
+This driver supports the SC-6000 and SC-6600 based Gallant's sound card.
+It don't support the Audio Excel DSP 16 III (try the SC-6600 code).
+I'm working on the III version of the card: if someone have useful
+informations about it, please let me know.
+For all the non-supported audio cards, you have to boot MS-DOS (or WIN95)
+activating the audio card with the MS-DOS device driver, then you have to
+<ctrl>-<alt>-<del> and boot Linux.
+Follow these steps:
+
+1) Compile Linux kernel with standard sound driver, using the emulation
+   you want, with the parameters of your audio card,
+   e.g. Microsoft Sound System irq10 dma3
+2) Install your new kernel as the default boot kernel.
+3) Boot MS-DOS and configure the audio card with the boot time device
+   driver, for MSS irq10 dma3 in our example.
+4) <ctrl>-<alt>-<del> and boot Linux. This will mantain the DOS configuration
+   and will boot the new kernel with sound driver. The sound driver will find
+   the audio card and will recognize and attach it.
+
+Reports on User successes
+-------------------------
+
+[--------------------------------------------------------------------------]
+
+From POPmail Mon Jul 29 18:23:12 1996
+Received: from cdc8g5.cdc.polimi.it by mbox.vol.it with SMTP
+       (1.39.111.2/16.2) id AA257995686; Mon, 29 Jul 1996 09:34:46 +0200
+Return-Path: <sjg95@unixfe.rl.ac.uk>
+Received: from [130.246.12.16] by cdc8g5.cdc.polimi.it with SMTP
+       (1.38.193.4/15.6) id AA27426; Mon, 29 Jul 1996 09:42:49 +0200
+Posted-Date: Mon, 29 Jul 1996 08:35:40 +0100
+Received-Date: Mon, 29 Jul 1996 09:42:49 +0200
+Received: (from sjg95@localhost) by unixfe.rl.ac.uk (8.7.3/8.7.3) 
+          id IAA58788 for riccardo@cdc8g5.cdc.polimi.it; Mon, 29 Jul 1996 08:35:40 +0100
+Date: Mon, 29 Jul 1996 08:35:40 +0100
+From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
+Message-Id: <199607290735.IAA58788@unixfe.rl.ac.uk>
+To: riccardo@cdc8g5.cdc.polimi.it (Riccardo Facchetti)
+Subject: Re: Audio Excel DSP 16 initialization code
+Newsgroups: comp.os.linux.announce
+X-Newsreader: TIN [version 1.2 PL2]
+Status: RO
+X-Status: A
+
+Just to let you know got my Audio Excel (emulating a MSS) working
+with my original SB16, thanks for the driver!
+
+/dev/sndstat:
+
+Linux huey 2.0.9 #10 Sat Jul 27 11:41:42 BST 1996 i586)
+Kernel: Linux huey 2.0.9 #14 Sun Jul 28 12:50:43 BST 1996 i586
+Config options: c0202
+
+Installed drivers: 
+Type 10: MS Sound System
+Type 24: MS Sound System (AXP)
+Type 27: Compaq Deskpro XL
+Type 2: Sound Blaster
+
+Card config: 
+MS Sound System at 0x530 irq 11 drq 3
+Sound Blaster at 0x240 irq 5 drq 1,5
+
+Audio devices:
+0: MSS audio codec (CS4231A)
+1: Sound Blaster 16 (4.12)
+
+Synth devices:
+
+Midi devices: NOT ENABLED IN CONFIG
+
+Timers:
+0: System clock
+1: MSS audio codec (CS4231A)
+
+Mixers:
+0: MSS audio codec (CS4231A)
+1: Sound Blaster
+
+[--------------------------------------------------------------------------]
+
+    Riccardo
index c962fa77ab7d4cc5645f338edea807712daed00e..db082bf526d7748473a4fdab7a9e2f3b0ea6b7c4 100644 (file)
 ================================================================
        AWE32 Sound Driver for Linux / FreeBSD
-               version 0.2.0; Oct. 16, 1996
+               version 0.3.1; Jan. 11, 1997
 ================================================================
 
 * GENERAL NOTES
 
-This is a sound driver extension for SB-AWE32 cards to enable the
-wave synth operations.  The driver is provided for both Linux 1.2.x
-and 2.0.x kernels, and also FreeBSD.  Follow the corresponding
-instruction in the installation document if you use old sound drivers.
+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.
 
 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
-and suggestions directly to me (_NOT_ to Linus Torvalds or Hannu
+and suggestions directly to Iwai (_NOT_ to Linus Torvalds or Hannu
 Savolainen).
 
 
 * CAUTION
 
-- In the v0.2.0, parameters of hardware control are changed to have a
-compatibility with GUS driver.  If you have older awesfx and drvmidi
-utilities, please get new versions and install them again.
+- 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.
 
-- The SFX file after v0.1.5 is no more compatible from 0.1.4x.
-Please remake the obsolete SFX files again by txt2sfx utility.
+
+* 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:
+       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)
+See the FAQ list on the URL above.
+
+
+* COMPILE FLAGS
+
+Compile conditions are defined in awe_config.h.
+
+- 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.
+
+- 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.
+
+- HAS_LOWLEVEL_H               (default: not defined)
+    indicates the system has "lowlevel.h" in the sound/lowlevel
+    directory.  OSS driver has this file.
+
+- AWE_NO_PATCHMGR              (default: not defined)
+    indicates the sound driver has no patch manager function (for
+    OSS-3.707 (in Linux-2.1.13) or newer). 
+
+- 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. 
+    
+- 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. 
+
+- AWE_ALWAYS_INIT_FM           (default: not defined)
+    indicates the AWE driver always initialize FM passthrough even
+    without DRAM on board.  Emu8000 chip has a restriction for playing
+    samples on DRAM that at least two channels must be occupied as
+    passthrough channels. 
+
+- 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)
+    Enables MIDI control #120 and #123 as "all notes off" and "all
+    sounds off" events, respectively.
+
+- 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.
+
+- DEF_FM_REVERB_DEPTH          (default: 0x10)
+    The default strength to be sent to the reverb effect engine.
+    From 0 to 0xff.  Larger numbers may often cause weird sounds.
 
 
 * USING THE DRIVER
@@ -32,8 +130,20 @@ 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/
 
-Follow the instruction in awesfx package to make patched GM and
-GS presets.  Then, load the SFX file on driver by sfxload utility.
+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
 
@@ -58,12 +168,29 @@ to reboot their system many times :-)
 
 * BUGS & TODO'S
 
-- Some instruments are inaudible, eg. glockenspiel and sinewaves.
-- No stereo sample can be used.
 - More smart patch management
 - More smart DRAM memory control
 - Dynamic buffer allocation
 - etc, etc, etc.
 
 
+* COPYRIGHT
+
+Copyright (C) 1996,1997 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
+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 program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
 Takashi Iwai <iwai@dragon.mm.t.u-tokyo.ac.jp>
index cad3c7e067398c72de8aeeea168822dd4b70f7b6..657fa697dbb121e56dc02d2a17abb334fcd19593 100644 (file)
@@ -22,9 +22,9 @@
  *        First version written.
  *   1995-12-31  Markus Kuhn
  *        Second revision, general code cleanup.
- *   1996-05-16         Hannu Savolainen
+ *   1997-05-16         Hannu Savolainen
  *       Integrated with other parts of the driver.
- *   1996-05-28  Markus Kuhn
+ *   1997-05-28  Markus Kuhn
  *        Initialize CS4231A mixer, make ACI first mixer,
  *        use new private mixer API for solo mode.
  */
@@ -73,6 +73,13 @@ int aci_solo;                     /* status bit of the card that can't be    *
 
 static int aci_present = 0;
 
+#ifdef MODULE                  /* Whether the aci mixer is to be reset.    */
+int aci_reset = 0;             /* Default: don't reset if the driver is a  */
+#else                          /* module; use "insmod sound.o aci_reset=1" */
+int aci_reset = 1;             /* to override.                             */
+#endif
+
+
 #define COMMAND_REGISTER    (aci_port)
 #define STATUS_REGISTER     (aci_port + 1)
 #define BUSY_REGISTER       (aci_port + 2)
@@ -282,7 +289,7 @@ static int getvolume(caddr_t arg,
   if (indexed_cmd(0xf0, right_index, &buf)) return -EIO;
   vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8;
 
-  return snd_ioctl_return((int *) arg, vol);
+  return (*(int *) arg = vol);
 }
 
 
@@ -290,23 +297,21 @@ static int setvolume(caddr_t arg,
                     unsigned char left_index, unsigned char right_index)
 {
   int vol, ret;
-  unsigned param;
 
-  param = get_user((int *) arg);
   /* left channel */
-  vol = param & 0xff;
+  vol = *(int *)arg & 0xff;
   if (vol > 100) vol = 100;
   vol = SCALE(100, 0x20, vol);
   if (write_cmd(left_index, 0x20 - vol)) return -EIO;
   ret = SCALE(0x20, 100, vol);
   /* right channel */
-  vol = (param >> 8) & 0xff;
+  vol = (*(int *)arg >> 8) & 0xff;
   if (vol > 100) vol = 100;
   vol = SCALE(100, 0x20, vol);
   if (write_cmd(right_index, 0x20 - vol)) return -EIO;
   ret |= SCALE(0x20, 100, vol) << 8;
  
-  return snd_ioctl_return((int *) arg, ret);
+  return (*(int *) arg = ret);
 }
 
 
@@ -318,14 +323,14 @@ aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
 
   /* handle solo mode control */
   if (cmd == SOUND_MIXER_PRIVATE1) {
-    if (get_user((int *) arg) >= 0) {
-      aci_solo = !!get_user((int *) arg);
+    if (*(int *) arg >= 0) {
+      aci_solo = !!*(int *) arg;
       if (write_cmd(0xd2, aci_solo)) return -EIO;
     } else if (aci_version >= 0xb0) {
       if ((status = read_general_status()) < 0) return -EIO;
-      return snd_ioctl_return ((int *) arg, (status & 0x20) == 0);
+      return (*(int *) arg = (status & 0x20) == 0);
     }
-    return snd_ioctl_return((int *) arg, aci_solo);
+    return (*(int *) arg = aci_solo);
   }
 
   if (((cmd >> 8) & 0xff) == 'M') {
@@ -349,14 +354,14 @@ aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
       case SOUND_MIXER_LINE2:  /* AUX2 */
        return setvolume(arg, 0x3e, 0x36);
       case SOUND_MIXER_IGAIN:  /* MIC pre-amp */
-       vol = get_user((int *) arg) & 0xff;
+       vol = *(int *) arg & 0xff;
        if (vol > 100) vol = 100;
        vol = SCALE(100, 3, vol);
        if (write_cmd(0x03, vol)) return -EIO;
        vol = SCALE(3, 100, vol);
-       return snd_ioctl_return((int *) arg, vol | (vol << 8));
+       return (*(int *) arg = vol | (vol << 8));
       case SOUND_MIXER_RECSRC:
-       return snd_ioctl_return ((int *) arg, 0);
+       return (*(int *) arg = 0);
        break;
       default:
        return -EINVAL;
@@ -365,7 +370,7 @@ aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
       /* only read */
       switch (cmd & 0xff) {
       case SOUND_MIXER_DEVMASK:
-       return snd_ioctl_return ((int *) arg,
+       return (*(int *) arg =
                                 SOUND_MASK_VOLUME | SOUND_MASK_CD    |
                                 SOUND_MASK_MIC    | SOUND_MASK_LINE  |
                                 SOUND_MASK_SYNTH  | SOUND_MASK_PCM   |
@@ -375,20 +380,20 @@ aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
                                 SOUND_MASK_LINE1  | SOUND_MASK_LINE2);
        break;
       case SOUND_MIXER_STEREODEVS:
-       return snd_ioctl_return ((int *) arg,
+       return (*(int *) arg =
                                 SOUND_MASK_VOLUME | SOUND_MASK_CD   |
                                 SOUND_MASK_MIC    | SOUND_MASK_LINE |
                                 SOUND_MASK_SYNTH  | SOUND_MASK_PCM  |
                                 SOUND_MASK_LINE1  | SOUND_MASK_LINE2);
        break;
       case SOUND_MIXER_RECMASK:
-       return snd_ioctl_return ((int *) arg, 0);
+       return (*(int *) arg = 0);
        break;
       case SOUND_MIXER_RECSRC:
-       return snd_ioctl_return ((int *) arg, 0);
+       return (*(int *) arg = 0);
        break;
       case SOUND_MIXER_CAPS:
-       return snd_ioctl_return ((int *) arg, 0);
+       return (*(int *) arg = 0);
        break;
       case SOUND_MIXER_VOLUME:
        return getvolume(arg, 0x04, 0x03);
@@ -410,7 +415,7 @@ aci_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
        if (indexed_cmd(0xf0, 0x21, &buf)) return -EIO;
        vol = SCALE(3, 100, buf <= 3 ? buf : 3);
        vol |= vol << 8;
-       return snd_ioctl_return((int *) arg, vol);
+       return (*(int *) arg = vol);
       default:
        return -EINVAL;
       }
@@ -507,15 +512,17 @@ int attach_aci(void)
   printk("<ACI %02x, id %02x %02x (%s)> at 0x%03x\n",
         aci_version, aci_idcode[0], aci_idcode[1], boardname, aci_port);
 
-  /* initialize ACI mixer */
-  implied_cmd(0xff);
-  aci_solo = 0;
+  if (aci_reset) {
+    /* initialize ACI mixer */
+    implied_cmd(0xff);
+    aci_solo = 0;
+  }
 
   /* attach the mixer */
   request_region(aci_port, 3, "sound mixer (ACI)");
   if (num_mixers < MAX_MIXER_DEV) {
     if (num_mixers > 0 &&
-        !strcmp("MAD16 WSS (CS4231A)", mixer_devs[num_mixers-1]->name)) {
+        !strncmp("MAD16 WSS", mixer_devs[num_mixers-1]->name, 9)) {
       /*
        * The previously registered mixer device is the CS4231A which
        * has no function on an ACI card. Make the ACI mixer the first
@@ -560,23 +567,30 @@ int attach_aci(void)
       mixer_devs[num_mixers++] = &aci_mixer_operations;
   }
 
-  /* Initialize ACI mixer with reasonable power-up values */
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_VOLUME, (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_SYNTH,  (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_PCM,    (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE,   (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_MIC,    (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_CD,     (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE1,  (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE2,  (caddr_t) &volume);
+  /* Just do something; otherwise the first write command fails, at
+   * least with my PCM20.
+   */
+  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_READ_VOLUME, (caddr_t) &volume);
+
+  if (aci_reset) {
+    /* Initialize ACI mixer with reasonable power-up values */
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_VOLUME, (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_SYNTH,  (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_PCM,    (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE,   (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_MIC,    (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_CD,     (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE1,  (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE2,  (caddr_t) &volume);
+  }
   
   aci_present = 1;
 
diff --git a/drivers/sound/lowlevel/aedsp16.c b/drivers/sound/lowlevel/aedsp16.c
new file mode 100644 (file)
index 0000000..beb1695
--- /dev/null
@@ -0,0 +1,1334 @@
+/*
+   sound/aedsp16.c
+
+   Audio Excel DSP 16 software configuration routines
+   Copyright (C) 1995  Riccardo Facchetti (riccardo@cdc8g5.cdc.polimi.it)
+
+   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 program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+ * Include the main OSS Lite header file. It include all the os, OSS Lite, etc
+ * headers needed by this source.
+ */
+#include <linux/config.h>
+#include "../sound_config.h"
+#include <linux/delay.h>
+
+#ifndef AEDSP16_BASE
+#undef CONFIG_AEDSP16
+#endif
+
+#if defined(CONFIG_AEDSP16)
+
+#if defined(CONFIG_AEDSP16_SBPRO) && defined(CONFIG_AEDSP16_MSS)
+#error You have to enable only one of the MSS and SBPRO emulations.
+#endif
+
+/*
+
+   READ THIS
+
+   This module started to configure the Audio Excel DSP 16 Sound Card.
+   Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards.
+
+   NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this
+   audio card and want to see the kernel support for it, please contact me.
+
+   Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401
+   compatible card.
+   It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
+   so before this module, the only way to configure the DSP under linux was
+   boot the MS-DOS loading the sound.sys device driver (this driver soft-
+   configure the sound board hardware by massaging someone of its registers),
+   and then ctrl-alt-del to boot linux with the DSP configured by the DOS
+   driver.
+
+   This module works configuring your Audio Excel DSP 16's irq, dma and
+   mpu-401-irq. The OSS Lite routines rely on the fact that if the
+   hardware is there, they can detect it. The problem with AEDSP16 is
+   that no hardware can be found by the probe routines if the sound card
+   is not configured properly. Sometimes the kernel probe routines can find
+   an SBPRO even when the card is not configured (this is the standard setup
+   of the card), but the SBPRO emulation don't work well if the card is not
+   properly initialized. For this reason
+
+   aedsp16_init_board()
+
+   routine is called before the OSS Lite probe routines try to detect the
+   hardware.
+
+   NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
+
+   NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards
+   have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They
+   have to be configured by software.
+
+   NOTE: The driver is merged with the new OSS Lite sound driver. It works
+   as a lowlevel driver.
+
+   The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
+   the OSS Lite sound driver can be configured for SBPRO and MSS cards
+   at the same time, but the aedsp16 can't be two cards!!
+   When we configure it, we have to choose the SBPRO or the MSS emulation
+   for AEDSP16. We also can install a *REAL* card of the other type (see [1]).
+
+   NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
+   please let me know if it works.
+
+   The MPU-401 support can be compiled in together with one of the other
+   two operating modes.
+
+   NOTE: This is something like plug-and-play: we have only to plug
+   the AEDSP16 board in the socket, and then configure and compile
+   a kernel that uses the AEDSP16 software configuration capability.
+   No jumper setting is needed!
+
+   For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
+   you have just to make config the OSS Lite package, configuring
+   the AEDSP16 sound card, then activating the SBPro emulation mode
+   and at last configuring IRQ and DMA.
+   Compile the kernel and run it.
+
+   NOTE: This means for SC-6000 cards that you can choose irq and dma,
+   but not the I/O addresses. To change I/O addresses you have to set
+   them with jumpers. For SC-6600 cards you have no jumpers so you have
+   to set up your full card configuration in the make config.
+
+   You can change the irq/dma/mirq settings WITHOUT THE NEED to open
+   your computer and massage the jumpers (there are no irq/dma/mirq
+   jumpers to be configured anyway, only I/O BASE values have to be
+   configured with jumpers)
+
+   For some ununderstandable reason, the card default of irq 7, dma 1,
+   don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
+   HDD work, the kernel start to erupt out a lot of messages like:
+
+   'Sound: DMA timed out - IRQ/DRQ config error?'
+
+   For what I can say, I have NOT any conflict at irq 7 (under linux I'm
+   using the lp polling driver), and dma line 1 is unused as stated by
+   /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
+   I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
+   Anyway a setting of irq 10, dma 3 works really fine.
+
+   NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
+   the emulation mode, all the installed hardware and the hardware
+   configuration (irq and dma settings of all the hardware).
+
+   This init module should work with SBPRO+MSS, when one of the two is
+   the AEDSP16 emulation and the other the real card. (see [1])
+   For example:
+
+   AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
+   AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
+
+   MPU401 should work. (see [2])
+
+   [1]
+       ---
+       Date: Mon, 29 Jul 1997 08:35:40 +0100
+       From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
+
+       [...]
+       Just to let you know got my Audio Excel (emulating a MSS) working
+       with my original SB16, thanks for the driver!
+       [...]
+       ---
+
+   [2] Not tested by me for lack of hardware.
+
+   TODO, WISHES AND TECH
+
+   - About I/O ports allocation -
+
+   Request the 2x0h region (port base) in any case if we are using this card.
+
+   NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16
+   port base region (see code) does not mean necessarily that we are emulating
+   sbpro.  Even if this region is the sbpro I/O ports region, we use this
+   region to access the control registers of the card, and if emulating
+   sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro
+   registers are not used, in no way, to emulate an sbpro: they are
+   used only for configuration purposes.
+
+   Started Fri Mar 17 16:13:18 MET 1995
+
+   v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c)
+   - Initial code.
+   v0.2 (ALPHA)
+   - Cleanups.
+   - Integrated with Linux voxware v 2.90-2 kernel sound driver.
+   - SoundBlaster Pro mode configuration.
+   - Microsoft Sound System mode configuration.
+   - MPU-401 mode configuration.
+   v0.3 (ALPHA)
+   - Cleanups.
+   - Rearranged the code to let aedsp16_init_board be more general.
+   - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
+   inclusion too. We rely on os.h
+   - Used the  to get a variable
+   len string (we are not sure about the len of Copyright string).
+   This works with any SB and compatible.
+   - Added the code to request_region at device init (should go in
+   the main body of voxware).
+   v0.4 (BETA)
+   - Better configure.c patch for aedsp16 configuration (better
+   logic of inclusion of AEDSP16 support)
+   - Modified the conditional compilation to better support more than
+   one sound card of the emulated type (read the NOTES above)
+   - Moved the sb init routine from the attach to the very first
+   probe in sb_card.c
+   - Rearrangements and cleanups
+   - Wiped out some unnecessary code and variables: this is kernel
+   code so it is better save some TEXT and DATA
+   - Fixed the request_region code. We must allocate the aedsp16 (sbpro)
+   I/O ports in any case because they are used to access the DSP
+   configuration registers and we can not allow anyone to get them.
+   v0.5
+   - cleanups on comments
+   - prep for diffs against v3.0-proto-950402
+   v0.6
+   - removed the request_region()s when compiling the MODULE sound.o
+   because we are not allowed (by the actual voxware structure) to
+   release_region()
+   v0.7 (pre ALPHA, not distributed)
+   - started porting this module to kernel 1.3.84. Dummy probe/attach
+   routines.
+   v0.8 (ALPHA)
+   - attached all the init routines.
+   v0.9 (BETA)
+   - Integrated with linux-pre2.0.7
+   - Integrated with configuration scripts.
+   - Cleaned up and beautyfied the code.
+   v0.9.9 (BETA)
+   - Thanks to Piercarlo Grandi: corrected the conditonal compilation code.
+     Now only the code configured is compiled in, with some memory saving.
+   v0.9.10
+   - Integration into the sound/lowlevel/ section of the sound driver.
+   - Re-organized the code.
+   v0.9.11 (not distributed)
+   - Rewritten the init interface-routines to initialize the AEDSP16 in
+     one shot.
+   - More cosmetics.
+   - SC-6600 support.
+   - More soft/hard configuration.
+   v0.9.12
+   - Refined the v0.9.11 code with conditional compilation to distinguish
+     between SC-6000 and SC-6600 code.
+   v1.0.0
+   - Prep for merging with OSS Lite and Linux kernel 2.1.13
+   - Corrected a bug in request/check/release region calls (thanks to the
+     new kernel exception handling).
+
+   Known Problems:
+   - Audio Excel DSP 16 III don't work with this driver.
+
+   Credits:
+   Many thanks to Gerald Britton <gbritton@CapAccess.org>. He helped me a
+   lot in testing the 0.9.11 and 0.9.12 versions of this driver.
+
+ */
+
+
+#define VERSION "1.0.0"                /* Version of Audio Excel DSP 16 driver */
+
+#undef AEDSP16_DEBUG 1         /* Define this to enable debug code     */
+#undef AEDSP16_DEBUG_MORE 1    /* Define this to enable more debug     */
+#undef AEDSP16_INFO  1         /* Define this to enable info code      */
+
+#if defined(AEDSP16_DEBUG)
+# define DBG(x) printk x
+# if defined(AEDSP16_DEBUG_MORE)
+#  define DBG1(x) printk x
+# else
+#  define DBG1(x)
+# endif
+#else
+# define DBG(x)
+# define DBG1(x)
+#endif
+
+/*
+ * Misc definitions
+ */
+#define TRUE   1
+#define FALSE  0
+
+/*
+ * Region Size for request/check/release region.
+ */
+#define IOBASE_REGION_SIZE     0x10
+
+/*
+ * Hardware related defaults
+ */
+#define DEF_AEDSP16_IOB 0x220   /* 0x220(default) 0x240                 */
+#define DEF_AEDSP16_IRQ 7      /* 5 7(default) 9 10 11                 */
+#define DEF_AEDSP16_MRQ 0      /* 5 7 9 10 0(default), 0 means disable */
+#define DEF_AEDSP16_DMA 1      /* 0 1(default) 3                       */
+
+/*
+ * Commands of AEDSP16's DSP (SBPRO+special).
+ * Some of them are COMMAND_xx, in the future they may change.
+ */
+#define WRITE_MDIRQ_CFG   0x50 /* Set M&I&DRQ mask (the real config)   */
+#define COMMAND_52        0x52 /*                                      */
+#define READ_HARD_CFG     0x58 /* Read Hardware Config (I/O base etc)  */
+#define COMMAND_5C        0x5c /*                                      */
+#define COMMAND_60        0x60 /*                                      */
+#define COMMAND_66        0x66 /*                                      */
+#define COMMAND_6C        0x6c /*                                      */
+#define COMMAND_6E        0x6e /*                                      */
+#define COMMAND_88        0x88 /*                                      */
+#define DSP_INIT_MSS      0x8c /* Enable Microsoft Sound System mode   */
+#define COMMAND_C5        0xc5 /*                                      */
+#define GET_DSP_VERSION   0xe1 /* Get DSP Version                      */
+#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright                    */
+
+/*
+ * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port
+ * to have the actual I/O port.
+ * Register permissions are:
+ * (wo) == Write Only
+ * (ro) == Read  Only
+ * (w-) == Write
+ * (r-) == Read
+ */
+#define DSP_RESET    0x06      /* offset of DSP RESET             (wo) */
+#define DSP_READ     0x0a      /* offset of DSP READ              (ro) */
+#define DSP_WRITE    0x0c      /* offset of DSP WRITE             (w-) */
+#define DSP_COMMAND  0x0c      /* offset of DSP COMMAND           (w-) */
+#define DSP_STATUS   0x0c      /* offset of DSP STATUS            (r-) */
+#define DSP_DATAVAIL 0x0e      /* offset of DSP DATA AVAILABLE    (ro) */
+
+
+#define RETRY           10     /* Various retry values on I/O opera-   */
+#define STATUSRETRY   1000     /* tions. Sometimes we have to          */
+#define HARDRETRY   500000     /* wait for previous cmd to complete    */
+
+/*
+ * Size of character arrays that store name and version of sound card
+ */
+#define CARDNAMELEN 15         /* Size of the card's name in chars     */
+#define CARDVERLEN  2          /* Size of the card's version in chars  */
+
+#if defined(CONFIG_SC6600)
+/*
+ * Bitmapped flags of hard configuration
+ */
+/*
+ * Decode macros (xl == low byte, xh = high byte)
+ */
+#define IOBASE(xl)             ((xl & 0x01)?0x240:0x220)
+#define JOY(xl)                (xl & 0x02)
+#define MPUADDR(xl)            (                       \
+                               (xl & 0x0C)?0x330:      \
+                               (xl & 0x08)?0x320:      \
+                               (xl & 0x04)?0x310:      \
+                                               0x300)
+#define WSSADDR(xl)            ((xl & 0x10)?0xE80:0x530)
+#define CDROM(xh)              (xh & 0x20)
+#define CDROMADDR(xh)          (((xh & 0x1F) << 4) + 0x200)
+/*
+ * Encode macros
+ */
+#define BLDIOBASE(xl, val) {           \
+       xl &= ~0x01;                    \
+       if (val == 0x240)               \
+               xl |= 0x01;             \
+       }
+#define BLDJOY(xl, val) {              \
+       xl &= ~0x02;                    \
+       if (val == 1)                   \
+               xl |= 0x02;             \
+       }
+#define BLDMPUADDR(xl, val) {          \
+       xl &= ~0x0C;                    \
+       switch (val) {                  \
+               case 0x330:             \
+                       xl |= 0x0C;     \
+                       break;          \
+               case 0x320:             \
+                       xl |= 0x08;     \
+                       break;          \
+               case 0x310:             \
+                       xl |= 0x04;     \
+                       break;          \
+               case 0x300:             \
+                       xl |= 0x00;     \
+                       break;          \
+               default:                \
+                       xl |= 0x00;     \
+                       break;          \
+               }                       \
+       }
+#define BLDWSSADDR(xl, val) {          \
+       xl &= ~0x10;                    \
+       if (val == 0xE80)               \
+               xl |= 0x10;             \
+       }
+#define BLDCDROM(xh, val) {            \
+       xh &= ~0x20;                    \
+       if (val == 1)                   \
+               xh |= 0x20;             \
+       }
+#define BLDCDROMADDR(xh, val) {                \
+       int tmp = val;                  \
+       tmp -= 0x200;                   \
+       tmp >>= 4;                      \
+       tmp &= 0x1F;                    \
+       xh |= tmp;                      \
+       xh &= 0x7F;                     \
+       xh |= 0x40;                     \
+       }
+#endif /* CONFIG_SC6600 */
+
+/*
+ * Bit mapped flags for calling aedsp16_init_board(), and saving the current
+ * emulation mode.
+ */
+#define INIT_NONE   (0   )
+#define INIT_SBPRO  (1<<0)
+#define INIT_MSS    (1<<1)
+#define INIT_MPU401 (1<<2)
+
+static int      soft_cfg = 0;  /* Will contain or'ed values of soft cf */
+static int      soft_cfg_1 = 0;        /* Will contain or'ed values of some cf */
+static int      gc = 0;                /* generic counter (utility counter)    */
+static int      ver[3];         /* DSP Version, hi<-ver[0], lo<-ver[1]  */
+
+#if defined(CONFIG_SC6600)
+static int     hard_cfg[2]     /* lo<-hard_cfg[0] hi<-hard_cfg[1]      */
+                     = { 0, 0};
+#endif /* CONFIG_SC6600 */
+
+#if defined(CONFIG_SC6600)
+/* Decoded hard configuration */
+struct d_hcfg {
+       int iobase;
+       int joystick;
+       int mpubase;
+       int wssbase;
+       int cdrom;
+       int cdrombase;
+} decoded_hcfg;
+#endif /* CONFIG_SC6600 */
+
+/* orVals contain the values to be or'ed                                       */
+struct orVals {
+       int     val;            /* irq|mirq|dma                         */
+       int     or;             /* soft_cfg |= TheStruct.or             */
+};
+
+/* aedsp16_info contain the audio card configuration                  */
+struct aedsp16_info {
+       int base_io;            /* base I/O address for accessing card  */
+       int irq;                /* irq value for DSP I/O                */
+       int mpu_irq;            /* irq for mpu401 interface I/O         */
+       int dma;                /* dma value for DSP I/O                */
+       int init;               /* Initialization status of the card    */
+};
+
+/*
+ * Magic values that the DSP will eat when configuring irq/mirq/dma
+ */
+/* DSP IRQ conversion array             */
+static struct orVals orIRQ[] = {
+       {0x05, 0x28},
+       {0x07, 0x08},
+       {0x09, 0x10},
+       {0x0a, 0x18},
+       {0x0b, 0x20},
+       {0x00, 0x00}
+};
+
+/* MPU-401 IRQ conversion array         */
+static struct orVals orMIRQ[] = {
+       {0x05, 0x04},
+       {0x07, 0x44},
+       {0x09, 0x84},
+       {0x0a, 0xc4},
+       {0x00, 0x00}
+};
+
+/* DMA Channels conversion array        */
+static struct orVals orDMA[] = {
+       {0x00, 0x01},
+       {0x01, 0x02},
+       {0x03, 0x03},
+       {0x00, 0x00}
+};
+
+static struct aedsp16_info ae_config = {
+       DEF_AEDSP16_IOB,
+       DEF_AEDSP16_IRQ,
+       DEF_AEDSP16_MRQ,
+       DEF_AEDSP16_DMA,
+       INIT_NONE
+};
+
+/*
+ * Buffers to store audio card informations
+ */
+static char     DSPCopyright[CARDNAMELEN + 1];
+static char     DSPVersion[CARDVERLEN + 1];
+
+static void aedsp16_delay_10msec(void)
+{
+       for (gc = 0; gc < 1000; gc++)
+               udelay(10);
+}
+
+static int aedsp16_wait_data(int port)
+{
+       int             loop = STATUSRETRY;
+       unsigned char   ret = 0;
+
+       DBG1(("aedsp16_wait_data (0x%x): ", port));
+
+       do {
+                 ret = inb(port + DSP_DATAVAIL);
+       /*
+        * Wait for data available (bit 7 of ret == 1)
+        */
+         } while (!(ret & 0x80) && loop--);
+
+       if (ret & 0x80) {
+               DBG1(("success.\n"));
+               return TRUE;
+       }
+
+       DBG1(("failure.\n"));
+       return FALSE;
+}
+
+static int aedsp16_read(int port)
+{
+       int inbyte;
+
+       DBG(("    Read DSP Byte (0x%x): ", port));
+
+       if (aedsp16_wait_data(port) == FALSE) {
+               DBG(("failure.\n"));
+               return -1;
+       }
+
+       inbyte = inb(port + DSP_READ);
+
+       DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte));
+
+       return inbyte;
+}
+
+static int aedsp16_test_dsp(int port)
+{
+       return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE);
+}
+
+static int aedsp16_dsp_reset(int port)
+{
+       /*
+        * Reset DSP
+        */
+
+       DBG(("Reset DSP:\n"));
+
+       outb(1, (port + DSP_RESET));
+       udelay(10);
+       outb(0, (port + DSP_RESET));
+       udelay(10);
+       udelay(10);
+       if (aedsp16_test_dsp(port) == TRUE) {
+               DBG(("success.\n"));
+               return TRUE;
+       } else
+               DBG(("failure.\n"));
+       return FALSE;
+}
+
+static int aedsp16_write(int port, int cmd)
+{
+       unsigned char   ret;
+       int             loop = HARDRETRY;
+
+       DBG(("    Write DSP Byte (0x%x) [0x%x]: ", port, cmd));
+
+       do {
+               ret = inb(port + DSP_STATUS);
+               /*
+                * DSP ready to receive data if bit 7 of ret == 0
+                */
+               if (!(ret & 0x80)) {
+                       outb(cmd, port + DSP_COMMAND);
+                       DBG(("success.\n"));
+                       return 0;
+               }
+       } while (loop--);
+
+       DBG(("timeout.\n"));
+       printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
+
+       return -1;
+}
+
+#if defined(CONFIG_SC6600)
+
+#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+void aedsp16_pinfo(void) {
+       DBG(("\n Base address:  %x\n", decoded_hcfg.iobase));
+       DBG((" Joystick    : %s present\n", decoded_hcfg.joystick?"":" not"));
+       DBG((" WSS addr    :  %x\n", decoded_hcfg.wssbase));
+       DBG((" MPU-401 addr:  %x\n", decoded_hcfg.mpubase));
+       DBG((" CDROM       : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not"));
+       DBG((" CDROMADDR   :  %x\n\n", decoded_hcfg.cdrombase));
+}
+#endif
+
+void aedsp16_hard_decode(void) {
+
+       DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+
+/*
+ * Decode Cfg Bytes.
+ */
+       decoded_hcfg.iobase     = IOBASE(hard_cfg[0]);
+       decoded_hcfg.joystick   = JOY(hard_cfg[0]);
+       decoded_hcfg.wssbase    = WSSADDR(hard_cfg[0]);
+       decoded_hcfg.mpubase    = MPUADDR(hard_cfg[0]);
+       decoded_hcfg.cdrom      = CDROM(hard_cfg[1]);
+       decoded_hcfg.cdrombase  = CDROMADDR(hard_cfg[1]);
+
+#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+       printk(" Original sound card configuration:\n");
+       aedsp16_pinfo();
+#endif
+
+/*
+ * Now set up the real kernel configuration.
+ */
+       decoded_hcfg.iobase     = AEDSP16_BASE;
+#if defined(CONFIG_AEDSP16_MSS)
+       decoded_hcfg.wssbase    = MSS_BASE;
+#endif
+#if defined(CONFIG_AEDSP16_MPU401)
+       decoded_hcfg.mpubase    = MPU_BASE;
+#endif
+
+#if defined(CONFIG_SC6600_JOY)
+       decoded_hcfg.joystick   = CONFIG_SC6600_JOY; /* Enable */
+#endif
+#if defined(CONFIG_SC6600_CDROM)
+       decoded_hcfg.cdrom      = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */
+#endif
+#if defined(CONFIG_SC6600_CDROMBASE)
+       decoded_hcfg.cdrombase  = CONFIG_SC6600_CDROMBASE; /* 0 Disable */
+#endif
+
+#if defined(AEDSP16_DEBUG)
+       DBG((" New Values:\n"));
+       aedsp16_pinfo();
+#endif
+
+       DBG(("success.\n"));
+}
+
+void aedsp16_hard_encode(void) {
+
+       DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+
+       hard_cfg[0] = 0;
+       hard_cfg[1] = 0;
+
+       hard_cfg[0] |= 0x20;
+
+       BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase);
+       BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase);
+       BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase);
+       BLDJOY(hard_cfg[0], decoded_hcfg.joystick);
+       BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom);
+       BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase);
+
+#if defined(AEDSP16_DEBUG)
+       aedsp16_pinfo();
+#endif
+
+       DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+       DBG(("success.\n"));
+
+}
+
+static int aedsp16_hard_write(int port) {
+
+       DBG(("aedsp16_hard_write:\n"));
+
+       if (aedsp16_write(port, COMMAND_6C)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C);
+               DBG(("failure.\n"));
+               return FALSE;
+       }
+       if (aedsp16_write(port, COMMAND_5C)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+               DBG(("failure.\n"));
+               return FALSE;
+       }
+       if (aedsp16_write(port, hard_cfg[0])) {
+               printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]);
+               DBG(("failure.\n"));
+               return FALSE;
+       }
+       if (aedsp16_write(port, hard_cfg[1])) {
+               printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]);
+               DBG(("failure.\n"));
+               return FALSE;
+       }
+       if (aedsp16_write(port, COMMAND_C5)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5);
+               DBG(("failure.\n"));
+               return FALSE;
+       }
+
+       DBG(("success.\n"));
+
+       return TRUE;
+}
+
+static int aedsp16_hard_read(int port) {
+
+       DBG(("aedsp16_hard_read:\n"));
+
+       if (aedsp16_write(port, READ_HARD_CFG)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG);
+               DBG(("failure.\n"));
+               return FALSE;
+       }
+
+       if ((hard_cfg[0] = aedsp16_read(port)) == -1) {
+               printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+                       READ_HARD_CFG);
+               DBG(("failure.\n"));
+               return FALSE;
+       }
+       if ((hard_cfg[1] = aedsp16_read(port)) == -1) {
+               printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+                       READ_HARD_CFG);
+               DBG(("failure.\n"));
+               return FALSE;
+       }
+       if (aedsp16_read(port) == -1) {
+               printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+                       READ_HARD_CFG);
+               DBG(("failure.\n"));
+               return FALSE;
+       }
+
+       DBG(("success.\n"));
+
+       return TRUE;
+}
+
+static int aedsp16_ext_cfg_write(int port) {
+
+       int extcfg, val;
+
+       if (aedsp16_write(port, COMMAND_66)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66);
+               return FALSE;
+       }
+
+       extcfg = 7;
+       if (decoded_hcfg.cdrom != 2)
+               extcfg = 0x0F;
+       if ((decoded_hcfg.cdrom == 4) ||
+           (decoded_hcfg.cdrom == 3))
+               extcfg &= ~2;
+       if (decoded_hcfg.cdrombase == 0)
+               extcfg &= ~2;
+       if (decoded_hcfg.mpubase == 0)
+               extcfg &= ~1;
+
+       if (aedsp16_write(port, extcfg)) {
+               printk("[AEDSP16] Write extcfg: failed!\n");
+               return FALSE;
+       }
+       if (aedsp16_write(port, 0)) {
+               printk("[AEDSP16] Write extcfg: failed!\n");
+               return FALSE;
+       }
+       if (decoded_hcfg.cdrom == 3) {
+               if (aedsp16_write(port, COMMAND_52)) {
+                       printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
+                       return FALSE;
+               }
+               if ((val = aedsp16_read(port)) == -1) {
+                       printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n"
+                                       , COMMAND_52);
+                       return FALSE;
+               }
+               val &= 0x7F;
+               if (aedsp16_write(port, COMMAND_60)) {
+                       printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
+                       return FALSE;
+               }
+               if (aedsp16_write(port, val)) {
+                       printk("[AEDSP16] Write val: failed!\n");
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+#endif /* CONFIG_SC6600 */
+
+static int aedsp16_cfg_write(int port) {
+       if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+               return FALSE;
+       }
+       if (aedsp16_write(port, soft_cfg)) {
+               printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
+               return FALSE;
+       }
+       return TRUE;
+}
+
+#if defined(CONFIG_AEDSP16_MSS)
+
+static int aedsp16_init_mss(int port)
+{
+       DBG(("aedsp16_init_mss:\n"));
+
+       aedsp16_delay_10msec();
+
+       if (aedsp16_write(port, DSP_INIT_MSS)) {
+               printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n",
+                               DSP_INIT_MSS);
+               DBG(("failure.\n"));
+               return FALSE;
+       }
+       aedsp16_delay_10msec();
+
+       if (aedsp16_cfg_write(port) == FALSE)
+               return FALSE;
+
+       outb(soft_cfg_1, MSS_BASE);
+
+       DBG(("success.\n"));
+
+       return TRUE;
+}
+
+#endif /* CONFIG_AEDSP16_MSS */
+
+static int aedsp16_setup_board(int port) {
+       int     loop = RETRY;
+
+#if defined(CONFIG_SC6600)
+       int     val = 0;
+
+       if (aedsp16_hard_read(port) == FALSE) {
+               printk("[AEDSP16] aedsp16_hard_read: failed!\n");
+               return FALSE;
+       }
+
+       if (aedsp16_write(port, COMMAND_52)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
+               return FALSE;
+       }
+
+       if ((val = aedsp16_read(port)) == -1) {
+               printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+                               COMMAND_52);
+               return FALSE;
+       }
+#endif
+
+       do {
+               if (aedsp16_write(port, COMMAND_88)) {
+                       printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88);
+                       return FALSE;
+               }
+               aedsp16_delay_10msec();
+       } while ((aedsp16_wait_data(port) == FALSE) && loop--);
+
+       if (aedsp16_read(port) == -1) {
+               printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+                               COMMAND_88);
+               return FALSE;
+       }
+
+#if !defined(CONFIG_SC6600)
+       if (aedsp16_write(port, COMMAND_5C)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+               return FALSE;
+       }
+#endif
+
+       if (aedsp16_cfg_write(port) == FALSE)
+               return FALSE;
+
+#if defined(CONFIG_SC6600)
+       if (aedsp16_write(port, COMMAND_60)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
+               return FALSE;
+       }
+       if (aedsp16_write(port, val)) {
+               printk("[AEDSP16] DATA 0x%x: failed!\n", val);
+               return FALSE;
+       }
+       if (aedsp16_write(port, COMMAND_6E)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E);
+               return FALSE;
+       }
+       if (aedsp16_write(port, ver[0])) {
+               printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]);
+               return FALSE;
+       }
+       if (aedsp16_write(port, ver[1])) {
+               printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]);
+               return FALSE;
+       }
+
+       if (aedsp16_hard_write(port) == FALSE) {
+               printk("[AEDSP16] aedsp16_hard_write: failed!\n");
+               return FALSE;
+       }
+
+       if (aedsp16_write(port, COMMAND_5C)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+               return FALSE;
+       }
+
+#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET)
+       if (aedsp16_cfg_write(port) == FALSE)
+               return FALSE;
+#endif
+
+#endif
+
+       return TRUE;
+}
+
+static int aedsp16_stdcfg(int port) {
+       if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+               return FALSE;
+       }
+       /*
+        * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
+        */
+       if (aedsp16_write(port, 0x0A)) {
+               printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static int aedsp16_dsp_version(int port)
+{
+       int             len = 0;
+       int             ret;
+
+       DBG(("Get DSP Version:\n"));
+
+       if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION);
+               DBG(("failed.\n"));
+               return FALSE;
+       }
+
+       do {
+               if ((ret = aedsp16_read(port)) == -1) {
+                       DBG(("failed.\n"));
+                       return FALSE;
+               }
+       /*
+        * We already know how many int are stored (2), so we know when the
+        * string is finished.
+        */
+               ver[len++] = ret;
+         } while (len < CARDVERLEN);
+       sprintf(DSPVersion, "%d.%d", ver[0], ver[1]);
+
+       DBG(("success.\n"));
+
+       return TRUE;
+}
+
+static int aedsp16_dsp_copyright(int port)
+{
+       int             len = 0;
+       int             ret;
+
+       DBG(("Get DSP Copyright:\n"));
+
+       if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) {
+               printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT);
+               DBG(("failed.\n"));
+               return FALSE;
+       }
+
+       do {
+               if ((ret = aedsp16_read(port)) == -1) {
+       /*
+        * If no more data available, return to the caller, no error if len>0.
+        * We have no other way to know when the string is finished.
+        */
+                       if (len)
+                               break;
+                       else {
+                               DBG(("failed.\n"));
+                               return FALSE;
+                       }
+               }
+
+               DSPCopyright[len++] = ret;
+
+         } while (len < CARDNAMELEN);
+
+       DBG(("success.\n"));
+
+       return TRUE;
+}
+
+static void aedsp16_init_tables(void)
+{
+       memset(DSPCopyright, 0, CARDNAMELEN + 1);
+       memset(DSPVersion, 0, CARDVERLEN + 1);
+
+       for (gc = 0; orIRQ[gc].or; gc++)
+               if (orIRQ[gc].val == ae_config.irq) {
+                       soft_cfg |= orIRQ[gc].or;
+                       soft_cfg_1 |= orIRQ[gc].or;
+               }
+
+       for (gc = 0; orMIRQ[gc].or; gc++)
+               if (orMIRQ[gc].or == ae_config.mpu_irq)
+                       soft_cfg |= orMIRQ[gc].or;
+
+       for (gc = 0; orDMA[gc].or; gc++)
+               if (orDMA[gc].val == ae_config.dma) {
+                       soft_cfg |= orDMA[gc].or;
+                       soft_cfg_1 |= orDMA[gc].or;
+               }
+}
+
+static int aedsp16_init_board(void)
+{
+       aedsp16_init_tables();
+
+       if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) {
+               printk("[AEDSP16] aedsp16_dsp_reset: failed!\n");
+               return FALSE;
+       }
+       if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) {
+               printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n");
+               return FALSE;
+       }
+
+       /*
+        * My AEDSP16 card return SC-6000 in DSPCopyright, so
+        * if we have something different, we have to be warned.
+        */
+       if (strcmp("SC-6000", DSPCopyright))
+               printk("[AEDSP16] Warning: non SC-6000 audio card!\n");
+
+       if (aedsp16_dsp_version(ae_config.base_io) == FALSE) {
+               printk("[AEDSP16] aedsp16_dsp_version: failed!\n");
+               return FALSE;
+       }
+
+       if (aedsp16_stdcfg(ae_config.base_io) == FALSE) {
+               printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
+               return FALSE;
+       }
+
+#if defined(CONFIG_SC6600)
+       if (aedsp16_hard_read(ae_config.base_io) == FALSE) {
+               printk("[AEDSP16] aedsp16_hard_read: failed!\n");
+               return FALSE;
+       }
+
+       aedsp16_hard_decode();
+
+       aedsp16_hard_encode();
+
+       if (aedsp16_hard_write(ae_config.base_io) == FALSE) {
+               printk("[AEDSP16] aedsp16_hard_write: failed!\n");
+               return FALSE;
+       }
+
+       if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) {
+               printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n");
+               return FALSE;
+       }
+#endif /* CONFIG_SC6600 */
+
+       if (aedsp16_setup_board(ae_config.base_io) == FALSE) {
+               printk("[AEDSP16] aedsp16_setup_board: failed!\n");
+               return FALSE;
+       }
+
+#if defined(CONFIG_AEDSP16_MSS)
+       if (ae_config.init & INIT_MSS) {
+               if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
+                       printk(
+               "[AEDSP16] Can not initialize Microsoft Sound System mode.\n");
+                       return FALSE;
+               }
+       }
+#endif
+
+#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+
+       printk("Audio Excel DSP 16 init v%s (%s %s) [",
+               VERSION, DSPCopyright,
+               DSPVersion);
+
+#if defined(CONFIG_AEDSP16_MPU401)
+       if (ae_config.init & INIT_MPU401) {
+               printk("MPU401");
+               if ((ae_config.init & INIT_MSS) ||
+                   (ae_config.init & INIT_SBPRO))
+                       printk(" ");
+       }
+#endif
+
+#if defined(CONFIG_AEDSP16_SBPRO)
+       if (ae_config.init & INIT_SBPRO) {
+               printk("SBPro");
+               if (ae_config.init & INIT_MSS)
+                       printk(" ");
+       }
+#endif
+
+#if defined(CONFIG_AEDSP16_MSS)
+       if (ae_config.init & INIT_MSS)
+               printk("MSS");
+#endif
+
+       printk("]\n");
+#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */
+
+       aedsp16_delay_10msec();
+
+       return TRUE;
+}
+
+#if defined(CONFIG_AEDSP16_SBPRO)
+
+static int init_aedsp16_sb(void)
+{
+       DBG(("init_aedsp16_sb: "));
+
+/*
+ * If the card is already init'ed MSS, we can not init it to SBPRO too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+       if (ae_config.init & INIT_MSS)
+               return FALSE;
+       if (ae_config.init & INIT_SBPRO)
+               return FALSE;
+
+       ae_config.init |= INIT_SBPRO;
+
+       DBG(("done.\n"));
+
+       return TRUE;
+}
+
+static void uninit_aedsp16_sb(void)
+{
+       DBG(("uninit_aedsp16_sb: "));
+
+       ae_config.init &= ~INIT_SBPRO;
+
+       DBG(("done.\n"));
+}
+#endif
+
+#if defined(CONFIG_AEDSP16_MSS)
+
+static int init_aedsp16_mss(void)
+{
+       DBG(("init_aedsp16_mss: "));
+
+/*
+ * If the card is already init'ed SBPRO, we can not init it to MSS too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+       if (ae_config.init & INIT_SBPRO)
+               return FALSE;
+       if (ae_config.init & INIT_MSS)
+               return FALSE;
+/*
+ * We must check the AEDSP16_BASE region too because these are the I/O ports
+ * to access card's control registers.
+ */
+       if (!(ae_config.init & INIT_MPU401)) {
+               if (check_region(ae_config.base_io, IOBASE_REGION_SIZE)) {
+                       printk(
+                       "AEDSP16 BASE I/O port region is already in use.\n");
+                       return FALSE;
+               }
+       }
+
+/*
+ * We must allocate the AEDSP16_BASE region too because these are the I/O ports
+ * to access card's control registers.
+ */
+       if (!(ae_config.init & INIT_MPU401))
+               request_region(ae_config.base_io, IOBASE_REGION_SIZE,
+                               "aedsp16 (base)");
+
+       ae_config.init |= INIT_MSS;
+
+       DBG(("done.\n"));
+
+       return TRUE;
+}
+
+static void uninit_aedsp16_mss(void)
+{
+       DBG(("uninit_aedsp16_mss: "));
+
+       if ((!(ae_config.init & INIT_MPU401)) &&
+          (ae_config.init & INIT_MSS)) {
+               release_region(ae_config.base_io, IOBASE_REGION_SIZE);
+               DBG(("AEDSP16 base region released.\n"));
+       }
+
+       ae_config.init &= ~INIT_MSS;
+       DBG(("done.\n"));
+}
+#endif
+
+#if defined(CONFIG_AEDSP16_MPU401)
+
+static int init_aedsp16_mpu(void)
+{
+       DBG(("init_aedsp16_mpu: "));
+
+       if (ae_config.init & INIT_MPU401)
+               return FALSE;
+
+/*
+ * We must check the AEDSP16_BASE region too because these are the I/O ports
+ * to access card's control registers.
+ */
+       if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) {
+               if (check_region(ae_config.base_io, IOBASE_REGION_SIZE)) {
+                       printk(
+                       "AEDSP16 BASE I/O port region is already in use.\n");
+                       return FALSE;
+               }
+       }
+
+       if (!(ae_config.init & (INIT_MSS | INIT_SBPRO)))
+               request_region(ae_config.base_io, IOBASE_REGION_SIZE,
+                               "aedsp16 (base)");
+
+       ae_config.init |= INIT_MPU401;
+
+       DBG(("done.\n"));
+
+       return TRUE;
+}
+
+static void uninit_aedsp16_mpu(void)
+{
+       DBG(("uninit_aedsp16_mpu: "));
+
+       if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) &&
+          (ae_config.init & INIT_MPU401)) {
+               release_region(ae_config.base_io, IOBASE_REGION_SIZE);
+               DBG(("AEDSP16 base region released.\n"));
+       }
+
+       ae_config.init &= ~INIT_MPU401;
+
+       DBG(("done.\n"));
+}
+#endif
+
+int init_aedsp16(void)
+{
+       int initialized = FALSE;
+
+       ae_config.base_io = AEDSP16_BASE;
+#if defined(CONFIG_AEDSP16_SBPRO)
+       ae_config.irq = AEDSP16_SBC_IRQ;
+       ae_config.dma = AEDSP16_SBC_DMA;
+#endif
+#if defined(CONFIG_AEDSP16_MSS)
+       ae_config.irq = AEDSP16_MSS_IRQ;
+       ae_config.dma = AEDSP16_MSS_DMA;
+#endif
+#if defined(CONFIG_AEDSP16_MPU401)
+       ae_config.mpu_irq = AEDSP16_MPU_IRQ;
+#endif
+
+       DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n",
+            ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq));
+
+#if defined(CONFIG_AEDSP16_SBPRO)
+       if (init_aedsp16_sb() == FALSE)
+               uninit_aedsp16_sb();
+       else
+               initialized = TRUE;
+#endif
+
+#if defined(CONFIG_AEDSP16_MPU401)
+       if (init_aedsp16_mpu() == FALSE)
+               uninit_aedsp16_mpu();
+       else
+               initialized = TRUE;
+#endif
+
+#if defined(CONFIG_AEDSP16_MSS)
+
+/*
+ * In the sequence of init routines, the MSS init MUST be the last!
+ * This because of the special register programming the MSS mode needs.
+ * A board reset would disable the MSS mode restoring the default SBPRO
+ * mode.
+ */
+       if (init_aedsp16_mss() == FALSE)
+               uninit_aedsp16_mss();
+       else
+               initialized = TRUE;
+#endif
+
+       if (initialized)
+               initialized = aedsp16_init_board();
+       return initialized;
+}
+
+void uninit_aedsp16(void)
+{
+#if defined(CONFIG_AEDSP16_MSS)
+       uninit_aedsp16_mss();
+#endif
+
+#if defined(CONFIG_AEDSP16_SBPRO)
+       uninit_aedsp16_sb();
+#endif
+
+#if defined(CONFIG_AEDSP16_MPU401)
+       uninit_aedsp16_mpu();
+#endif
+}
+#endif                         /* CONFIG_AEDSP16 */
diff --git a/drivers/sound/lowlevel/awe_config.h b/drivers/sound/lowlevel/awe_config.h
new file mode 100644 (file)
index 0000000..c80eb3f
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * sound/awe_config.h
+ *
+ * Configuration of AWE32 sound driver
+ *   version 0.2.99e; Dec. 10, 1997
+ *
+ * Copyright (C) 1996,1997 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
+ * 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 program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef AWE_CONFIG_H_DEF
+#define AWE_CONFIG_H_DEF
+
+/*----------------------------------------------------------------
+ * system configuration
+ *----------------------------------------------------------------*/
+
+/* if you're using obsolete VoxWare 3.0.x on Linux 1.2.x (or FreeBSD),
+ * define the following line.
+ */
+#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
+
+/* if your system doesn't support patch manager (OSS 3.7 or newer),
+ * define the following line.
+ */
+#define AWE_NO_PATCHMGR
+
+/*----------------------------------------------------------------
+ * AWE32 card configuration:
+ * uncomment the following lines only when auto detection doesn't
+ * work properly on your machine.
+ *----------------------------------------------------------------*/
+
+/*#define AWE_DEFAULT_BASE_ADDR        0x620*/ /* base port address */
+/*#define AWE_DEFAULT_MEM_SIZE 512*/   /* kbytes */
+
+
+/*----------------------------------------------------------------
+ * 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.
+ *----------------------------------------------------------------*/
+
+#define AWE_MAX_SAMPLES 400
+#define AWE_MAX_INFOS 1500
+
+
+/*----------------------------------------------------------------
+ * chorus & reverb effects send for FM chip: from 0 to 0xff
+ * larger numbers often cause weird sounds.
+ *----------------------------------------------------------------*/
+
+#define DEF_FM_CHORUS_DEPTH    0x10
+#define DEF_FM_REVERB_DEPTH    0x10
+
+
+/*----------------------------------------------------------------*
+ * other compile conditions
+ *----------------------------------------------------------------*/
+
+/* initialize FM passthrough even without extended RAM */
+#undef 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
+
+
+#ifdef linux
+/* i tested this only on my linux */
+#define INLINE  __inline__
+#else
+#define INLINE /**/
+#endif
+
+
+/*----------------------------------------------------------------*/
+
+/* reading configuration of sound driver */
+
+#ifdef AWE_OBSOLETE_VOXWARE
+
+#ifdef __FreeBSD__
+#  include <i386/isa/sound/sound_config.h>
+#else
+#  include "sound_config.h"
+#endif
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AWE32)
+#define CONFIG_AWE32_SYNTH
+#endif
+
+#else /* AWE_OBSOLETE_VOXWARE */
+
+#ifdef HAS_LOWLEVEL_H
+#include "lowlevel.h"
+#endif
+
+#include "../sound_config.h"
+
+#endif /* AWE_OBSOLETE_VOXWARE */
+
+
+#endif  /* AWE_CONFIG_H_DEF */
index 3f6d385e2f92bb1048f05360bda0f70906bf9050..6aa151fc1db433fdc2e1e7c10604f7f2d1011f79 100644 (file)
@@ -1,26 +1,29 @@
-/*================================================================
- * AWE32 access routines
- *================================================================*/
-
-#ifndef AWE_HW_H_DEF
-#define AWE_HW_H_DEF
-
-/*
- * user configuration:
- * if auto detection can't work properly, define the following values
- * for your machine.
- */
-/*#define AWE_DEFAULT_BASE_ADDR        0x620*/ /* base port address */
-/*#define AWE_DEFAULT_MEM_SIZE 512*/   /* kbytes */
-
-
 /*
- * maximum size of sample table:
- * if your data overflow, increase the following values.
+ * sound/awe_hw.h
+ *
+ * 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
+ *
+ * Copyright (C) 1996,1997 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
+ * 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 program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-#define AWE_MAX_SAMPLES                400
-#define AWE_MAX_INFOS          900     /* GS presets has 801 infos! */
 
+#ifndef AWE_HW_H_DEF
+#define AWE_HW_H_DEF
 
 /*
  * Emu-8000 control registers
@@ -45,6 +48,7 @@
 #define AWE_HWCF4      awe_cmd_idx(1,9),  Data1        /* DW: config dw 4 */
 #define AWE_HWCF5      awe_cmd_idx(1,10), Data1        /* DW: config dw 5 */
 #define AWE_HWCF6      awe_cmd_idx(1,13), Data1        /* DW: config dw 6 */
+#define AWE_HWCF7      awe_cmd_idx(1,14), Data1        /* DW: config dw 7? (not documented) */
 #define AWE_SMALR      awe_cmd_idx(1,20), Data1        /* DW: sound memory address for left read */
 #define AWE_SMARR      awe_cmd_idx(1,21), Data1        /* DW:    for right read */
 #define AWE_SMALW      awe_cmd_idx(1,22), Data1        /* DW: sound memory address for left write */
@@ -76,7 +80,7 @@
 #define AWE_TREMFRQ(ch)        awe_cmd_idx(4,ch), Data3        /* W: LFO#1 tremolo amount and freq */
 #define AWE_FM2FRQ2(ch)        awe_cmd_idx(5,ch), Data3        /* W: LFO#2 vibrato amount and freq */
 
-/*  used during detection (returns ROM version ?)                    */
+/* used during detection (returns ROM version?; not documented in ADIP) */
 #define AWE_U1         0xE0, Data3       /* (R)(W) used in initialization */
 #define AWE_U2(ch)     0xC0+(ch), Data3  /* (W)(W) used in init envelope  */
 
 #define AWE_MAX_VOICES         32
 #define AWE_NORMAL_VOICES      30      /*30&31 are reserved for DRAM refresh*/
 
+#define AWE_MAX_CHANNELS       32      /* max midi channels (must >= voices) */
+
 #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 */
 
 #endif
diff --git a/drivers/sound/lowlevel/awe_voice.h b/drivers/sound/lowlevel/awe_voice.h
deleted file mode 100644 (file)
index 0aa0ed7..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-#ifndef AWE_VOICE_H
-#define AWE_VOICE_H
-/*================================================================
- * awe_voice.h -- voice information for AWE32 wave table synth
- *     ver.0.2.0; Oct. 16, 1996
- *     copyright (c) 1996  by Takashi Iwai
- *================================================================*/
-
-#ifndef SAMPLE_TYPE_AWE32
-#define SAMPLE_TYPE_AWE32      0x20
-#endif
-
-#ifndef _PATCHKEY
-#define _PATCHKEY(id) ((id<<8)|0xfd)
-#endif
-
-/*----------------------------------------------------------------
- * patch information record
- *----------------------------------------------------------------*/
-
-/* patch interface header: 16 bytes */
-typedef struct awe_patch_info {
-       short key;                      /* use AWE_PATCH here */
-#define AWE_PATCH      _PATCHKEY(0x07)
-
-       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
-
-       short reserved;                 /* word alignment data */
-       char data[0];                   /* patch data follows here */
-} awe_patch_info;
-
-
-/*----------------------------------------------------------------
- * raw voice information record
- *----------------------------------------------------------------*/
-
-/* wave table envelope & effect parameters to control EMU8000 */
-typedef struct _awe_voice_parm {
-       unsigned short moddelay;        /* modulation delay (0x8000) */
-       unsigned short modatkhld;       /* modulation attack & hold time (0x7f7f) */
-       unsigned short moddcysus;       /* modulation decay & sustain (0x7f7f) */
-       unsigned short modrelease;      /* modulation release time (0x807f) */
-       short modkeyhold, modkeydecay;  /* envelope change per key (not used) */
-       unsigned short voldelay;        /* volume delay (0x8000) */
-       unsigned short volatkhld;       /* volume attack & hold time (0x7f7f) */
-       unsigned short voldcysus;       /* volume decay & sustain (0x7f7f) */
-       unsigned short volrelease;      /* volume release time (0x807f) */
-       short volkeyhold, volkeydecay;  /* envelope change per key (not used) */
-       unsigned short lfo1delay;       /* LFO1 delay (0x8000) */
-       unsigned short lfo2delay;       /* LFO2 delay (0x8000) */
-       unsigned short pefe;            /* modulation pitch & cutoff (0x0000) */
-       unsigned short fmmod;           /* LFO1 pitch & cutoff (0x0000) */
-       unsigned short tremfrq;         /* LFO1 volume & freq (0x0000) */
-       unsigned short fm2frq2;         /* LFO2 pitch & freq (0x0000) */
-       unsigned char cutoff;           /* initial cutoff (0xff) */
-       unsigned char filterQ;          /* initial filter Q [0-15] (0x0) */
-       unsigned char chorus;           /* chorus send (0x00) */
-       unsigned char reverb;           /* reverb send (0x00) */
-       unsigned short reserved[4];     /* not used */
-} awe_voice_parm;
-
-/* wave table parameters: 92 bytes */
-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 */
-       short rate_offset;              /* sample rate pitch offset */
-       unsigned short mode;            /* sample mode */
-#define AWE_MODE_ROMSOUND              0x8000
-#define AWE_MODE_STEREO                        1
-#define AWE_MODE_LOOPING               2
-#define AWE_MODE_NORELEASE             4       /* obsolete */
-#define AWE_MODE_INIT_PARM             8
-
-       short root;                     /* midi root key */
-       short tune;                     /* pitch tuning (in cents) */
-       char low, high;                 /* key note range */
-       char vellow, velhigh;           /* velocity range */
-       char fixkey, fixvel;            /* fixed key, velocity */
-       char pan, fixpan;               /* panning, fixed panning */
-       short exclusiveClass;           /* exclusive class (0 = none) */
-       unsigned char amplitude;        /* sample volume (127 max) */
-       unsigned char attenuation;      /* attenuation (0.375dB) */
-       short scaleTuning;              /* pitch scale tuning(%), normally 100 */
-       awe_voice_parm parm;            /* voice envelope parameters */
-       short index;                    /* internal index (set by driver) */
-} awe_voice_info;
-
-/* 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 */
-       awe_voice_info info[0];         /* voice information follows here */
-} awe_voice_rec;
-
-
-/*----------------------------------------------------------------
- * sample wave information
- *----------------------------------------------------------------*/
-
-/* wave table sample header: 32 bytes */
-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) */
-       short checksum_flag;            /* use check sum = 1 */
-       unsigned short mode_flags;      /* mode flags */
-#define AWE_SAMPLE_8BITS       1       /* wave data is 8bits */
-#define AWE_SAMPLE_UNSIGNED    2       /* wave data is unsigned */
-#define AWE_SAMPLE_NO_BLANK    4       /* no blank loop is attached */
-#define AWE_SAMPLE_SINGLESHOT  8       /* single-shot w/o loop */
-#define AWE_SAMPLE_BIDIR_LOOP  16      /* bidirectional looping */
-#define AWE_SAMPLE_STEREO_LEFT 32      /* stereo left sound */
-#define AWE_SAMPLE_STEREO_RIGHT        64      /* stereo right sound */
-       unsigned long checksum;         /* check sum */
-       unsigned short data[0];         /* sample data follows here */
-} awe_sample_info;
-
-
-/*----------------------------------------------------------------
- * awe hardware controls
- *----------------------------------------------------------------*/
-
-#define _AWE_DEBUG_MODE                        0x00
-#define _AWE_REVERB_MODE               0x01
-#define _AWE_CHORUS_MODE               0x02
-#define _AWE_REMOVE_LAST_SAMPLES       0x03
-#define _AWE_INITIALIZE_CHIP           0x04
-#define _AWE_SEND_EFFECT               0x05
-#define _AWE_TERMINATE_CHANNEL         0x06
-#define _AWE_TERMINATE_ALL             0x07
-#define _AWE_INITIAL_VOLUME            0x08
-#define _AWE_SET_GUS_BANK              0x09
-
-#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;\
- _SEQ_ADVBUF(8);}
-
-#define AWE_DEBUG_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0)
-#define AWE_REVERB_MODE(dev,p1)        _AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0)
-#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)
-#define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value)
-#define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0)
-#define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0)
-#define AWE_INITIAL_VOLUME(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 0)
-#define AWE_SET_GUS_BANK(dev,bank) _AWE_CMD(dev, 0, _AWE_SET_GUS_BANK, bank, 0)
-
-/* reverb mode */
-#define        AWE_REVERB_ROOM1        0
-#define AWE_REVERB_ROOM2       1
-#define        AWE_REVERB_ROOM3        2
-#define        AWE_REVERB_HALL1        3
-#define        AWE_REVERB_HALL2        4
-#define        AWE_REVERB_PLATE        5
-#define        AWE_REVERB_DELAY        6
-#define        AWE_REVERB_PANNINGDELAY 7
-
-/* chorus mode */
-#define AWE_CHORUS_1           0
-#define        AWE_CHORUS_2            1
-#define        AWE_CHORUS_3            2
-#define        AWE_CHORUS_4            3
-#define        AWE_CHORUS_FEEDBACK     4
-#define        AWE_CHORUS_FLANGER      5
-#define        AWE_CHORUS_SHORTDELAY   6
-#define        AWE_CHORUS_SHORTDELAY2  7
-
-/* effects */
-enum {
-
-/* modulation envelope parameters */
-/* 0*/ AWE_FX_ENV1_DELAY,      /* WORD: ENVVAL */
-/* 1*/ AWE_FX_ENV1_ATTACK,     /* BYTE: up ATKHLD */
-/* 2*/ AWE_FX_ENV1_HOLD,       /* BYTE: lw ATKHLD */
-/* 3*/ AWE_FX_ENV1_DECAY,      /* BYTE: lw DCYSUS */
-/* 4*/ AWE_FX_ENV1_RELEASE,    /* BYTE: lw DCYSUS */
-/* 5*/ AWE_FX_ENV1_SUSTAIN,    /* BYTE: up DCYSUS */
-/* 6*/ AWE_FX_ENV1_PITCH,      /* BYTE: up PEFE */
-/* 7*/ AWE_FX_ENV1_CUTOFF,     /* BYTE: lw PEFE */
-
-/* volume envelope parameters */
-/* 8*/ AWE_FX_ENV2_DELAY,      /* WORD: ENVVOL */
-/* 9*/ AWE_FX_ENV2_ATTACK,     /* BYTE: up ATKHLDV */
-/*10*/ AWE_FX_ENV2_HOLD,       /* BYTE: lw ATKHLDV */
-/*11*/ AWE_FX_ENV2_DECAY,      /* BYTE: lw DCYSUSV */
-/*12*/ AWE_FX_ENV2_RELEASE,    /* BYTE: lw DCYSUSV */
-/*13*/ AWE_FX_ENV2_SUSTAIN,    /* BYTE: up DCYSUSV */
-       
-/* LFO1 (tremolo & vibrato) parameters */
-/*14*/ AWE_FX_LFO1_DELAY,      /* WORD: LFO1VAL */
-/*15*/ AWE_FX_LFO1_FREQ,       /* BYTE: lo TREMFRQ */
-/*16*/ AWE_FX_LFO1_VOLUME,     /* BYTE: up TREMFRQ */
-/*17*/ AWE_FX_LFO1_PITCH,      /* BYTE: up FMMOD */
-/*18*/ AWE_FX_LFO1_CUTOFF,     /* BYTE: lo FMMOD */
-
-/* LFO2 (vibrato) parameters */
-/*19*/ AWE_FX_LFO2_DELAY,      /* WORD: LFO2VAL */
-/*20*/ AWE_FX_LFO2_FREQ,       /* BYTE: lo FM2FRQ2 */
-/*21*/ AWE_FX_LFO2_PITCH,      /* BYTE: up FM2FRQ2 */
-
-/* Other overall effect parameters */
-/*22*/ AWE_FX_INIT_PITCH,      /* SHORT: pitch offset */
-/*23*/ AWE_FX_CHORUS,          /* BYTE: chorus effects send (0-255) */
-/*24*/ AWE_FX_REVERB,          /* BYTE: reverb effects send (0-255) */
-/*25*/ AWE_FX_CUTOFF,          /* BYTE: up IFATN */
-/*26*/ AWE_FX_FILTERQ,         /* BYTE: up CCCA */
-
-/* Sample / loop offset changes */
-/*27*/ AWE_FX_SAMPLE_START,    /* SHORT: offset */
-/*28*/ AWE_FX_LOOP_START,      /* SHORT: offset */
-/*29*/ AWE_FX_LOOP_END,        /* SHORT: offset */
-/*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 */
-
-       AWE_FX_END,
-};
-
-
-#endif /* AWE_VOICE_H */
index 7b49e9ada31a0c3fca77a5ae0881db5b73edd9bd..740ecb94be0e0cd68a44e7fbdc28af828e1f78ac 100644 (file)
@@ -1,58 +1,54 @@
-/*================================================================
- * awe_wave.c -- driver for AWE32 wave table synth
- *     version 0.2.0; Oct. 16, 1996
- *     copyright (c) 1996  by Takashi Iwai
- *================================================================*/
-
-/* if you're using obsolete VoxWare 3.0.x on Linux 1.2.x (or FreeBSD),
- * uncomment the following line
+/*
+ * sound/awe_wave.c
+ *
+ * The low level driver for the AWE32/Sound Blaster 32 wave table synth.
+ *   version 0.3.1b; Jan. 21, 1997
+ *
+ * Copyright (C) 1996,1997 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
+ * 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 program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/* #define AWE_OBSOLETE_VOXWARE */
 
-#include "lowlevel.h"
-
-#ifdef AWE_OBSOLETE_VOXWARE
-
-#include "sound_config.h"
-#if !defined(EXCLUDE_AWE32)
-#define CONFIG_AWE32_SYNTH
+#define AWEDRV_VERSION         "0.3.1b"
+#ifdef __FreeBSD__
+#  include <i386/isa/sound/awe_config.h>
+#else
+#  include "awe_config.h"
 #endif
 
-#else /* AWE_OBSOLETE_VOXWARE */
-
-#include "../sound_config.h"
-
-#endif /* AWE_OBSOLETE_VOXWARE */
-
-
-/*----------------------------------------------------------------*
- * compile condition
- *----------------------------------------------------------------*/
-
-/* initialize FM passthrough even without extended RAM */
-/*#define AWE_ALWAYS_INIT_FM*/
-
-/* debug on */
-#define AWE_DEBUG_ON
-
-/* verify checksum for uploading samples */
-#define AWE_CHECKSUM_DATA
-#define AWE_CHECKSUM_MEMORY
-
-/* disable interruption during sequencer operation */
-/*#define AWE_NEED_DISABLE_INTR*/
-
 /*----------------------------------------------------------------*/
 
 #ifdef CONFIG_AWE32_SYNTH
 
-#include "awe_hw.h"
-#include "awe_voice.h"
+#ifdef __FreeBSD__
+#  include <i386/isa/sound/awe_hw.h>
+#  include <i386/isa/sound/awe_voice.h>
+#else
+#  include "awe_hw.h"
+#  include <linux/awe_voice.h>
+#endif
 
 #ifdef AWE_OBSOLETE_VOXWARE
-#include "tuning.h"
+#  ifdef __FreeBSD__
+#    define SEQUENCER_C
+#    include <i386/isa/sound/tuning.h>
+#  else
+#    include "tuning.h"
+#  endif
 #else
-#include "../tuning.h"
+#  include "../tuning.h"
 #endif
 
 #ifdef linux
@@ -66,8 +62,8 @@
  * debug message
  *----------------------------------------------------------------*/
 
-#ifdef AWE_DEBUG_ON
 static int debug_mode = 0;
+#ifdef AWE_DEBUG_ON
 #define DEBUG(LVL,XXX) {if (debug_mode > LVL) { XXX; }}
 #define ERRMSG(XXX)    {if (debug_mode) { XXX; }}
 #define FATALERR(XXX)  XXX
@@ -95,6 +91,7 @@ static awe_voice_list *infos;
 
 #define AWE_MAX_PRESETS                256
 #define AWE_DEFAULT_BANK       0
+#define AWE_DRUM_BANK          128
 
 /* preset table index */
 static awe_voice_list *preset_table[AWE_MAX_PRESETS];
@@ -103,35 +100,68 @@ static awe_voice_list *preset_table[AWE_MAX_PRESETS];
  * voice table
  *----------------------------------------------------------------*/
 
-#define AWE_FX_BYTES   ((AWE_FX_END+7)/8)
+/* effects table */
+#define AWE_FX_NBYTES  ((AWE_FX_END+7)/8)
+typedef        struct FX_Rec { /* channel effects */
+       unsigned char flags[AWE_FX_NBYTES];
+       short val[AWE_FX_END];
+} FX_Rec;
 
-typedef struct _voice_info {
-       int state;              /* status (on = 1, off = 0) */
-       int note;               /* midi key (0-127) */
-       int velocity;           /* midi velocity (0-127) */
+
+/* channel parameters */
+typedef struct _awe_chan_info {
+       int bank;               /* current tone bank */
+       int instr;              /* current program */
        int bender;             /* midi pitchbend (-8192 - 8192) */
        int bender_range;       /* midi bender range (x100) */
        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 */
+       FX_Rec fx;              /* 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 */
+
+       int ch;                 /* midi channel */
+       int key;                /* internal key for search */
+       int time;               /* allocated time */
+       awe_chan_info   *cinfo; /* channel info */
+
+       int note;               /* midi key (0-127) */
+       int velocity;           /* midi velocity (0-127) */
+       awe_voice_info *sample; /* assigned voice */
 
        /* EMU8000 parameters */
        int apitch;             /* pitch parameter */
        int avol;               /* volume parameter */
-
-       /* instrument parameters */
-       int bank;               /* current tone bank */
-       int instr;              /* current program */
-       awe_voice_list *vrec;
-       awe_voice_info *sample;
-
-       /* channel effects */
-       unsigned char fx_flags[AWE_FX_BYTES];
-       short fx[AWE_FX_END];
+       int apan;               /* panning parameter */
 } voice_info;
 
+/* voice information */
 static voice_info voices[AWE_MAX_VOICES];
 
+#define IS_NO_SOUND(v) (voices[v].state == AWE_ST_OFF || voices[v].state == AWE_ST_STANDBY)
+#define IS_NO_EFFECT(v)        (voices[v].state != AWE_ST_ON)
+#define IS_PLAYING(v)  (!IS_NO_SOUND(v))
+
+
+/* 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
+
 
 /*----------------------------------------------------------------
  * global variables
@@ -155,12 +185,21 @@ static unsigned short current_sf_id = 0;  /* internal id */
 
 static int reverb_mode = 0;            /* reverb mode */
 static int chorus_mode = 0;            /* chorus mode */
-static unsigned short init_atten = 32;  /* 12dB */
+static unsigned short init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
+
+static int awe_present = FALSE;                /* awe device present? */
+static int awe_busy = FALSE;           /* awe device opened? */
 
-static int awe_present = 0;            /* awe device present? */
-static int awe_busy = 0;               /* awe device opened? */
+#define DEFAULT_DRUM_FLAGS     (1 << 9)
+#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 int current_alloc_time = 0;             /* voice allocation time */
 
 static int awe_gus_bank = AWE_DEFAULT_BANK;    /* GUS default bank number */
+static int awe_exclusive_sound = TRUE;         /* exclusive sound on */
 
 
 static struct synth_info awe_info = {
@@ -202,6 +241,7 @@ static void awe_initialize(void);
 /* set voice parameters */
 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
 static int freq_to_note(int freq);
 static int calc_rate_offset(int Hz);
 /*static int calc_parm_delay(int msec);*/
@@ -209,26 +249,31 @@ static int calc_parm_hold(int msec);
 static int calc_parm_attack(int msec);
 static int calc_parm_decay(int msec);
 static int calc_parm_search(int msec, short *table);
+#endif
 
 /* turn on/off note */
 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);
+static void awe_exclusive_off(int voice, int exclass);
 
 /* calculate voice parameters */
-static void awe_set_pitch(int voice);
-static void awe_set_volume(int voice);
+typedef void (*fx_affect_func)(int voice, int forced);
+static void awe_set_pitch(int voice, int forced);
+static void awe_set_voice_pitch(int voice, int forced);
+static void awe_set_volume(int voice, int forced);
+static void awe_set_voice_vol(int voice, int forced);
 static void awe_set_pan(int voice, int forced);
-static void awe_fx_fmmod(int voice);
-static void awe_fx_tremfrq(int voice);
-static void awe_fx_fm2frq2(int voice);
-static void awe_fx_cutoff(int voice);
-static void awe_fx_initpitch(int voice);
+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, int inst_only);
+static void awe_voice_init(int voice);
 
 /* sequencer interface */
 static int awe_open(int dev, int mode);
@@ -237,6 +282,7 @@ static int awe_ioctl(int dev, unsigned int cmd, caddr_t arg);
 static int awe_kill_note(int dev, int voice, int note, int velocity);
 static int awe_start_note(int dev, int v, int note_num, int volume);
 static int awe_set_instr(int dev, int voice, int instr_no);
+static int awe_set_instr_2(int dev, int voice, int instr_no);
 static void awe_reset(int dev);
 static void awe_hw_control(int dev, unsigned char *event);
 static int awe_load_patch(int dev, int format, const char *addr,
@@ -245,23 +291,35 @@ static void awe_aftertouch(int dev, int voice, int pressure);
 static void awe_controller(int dev, int voice, int ctrl_num, int value);
 static void awe_panning(int dev, int voice, int value);
 static void awe_volume_method(int dev, int mode);
+#ifndef AWE_NO_PATCHMGR
+static int awe_patchmgr(int dev, struct patmgr_info *rec);
+#endif
 static void awe_bender(int dev, int voice, int value);
 static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc);
 static void awe_setup_voice(int dev, int voice, int chn);
 
 /* hardware controls */
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 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_sustain_off(int voice, int forced);
 
 /* voice search */
-static awe_voice_info *awe_search_voice(int voice, int note);
 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 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);
+#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);
@@ -272,6 +330,7 @@ static void awe_init_audio(void);
 static void awe_init_dma(void);
 static void awe_init_array(void);
 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);
@@ -280,6 +339,7 @@ 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 void awe_set_chorus_mode(int mode);
@@ -302,17 +362,6 @@ static void awe_set_reverb_mode(int mode);
 #endif /* AWE_OBSOLETE_VOXWARE */
 
 
-#ifdef AWE_NEED_DISABLE_INTR
-#define DECL_INTR_FLAGS(x)     unsigned long x
-#else
-#undef DISABLE_INTR
-#undef RESTORE_INTR
-#define DECL_INTR_FLAGS(x) /**/
-#define DISABLE_INTR(x) /**/
-#define RESTORE_INTR(x) /**/
-#endif
-
-
 /* macros for Linux and FreeBSD compatibility */
 
 #undef OUTW
@@ -324,6 +373,8 @@ static void awe_set_reverb_mode(int mode);
 #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)      \
@@ -332,8 +383,22 @@ static void awe_set_reverb_mode(int mode);
                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)
@@ -347,6 +412,8 @@ static void awe_set_reverb_mode(int mode);
               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)
 #endif
 
 
@@ -356,6 +423,7 @@ static void awe_set_reverb_mode(int mode);
 
 static struct synth_operations awe_operations =
 {
+       "EMU8K",
        &awe_info,
        0,
        SYNTH_TYPE_SAMPLE,
@@ -365,7 +433,7 @@ static struct synth_operations awe_operations =
        awe_ioctl,
        awe_kill_note,
        awe_start_note,
-       awe_set_instr,
+       awe_set_instr_2,
        awe_reset,
        awe_hw_control,
        awe_load_patch,
@@ -373,6 +441,9 @@ static struct synth_operations awe_operations =
        awe_controller,
        awe_panning,
        awe_volume_method,
+#ifndef AWE_NO_PATCHMGR
+       awe_patchmgr,
+#endif
        awe_bender,
        awe_alloc,
        awe_setup_voice
@@ -404,7 +475,7 @@ int attach_awe(void)
 
        /* allocate sample tables */
        PERMANENT_MALLOC(awe_sample_info *, samples,
-                        AWE_MAX_SAMPLES * sizeof(awe_sample_info), mem_start);
+                        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) {
@@ -429,14 +500,21 @@ int attach_awe(void)
        /* intialize AWE32 hardware */
        awe_initialize();
 
-       printk("<AWE32 SynthCard (%dk)>\n", (int)awe_mem_size/1024);
-       sprintf(awe_info.name, "AWE32 Synth (%dk)", (int)awe_mem_size/1024);
+#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));
+#endif
+       sprintf(awe_info.name, "AWE32 Driver v%s (DRAM %dk)",
+               AWEDRV_VERSION, (int)awe_mem_size/1024);
 
        /* set reverb & chorus modes */
        awe_set_reverb_mode(reverb_mode);
        awe_set_chorus_mode(chorus_mode);
 
-       awe_present = 1;
+       awe_present = TRUE;
 
 #ifdef AWE_OBSOLETE_VOXWARE
        return mem_start;
@@ -491,11 +569,13 @@ awe_reset_samples(void)
  *================================================================*/
 
 /* select a given AWE32 pointer */
-#define awe_set_cmd(cmd)       OUTW(cmd, awe_base + 0x802)
+static int awe_cur_cmd = -1;
+#define awe_set_cmd(cmd) \
+if (awe_cur_cmd != cmd) { OUTW(cmd, awe_base + 0x802); awe_cur_cmd = cmd; }
 #define awe_port(port)         (awe_base - 0x620 + port)
 
 /* write 16bit data */
-static void
+INLINE static void
 awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
 {
        awe_set_cmd(cmd);
@@ -503,7 +583,7 @@ awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
 }
 
 /* write 32bit data */
-static void
+INLINE static void
 awe_poke_dw(unsigned short cmd, unsigned short port, unsigned long data)
 {
        awe_set_cmd(cmd);
@@ -512,7 +592,7 @@ awe_poke_dw(unsigned short cmd, unsigned short port, unsigned long data)
 }
 
 /* read 16bit data */
-static unsigned short
+INLINE static unsigned short
 awe_peek(unsigned short cmd, unsigned short port)
 {
        unsigned short k;
@@ -522,7 +602,7 @@ awe_peek(unsigned short cmd, unsigned short port)
 }
 
 /* read 32bit data */
-static unsigned long
+INLINE static unsigned long
 awe_peek_dw(unsigned short cmd, unsigned short port)
 {
        unsigned long k1, k2;
@@ -567,25 +647,25 @@ awe_wait(unsigned short delay)
 static int
 awe_check_port(void)
 {
-       return (check_region(awe_port(Data0), 3) ||
-               check_region(awe_port(Data1), 3) ||
-               check_region(awe_port(Data3), 3));
+       return (check_region(awe_port(Data0), 4) ||
+               check_region(awe_port(Data1), 4) ||
+               check_region(awe_port(Data3), 4));
 }
 
 static void
 awe_request_region(void)
 {
-       request_region(awe_port(Data0), 3, "sound driver (AWE32)");
-       request_region(awe_port(Data1), 3, "sound driver (AWE32)");
-       request_region(awe_port(Data3), 3, "sound driver (AWE32)");
+       request_region(awe_port(Data0), 4, "sound driver (AWE32)");
+       request_region(awe_port(Data1), 4, "sound driver (AWE32)");
+       request_region(awe_port(Data3), 4, "sound driver (AWE32)");
 }
 
 static void
 awe_release_region(void)
 {
-       release_region(awe_port(Data0), 3);
-       release_region(awe_port(Data1), 3);
-       release_region(awe_port(Data3), 3);
+       release_region(awe_port(Data0), 4);
+       release_region(awe_port(Data1), 4);
+       release_region(awe_port(Data3), 4);
 }
 
 #endif /* !AWE_OBSOLETE_VOXWARE */
@@ -597,30 +677,22 @@ awe_release_region(void)
 static void
 awe_initialize(void)
 {
-       unsigned short data;
-       DECL_INTR_FLAGS(flags);
-
        DEBUG(0,printk("AWE32: initializing..\n"));
-       DISABLE_INTR(flags);
-
-       /* check for an error condition */
-       data = awe_peek(AWE_U1);
-       if (!(data & 0x000F) == 0x000C) {
-               FATALERR(printk("AWE32: can't initialize AWE32\n"));
-       }
 
        /* initialize hardware configuration */
        awe_poke(AWE_HWCF1, 0x0059);
        awe_poke(AWE_HWCF2, 0x0020);
 
-       /* disable audio output */
-       awe_poke(AWE_HWCF3, 0x0000);
+       /* disable audio; this seems to reduce a clicking noise a bit.. */
+       awe_poke(AWE_HWCF3, 0);
 
        /* initialize audio channels */
        awe_init_audio();
 
-       /* initialize init array */
+       /* initialize DMA */
        awe_init_dma();
+
+       /* initialize init array */
        awe_init_array();
 
        /* check DRAM memory size */
@@ -634,13 +706,6 @@ awe_initialize(void)
 
        /* enable audio */
        awe_poke(AWE_HWCF3, 0x0004);
-
-       data = awe_peek(AWE_HWCF2);
-       if (~data & 0x40) {
-               FATALERR(printk("AWE32: Unable to initialize AWE32.\n"));
-       }
-
-       RESTORE_INTR(flags);
 }
 
 
@@ -720,6 +785,8 @@ awe_init_voice_parm(awe_voice_parm *pp)
 }      
 
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
 /* convert frequency mHz to abstract cents (= midi key * 100) */
 static int
 freq_to_note(int mHz)
@@ -811,27 +878,27 @@ calc_rate_offset(int Hz)
  * convert envelope time parameter to AWE32 raw parameter
  *----------------------------------------------------------------*/
 
-/* attack & decay/release time table (mHz) */
+/* attack & decay/release time table (msec) */
 static short attack_time_tbl[128] = {
-32767, 5939, 3959, 2969, 2375, 1979, 1696, 1484, 1319, 1187, 1079, 989, 913, 848, 791, 742,
+32767, 11878, 5939, 3959, 2969, 2375, 1979, 1696, 1484, 1319, 1187, 1079, 989, 913, 848, 791, 742,
  698, 659, 625, 593, 565, 539, 516, 494, 475, 456, 439, 424, 409, 395, 383, 371,
  359, 344, 330, 316, 302, 290, 277, 266, 255, 244, 233, 224, 214, 205, 196, 188,
  180, 173, 165, 158, 152, 145, 139, 133, 127, 122, 117, 112, 107, 103, 98, 94,
  90, 86, 83, 79, 76, 73, 69, 67, 64, 61, 58, 56, 54, 51, 49, 47,
  45, 43, 41, 39, 38, 36, 35, 33, 32, 30, 29, 28, 27, 25, 24, 23,
  22, 21, 20, 20, 19, 18, 17, 16, 16, 15, 14, 14, 13, 13, 12, 11,
- 11, 10, 10, 10, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 0,
+ 11, 10, 10, 10, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 0,
 };
 
 static short decay_time_tbl[128] = {
-32767, 3651, 3508, 3371, 3239, 3113, 2991, 2874, 2761, 2653, 2550, 2450, 2354, 2262, 2174, 2089,
- 2007, 1928, 1853, 1781, 1711, 1644, 1580, 1518, 1459, 1401, 1347, 1294, 1243, 1195, 1148, 1103,
- 1060, 1018, 979, 940, 904, 868, 834, 802, 770, 740, 711, 683, 657, 631, 606, 582,
560, 538, 517, 496, 477, 458, 440, 423, 407, 391, 375, 361, 347, 333, 320, 307,
295, 284, 273, 262, 252, 242, 232, 223, 215, 206, 198, 190, 183, 176, 169, 162,
- 156, 150, 144, 138, 133, 128, 123, 118, 113, 109, 104, 100, 96, 93, 89, 85,
- 82, 79, 76, 73, 70, 67, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45,
- 43, 41, 40, 38, 37, 35, 34, 32, 31, 30, 29, 28, 27, 25, 24, 0,
+32767, 32766, 4589, 4400, 4219, 4045, 3879, 3719, 3566, 3419, 3279, 3144, 3014, 2890, 2771, 2657,
+ 2548, 2443, 2343, 2246, 2154, 2065, 1980, 1899, 1820, 1746, 1674, 1605, 1539, 1475, 1415, 1356,
+ 1301, 1247, 1196, 1146, 1099, 1054, 1011, 969, 929, 891, 854, 819, 785, 753, 722, 692,
664, 636, 610, 585, 561, 538, 516, 494, 474, 455, 436, 418, 401, 384, 368, 353,
339, 325, 311, 298, 286, 274, 263, 252, 242, 232, 222, 213, 204, 196, 188, 180,
+ 173, 166, 159, 152, 146, 140, 134, 129, 123, 118, 113, 109, 104, 100, 96, 92,
+ 88, 84, 81, 77, 74, 71, 68, 65, 63, 60, 58, 55, 53, 51, 49, 47,
+ 45, 43, 41, 39, 38, 36, 35, 33, 32, 30, 29, 28, 27, 26, 25, 24,
 };
 
 /*
@@ -842,6 +909,7 @@ calc_parm_delay(int msec)
 }
 */
 
+/* delay time = 0x8000 - msec/92 */
 static int
 calc_parm_hold(int msec)
 {
@@ -851,22 +919,25 @@ calc_parm_hold(int msec)
        return val;
 }
 
+/* attack time: search from time table */
 static int
 calc_parm_attack(int msec)
 {
        return calc_parm_search(msec, attack_time_tbl);
 }
 
+/* decay/release time: search from time table */
 static int
 calc_parm_decay(int msec)
 {
        return calc_parm_search(msec, decay_time_tbl);
 }
 
+/* search an index for specified time from given time table */
 static int
 calc_parm_search(int msec, short *table)
 {
-       int left = 0, right = 127, mid;
+       int left = 1, right = 127, mid;
        while (left < right) {
                mid = (left + right) / 2;
                if (msec < (int)table[mid])
@@ -876,6 +947,7 @@ calc_parm_search(int msec, short *table)
        }
        return left;
 }
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
 
 
 /*================================================================
@@ -883,103 +955,53 @@ calc_parm_search(int msec, short *table)
  *================================================================*/
 
 /* set an effect value */
-#define FX_SET(v,type,value) \
-(voices[v].fx_flags[(type)/8] |= (1<<((type)%8)),\
- voices[v].fx[type] = (value))
-/* check the effect value is set */
-#define FX_ON(v,type)  (voices[v].fx_flags[(type)/8] & (1<<((type)%8)))
-
-#if 0
-#define FX_BYTE(v,type,value)\
-       (FX_ON(v,type) ? (unsigned char)voices[v].fx[type] :\
-        (unsigned char)(value))
-#define FX_WORD(v,type,value)\
-       (FX_ON(v,type) ? (unsigned short)voices[v].fx[type] :\
-        (unsigned short)(value))
+#define FX_SET(rec,type,value) \
+       ((rec)->flags[(type)/8] |= (1 << ((type) % 8)), \
+        (rec)->val[type] = (value))
 
-#else
+/* check the effect value is set */
+#define FX_ON(rec,type)        ((rec)->flags[(type)/8] & (1<<((type)%8)))
 
 /* get byte effect value */
-static unsigned char FX_BYTE(int v, int type, unsigned char value)
-{
-       unsigned char tmp;
-       if (FX_ON(v,type))
-               tmp = (unsigned char)voices[v].fx[type];
-       else
-               tmp = value;
-       DEBUG(4,printk("AWE32: [-- byte(%d) = %x]\n", type, tmp));
-       return tmp;
-}
-
+#define FX_BYTE(rec,type,value) \
+       (unsigned char)(FX_ON(rec,type) ? (rec)->val[type] : (value))
 /* get word effect value */
-static unsigned short FX_WORD(int v, int type, unsigned short value)
-{
-       unsigned short tmp;
-       if (FX_ON(v,type))
-               tmp = (unsigned short)voices[v].fx[type];
-       else
-               tmp = value;
-       DEBUG(4,printk("AWE32: [-- word(%d) = %x]\n", type, tmp));
-       return tmp;
-}
-
-#endif
+#define FX_WORD(rec,type,value) \
+       (unsigned short)(FX_ON(rec,type) ? (rec)->val[type] : (value))
 
 /* get word (upper=type1/lower=type2) effect value */
-static unsigned short FX_COMB(int v, int type1, int type2, unsigned short value)
+static unsigned short
+FX_COMB(FX_Rec *rec, int type1, int type2, unsigned short value)
 {
        unsigned short tmp;
-       if (FX_ON(v, type1))
-               tmp = (unsigned short)(voices[v].fx[type1]) << 8;
+       if (FX_ON(rec, type1))
+               tmp = (unsigned short)(rec->val[type1]) << 8;
        else
                tmp = value & 0xff00;
-       if (FX_ON(v, type2))
-               tmp |= (unsigned short)(voices[v].fx[type2]) & 0xff;
+       if (FX_ON(rec, type2))
+               tmp |= (unsigned short)(rec->val[type2]) & 0xff;
        else
                tmp |= value & 0xff;
-       DEBUG(4,printk("AWE32: [-- comb(%d/%d) = %x]\n", type1, type2, tmp));
        return tmp;
 }
 
 /* address offset */
 static long
-FX_OFFSET(int voice, int lo, int hi)
+FX_OFFSET(FX_Rec *rec, int lo, int hi, int mode)
 {
-       awe_voice_info *vp;
-       long addr;
-       if ((vp = voices[voice].sample) == NULL || vp->index < 0)
-               return 0;
-
-       addr = 0;
-       if (FX_ON(voice, hi)) {
-               addr = (short)voices[voice].fx[hi];
+       long addr = 0;
+       if (FX_ON(rec, hi)) {
+               addr = (short)rec->val[hi];
                addr = addr << 15;
        }
-       if (FX_ON(voice, lo))
-               addr += (short)voices[voice].fx[lo];
-       if (!(vp->mode & (AWE_SAMPLE_8BITS<<6)))
+       if (FX_ON(rec, lo))
+               addr += (short)rec->val[lo];
+       if (!(mode & AWE_SAMPLE_8BITS))
                addr /= 2;
        return addr;
 }
 
 
-typedef void (*fx_affect_func)(int voice);
-static fx_affect_func fx_realtime[] = {
-       /* env1: delay, attack, hold, decay, release, sustain, pitch, cutoff*/
-       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-       /* env2: delay, attack, hold, decay, release, sustain */
-       NULL, NULL, NULL, NULL, NULL, NULL,
-       /* lfo1: delay, freq, volume, pitch, cutoff */
-       NULL, awe_fx_tremfrq, awe_fx_tremfrq, awe_fx_fmmod, awe_fx_fmmod,
-       /* lfo2: delay, freq, pitch */
-       NULL, awe_fx_fm2frq2, awe_fx_fm2frq2,
-       /* global: initpitch, chorus, reverb, cutoff, filterQ */
-       awe_fx_initpitch, NULL, NULL, awe_fx_cutoff, NULL,
-       /* sample: start, loopstart, loopend */
-       NULL, NULL, NULL,
-};
-
-
 /*================================================================
  * turn on/off sample
  *================================================================*/
@@ -989,8 +1011,8 @@ awe_note_on(int voice)
 {
        unsigned long temp;
        long addr;
-       unsigned short tmp2;
        awe_voice_info *vp;
+       FX_Rec *fx = &voices[voice].cinfo->fx;
 
        /* A voice sample must assigned before calling */
        if ((vp = voices[voice].sample) == NULL || vp->index < 0)
@@ -1005,71 +1027,65 @@ awe_note_on(int voice)
 
        /* modulation & volume envelope */
        awe_poke(AWE_ENVVAL(voice),
-                FX_WORD(voice, AWE_FX_ENV1_DELAY, vp->parm.moddelay));
+                FX_WORD(fx, AWE_FX_ENV1_DELAY, vp->parm.moddelay));
        awe_poke(AWE_ATKHLD(voice),
-                FX_COMB(voice, AWE_FX_ENV1_ATTACK, AWE_FX_ENV1_HOLD,
+                FX_COMB(fx, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
                         vp->parm.modatkhld));
        awe_poke(AWE_DCYSUS(voice),
-                FX_COMB(voice, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
+                FX_COMB(fx, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
                          vp->parm.moddcysus));
        awe_poke(AWE_ENVVOL(voice),
-                FX_WORD(voice, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
+                FX_WORD(fx, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
        awe_poke(AWE_ATKHLDV(voice),
-                FX_COMB(voice, AWE_FX_ENV2_ATTACK, AWE_FX_ENV2_HOLD,
+                FX_COMB(fx, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
                         vp->parm.volatkhld));
        /* decay/sustain parameter for volume envelope must be set at last */
 
        /* pitch offset */
-       awe_poke(AWE_IP(voice), voices[voice].apitch);
-       DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch));
+       awe_set_pitch(voice, TRUE);
 
        /* cutoff and volume */
-       tmp2 = FX_BYTE(voice, AWE_FX_CUTOFF, vp->parm.cutoff);
-       tmp2 = (tmp2 << 8) | voices[voice].avol;
-       awe_poke(AWE_IFATN(voice), tmp2);
+       awe_set_volume(voice, TRUE);
 
        /* modulation envelope heights */
        awe_poke(AWE_PEFE(voice),
-                FX_COMB(voice, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
+                FX_COMB(fx, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
                         vp->parm.pefe));
 
        /* lfo1/2 delay */
        awe_poke(AWE_LFO1VAL(voice),
-                FX_WORD(voice, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
+                FX_WORD(fx, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
        awe_poke(AWE_LFO2VAL(voice),
-                FX_WORD(voice, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
+                FX_WORD(fx, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
 
        /* lfo1 pitch & cutoff shift */
-       awe_poke(AWE_FMMOD(voice),
-                FX_COMB(voice, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
-                        vp->parm.fmmod));
+       awe_fx_fmmod(voice, TRUE);
        /* lfo1 volume & freq */
-       awe_poke(AWE_TREMFRQ(voice),
-                FX_COMB(voice, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
-                        vp->parm.tremfrq));
+       awe_fx_tremfrq(voice, TRUE);
        /* lfo2 pitch & freq */
-       awe_poke(AWE_FM2FRQ2(voice),
-                FX_COMB(voice, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
-                        vp->parm.fm2frq2));
-
+       awe_fx_fm2frq2(voice, TRUE);
        /* pan & loop start */
         awe_set_pan(voice, 1);
 
        /* chorus & loop end (chorus 8bit, MSB) */
        addr = vp->loopend - 1;
-       addr += FX_OFFSET(voice, AWE_FX_LOOP_END,
-                         AWE_FX_COARSE_LOOP_END);
-       temp = FX_BYTE(voice, AWE_FX_CHORUS, vp->parm.chorus);
+       addr += FX_OFFSET(fx, 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;
        awe_poke_dw(AWE_CSL(voice), temp);
+       DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n",
+                      (int)vp->loopend, (int)addr));
 
        /* Q & current address (Q 4bit value, MSB) */
        addr = vp->start - 1;
-       addr += FX_OFFSET(voice, AWE_FX_SAMPLE_START,
-                         AWE_FX_COARSE_SAMPLE_START);
-       temp = FX_BYTE(voice, AWE_FX_FILTERQ, vp->parm.filterQ);
+       addr += FX_OFFSET(fx, 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;
        awe_poke_dw(AWE_CCCA(voice), temp);
+       DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n",
+                      (int)vp->start, (int)addr));
 
        /* reset volume */
        awe_poke_dw(AWE_VTFT(voice), 0x0000FFFF);
@@ -1077,36 +1093,41 @@ awe_note_on(int voice)
 
        /* turn on envelope */
        awe_poke(AWE_DCYSUSV(voice),
-                FX_COMB(voice, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
+                FX_COMB(fx, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
                          vp->parm.voldcysus));
        /* set chorus */
-       temp = FX_BYTE(voice, AWE_FX_REVERB, vp->parm.reverb);
+       temp = FX_BYTE(fx, 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);
-
-       DEBUG(3,printk("AWE32: [-- start=%x loop=%x]\n",
-                      (int)vp->start, (int)vp->loopstart));
 }
 
+
 /* turn off the voice */
 static void
 awe_note_off(int voice)
 {
        awe_voice_info *vp;
        unsigned short tmp;
-       if ((vp = voices[voice].sample) == NULL || !voices[voice].state)
+       FX_Rec *fx = &voices[voice].cinfo->fx;
+       if ((vp = voices[voice].sample) == NULL) {
+               awe_voice_init(voice);
                return;
-       if (FX_ON(voice, AWE_FX_ENV1_RELEASE))
-               tmp = 0x8000 | voices[voice].fx[AWE_FX_ENV1_RELEASE];
+       }
+
+       if (FX_ON(fx, AWE_FX_ENV1_RELEASE))
+               tmp = 0x8000 | fx->val[AWE_FX_ENV1_RELEASE];
        else
                tmp = vp->parm.modrelease;
        awe_poke(AWE_DCYSUS(voice), tmp);
-       if (FX_ON(voice, AWE_FX_ENV2_RELEASE))
-               tmp = 0x8000 | voices[voice].fx[AWE_FX_ENV2_RELEASE];
+       if (FX_ON(fx, AWE_FX_ENV2_RELEASE))
+               tmp = 0x8000 | fx->val[AWE_FX_ENV2_RELEASE];
        else
                tmp = vp->parm.volrelease;
        awe_poke(AWE_DCYSUSV(voice), tmp);
+       voices[voice].state = AWE_ST_OFF;
+
+       awe_voice_init(voice);
 }
 
 /* force to terminate the voice (no releasing echo) */
@@ -1114,29 +1135,27 @@ static void
 awe_terminate(int voice)
 {
        awe_poke(AWE_DCYSUSV(voice), 0x807F);
+       awe_tweak_voice(voice);
+       awe_voice_init(voice);
 }
 
 
 /* turn off other voices with the same exclusive class (for drums) */
 static void
-awe_exclusive_off(int voice)
+awe_exclusive_off(int voice, int exclass)
 {
-       int i, excls;
+       int i;
 
-       if (voices[voice].sample == NULL) /* no sample */
-               return;
-       excls = voices[voice].sample->exclusiveClass;
-       if (excls == 0) /* not exclusive */
+       if (exclass == 0) /* not exclusive */
                return;
 
        /* turn off voices with the same class */
        for (i = 0; i < awe_max_voices; i++) {
-               if (i != voice && voices[voice].state &&
+               if (i != voice && IS_PLAYING(voice) &&
                    voices[i].sample &&
-                   voices[i].sample->exclusiveClass == excls) {
+                   voices[i].sample->exclusiveClass == exclass) {
                        DEBUG(4,printk("AWE32: [exoff(%d)]\n", i));
                        awe_note_off(i);
-                       awe_voice_init(i, 1);
                }
        }
 }
@@ -1148,26 +1167,47 @@ awe_exclusive_off(int voice)
 
 /* change pitch */
 static void
-awe_set_pitch(int voice)
+awe_set_pitch(int voice, int forced)
 {
-       if (!voices[voice].state) return;
+       if (IS_NO_EFFECT(voice) && !forced) return;
        awe_poke(AWE_IP(voice), voices[voice].apitch);
+       DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch));
+}
+
+/* calculate & change pitch */
+static void
+awe_set_voice_pitch(int voice, int forced)
+{
+       awe_calc_pitch(voice);
+       awe_set_pitch(voice, forced);
 }
 
 /* change volume */
 static void
-awe_set_volume(int voice)
+awe_set_volume(int voice, int forced)
 {
        awe_voice_info *vp;
        unsigned short tmp2;
-       if (!voices[voice].state) return;
+       FX_Rec *fx = &voices[voice].cinfo->fx;
+
+       if (IS_NO_EFFECT(voice) && !forced) return;
        if ((vp = voices[voice].sample) == NULL || vp->index < 0)
                return;
-       tmp2 = FX_BYTE(voice, AWE_FX_CUTOFF, vp->parm.cutoff);
+
+       tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff);
        tmp2 = (tmp2 << 8) | voices[voice].avol;
        awe_poke(AWE_IFATN(voice), tmp2);
 }
 
+/* calculate & change volume */
+static void
+awe_set_voice_vol(int voice, int forced)
+{
+       awe_calc_volume(voice);
+       awe_set_volume(voice, forced);
+}
+
+
 /* change pan; this could make a click noise.. */
 static void
 awe_set_pan(int voice, int forced)
@@ -1175,8 +1215,9 @@ awe_set_pan(int voice, int forced)
        unsigned long temp;
        long addr;
        awe_voice_info *vp;
+       FX_Rec *fx = &voices[voice].cinfo->fx;
 
-       if (!voices[voice].state && !forced) return;
+       if (IS_NO_EFFECT(voice) && !forced) return;
        if ((vp = voices[voice].sample) == NULL || vp->index < 0)
                return;
 
@@ -1187,7 +1228,7 @@ awe_set_pan(int voice, int forced)
                int pos = 0;
                if (vp->pan >= 0) /* 0-127 */
                        pos = (int)vp->pan * 2 - 128;
-               pos += voices[voice].panning; /* -128 - 127 */
+               pos += voices[voice].cinfo->panning; /* -128 - 127 */
                pos = 127 - pos;
                if (pos < 0)
                        temp = 0;
@@ -1196,76 +1237,75 @@ awe_set_pan(int voice, int forced)
                else
                        temp = pos;
        }
-       addr = vp->loopstart - 1;
-       addr += FX_OFFSET(voice, AWE_FX_LOOP_START,
-                         AWE_FX_COARSE_LOOP_START);
-       temp = (temp<<24) | (unsigned long)addr;
-       awe_poke_dw(AWE_PSST(voice), temp);
+       if (forced || temp != voices[voice].apan) {
+               addr = vp->loopstart - 1;
+               addr += FX_OFFSET(fx, AWE_FX_LOOP_START,
+                                 AWE_FX_COARSE_LOOP_START, vp->mode);
+               temp = (temp<<24) | (unsigned long)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));
+       }
 }
 
 /* effects change during playing */
 static void
-awe_fx_fmmod(int voice)
+awe_fx_fmmod(int voice, int forced)
 {
        awe_voice_info *vp;
-       if (!voices[voice].state) return;
+       FX_Rec *fx = &voices[voice].cinfo->fx;
+       if (IS_NO_EFFECT(voice) && !forced) return;
        if ((vp = voices[voice].sample) == NULL || vp->index < 0)
                return;
        awe_poke(AWE_FMMOD(voice),
-                FX_COMB(voice, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
+                FX_COMB(fx, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
                         vp->parm.fmmod));
 }
 
+/* set tremolo (lfo1) volume & frequency */
 static void
-awe_fx_tremfrq(int voice)
+awe_fx_tremfrq(int voice, int forced)
 {
        awe_voice_info *vp;
-       if (!voices[voice].state) return;
+       FX_Rec *fx = &voices[voice].cinfo->fx;
+       if (IS_NO_EFFECT(voice) && !forced) return;
        if ((vp = voices[voice].sample) == NULL || vp->index < 0)
                return;
        awe_poke(AWE_TREMFRQ(voice),
-                FX_COMB(voice, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
+                FX_COMB(fx, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
                         vp->parm.tremfrq));
 }
 
+/* set lfo2 pitch & frequency */
 static void
-awe_fx_fm2frq2(int voice)
+awe_fx_fm2frq2(int voice, int forced)
 {
        awe_voice_info *vp;
-       if (!voices[voice].state) return;
+       FX_Rec *fx = &voices[voice].cinfo->fx;
+       if (IS_NO_EFFECT(voice) && !forced) return;
        if ((vp = voices[voice].sample) == NULL || vp->index < 0)
                return;
        awe_poke(AWE_FM2FRQ2(voice),
-                FX_COMB(voice, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
+                FX_COMB(fx, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
                         vp->parm.fm2frq2));
 }
 
+/* set total cutoff  & current pitch */
 static void
-awe_fx_cutoff(int voice)
+awe_fx_cutoff(int voice, int forced)
 {
        unsigned short tmp2;
        awe_voice_info *vp;
-       if (!voices[voice].state) return;
+       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(voice, AWE_FX_CUTOFF, vp->parm.cutoff);
+       tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff);
        tmp2 = (tmp2 << 8) | voices[voice].avol;
        awe_poke(AWE_IFATN(voice), tmp2);
 }
 
-static void
-awe_fx_initpitch(int voice)
-{
-       if (!voices[voice].state) return;
-       if (FX_ON(voice, AWE_FX_INIT_PITCH)) {
-               DEBUG(3,printk("AWE32: initpitch ok\n"));
-       } else {
-               DEBUG(3,printk("AWE32: BAD initpitch %d\n", AWE_FX_INIT_PITCH));
-       }
-       awe_calc_pitch(voice);
-       awe_poke(AWE_IP(voice), voices[voice].apitch);
-}
-
 
 /*================================================================
  * calculate pitch offset
@@ -1279,6 +1319,7 @@ awe_calc_pitch(int voice)
 {
        voice_info *vp = &voices[voice];
        awe_voice_info *ap;
+       awe_chan_info *cp = voices[voice].cinfo;
        int offset;
 
        /* search voice information */
@@ -1300,18 +1341,18 @@ awe_calc_pitch(int voice)
        }
        offset += ap->tune * 4096 / 1200;
        DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset));
-       if (vp->bender != 0) {
-               DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, vp->bender));
+       if (cp->bender != 0) {
+               DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender));
                /* (819200: 1 semitone) ==> (4096: 12 semitones) */
-               offset += vp->bender * vp->bender_range / 2400;
+               offset += cp->bender * cp->bender_range / 2400;
        }
        offset = (offset * ap->scaleTuning) / 100;
        DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset));
 
        /* add initial pitch correction */
-       if (FX_ON(voice, AWE_FX_INIT_PITCH)) {
-               DEBUG(3,printk("AWE32: fx_pitch(%d) %d\n", voice, vp->fx[AWE_FX_INIT_PITCH]));
-               offset += vp->fx[AWE_FX_INIT_PITCH];
+       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]));
+               offset += cp->fx.val[AWE_FX_INIT_PITCH];
        }
 
        /* 0xe000: root pitch */
@@ -1324,11 +1365,14 @@ awe_calc_pitch(int voice)
 }
 
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+/* calculate MIDI key and semitone from the specified frequency */
 static void
 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;
        int offset;
        int note;
 
@@ -1342,14 +1386,16 @@ awe_calc_pitch_from_freq(int voice, int freq)
        note = freq_to_note(freq);
        offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200;
        offset = (offset * ap->scaleTuning) / 100;
-       if (FX_ON(voice, AWE_FX_INIT_PITCH))
-               offset += vp->fx[AWE_FX_INIT_PITCH];
+       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)
                vp->apitch = 0xffff;
        if (vp->apitch < 0)
                vp->apitch = 0;
 }
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
 
 /*================================================================
  * calculate volume attenuation
@@ -1375,6 +1421,7 @@ awe_calc_volume(int voice)
 {
        voice_info *vp = &voices[voice];
        awe_voice_info *ap;
+       awe_chan_info *cp = voices[voice].cinfo;
        int vol;
 
        /* search voice information */
@@ -1387,13 +1434,8 @@ awe_calc_volume(int voice)
                        return;
        }
        
-       if (vp->velocity < ap->vellow)
-               vp->velocity = ap->vellow;
-       else if (vp->velocity > ap->velhigh)
-               vp->velocity = ap->velhigh;
-
        /* 0 - 127 */
-       vol = (vp->velocity * vp->main_vol * vp->expression_vol) / (127*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;
@@ -1412,35 +1454,52 @@ awe_calc_volume(int voice)
  * synth operation routines
  *================================================================*/
 
+#define AWE_VOICE_KEY(v)       (0x8000 | (v))
+#define AWE_CHAN_KEY(c,n)      (((c) << 8) | ((n) + 1))
+
 /* initialize the voice */
 static void
-awe_voice_init(int voice, int inst_only)
-{
-       if (! inst_only) {
-               /* clear voice parameters */
-               voices[voice].note = -1;
-               voices[voice].velocity = 0;
-               voices[voice].panning = 0; /* zero center */
-               voices[voice].bender = 0; /* zero tune skew */
-               voices[voice].bender_range = 200; /* sense * 100 */
-               voices[voice].main_vol = 127;
-               voices[voice].expression_vol = 127;
-               voices[voice].bank = AWE_DEFAULT_BANK;
-               voices[voice].instr = -1;
-               voices[voice].vrec = NULL;
-               voices[voice].sample = NULL;
-       }
+awe_voice_init(int voice)
+{
+       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;
 
        /* clear voice mapping */
-       voices[voice].state = 0;
        voice_alloc->map[voice] = 0;
 
        /* emu8000 parameters */
        voices[voice].apitch = 0;
        voices[voice].avol = 255;
+       voices[voice].apan = -1;
 
        /* clear effects */
-       BZERO(voices[voice].fx_flags, sizeof(voices[voice].fx_flags));
+       if (! awe_channel_mode)
+               BZERO(&voices[voice].cinfo->fx, sizeof(FX_Rec));
+}
+
+/* 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));
 }
 
 
@@ -1457,14 +1516,19 @@ awe_open(int dev, int mode)
        if (awe_busy)
                return RET_ERROR(EBUSY);
 
-       awe_busy = 1;
+       awe_busy = TRUE;
        awe_reset(dev);
 
        /* clear sample position flag */
        loaded_once = 0;
 
-       /* set GUS bank to default */
+       /* set default mode */
+       init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
        awe_gus_bank = AWE_DEFAULT_BANK;
+       drum_flags = DEFAULT_DRUM_FLAGS;
+       awe_exclusive_sound = TRUE;
+       awe_channel_mode = 0;
+
        return 0;
 }
 
@@ -1476,7 +1540,7 @@ static void
 awe_close(int dev)
 {
        awe_reset(dev);
-       awe_busy = 0;
+       awe_busy = FALSE;
 }
 
 
@@ -1494,7 +1558,7 @@ awe_ioctl(int dev, unsigned int cmd, caddr_t arg)
 
        case SNDCTL_SEQ_RESETSAMPLES:
                awe_reset_samples();
-               awe_reset(dev); /* better to reset emu8k chip... */
+               awe_reset(dev);
                return 0;
                break;
 
@@ -1520,88 +1584,139 @@ awe_ioctl(int dev, unsigned int cmd, caddr_t arg)
 static int
 awe_kill_note(int dev, int voice, int note, int velocity)
 {
-       awe_voice_info *vp;
-       DECL_INTR_FLAGS(flags);
+       int i, key, besttime;
 
-       DEBUG(2,printk("AWE32: [off(%d)]\n", voice));
-       if (voice < 0 || voice >= awe_max_voices)
-                     return RET_ERROR(EINVAL);
-       if ((vp = voices[voice].sample) == NULL)
-               return 0;
-       
-       if (!(vp->mode & AWE_MODE_NORELEASE)) {
-               DISABLE_INTR(flags);
-               awe_note_off(voice);
-               RESTORE_INTR(flags);
+       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 < 0 || voice >= AWE_MAX_CHANNELS)
+                       return RET_ERROR(EINVAL);
+               if (channels[voice].instr > 128)
+                       note = channels[voice].instr - 128;
+               key = AWE_CHAN_KEY(voice, note);
+       } else {
+               if (voice < 0 || voice >= awe_max_voices)
+                       return RET_ERROR(EINVAL);
+               key = AWE_VOICE_KEY(voice);
        }
-       awe_voice_init(voice, 1);
-       return 0;
-}
-
-
-/* search the note with the specified key range */
-static awe_voice_info *
-awe_search_voice(int voice, int note)
-{
-       awe_voice_list *rec;
-       int maxc;
 
-       for (rec = voices[voice].vrec, maxc = AWE_MAX_INFOS;
-            rec && maxc; rec = rec->next_instr, maxc--) {
-               if (rec->v.low <= note && note <= rec->v.high)
-                       return &rec->v;
+       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;
+               }
        }
-       return NULL;
+       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);
+               }
+       }
+       return 0;
 }
 
+
 /* start a voice:
  *   if note is 255, identical with aftertouch function.
  *   Otherwise, start a voice with specified not and volume.
  */
 static int
-awe_start_note(int dev, int v, int note_num, int volume)
-{
-       DECL_INTR_FLAGS(flags);
-
-       DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", v, note_num, volume));
-       if (v < 0 || v >= awe_max_voices)
+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 < 0 || voice >= AWE_MAX_CHANNELS)
+                       return RET_ERROR(EINVAL);
+               if (channels[voice].instr > 128)
+                       note = channels[voice].instr - 128;
+               if (note >= 128) { /* key volume mode */
+                       note -= 128;
+                       volonly = 1;
+               }
+               key = AWE_CHAN_KEY(voice, note);
+       } else {
+               if (voice < 0 || voice >= awe_max_voices)
                      return RET_ERROR(EINVAL);
-       /* an instrument must be set before starting a note */
-       if (voices[v].vrec == NULL) {
-               DEBUG(1,printk("AWE32: [-- vrec is null]\n"));
+               if (voices[voice].cinfo->instr > 128)
+                       note = voices[voice].cinfo->instr - 128;
+               key = AWE_VOICE_KEY(voice);
+               if (note == 255)
+                       volonly = 1;
+       }
+
+       /* 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);
+                       }
+               }
                return 0;
        }
 
-       if (note_num == 255) {
-               /* dynamic volume change; sample is already assigned */
-               if (! voices[v].state || voices[v].sample == NULL)
-                       return 0;
-               /* calculate volume parameter */
-               voices[v].velocity = volume;
-               awe_calc_volume(v);
-               DISABLE_INTR(flags);
-               awe_set_volume(v);
-               RESTORE_INTR(flags);
-               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)
+                               awe_note_off(i);
        }
-       /* assign a sample with the corresponding note */
-       if ((voices[v].sample = awe_search_voice(v, note_num)) == NULL) {
-               DEBUG(1,printk("AWE32: [-- sample is null]\n"));
-               return 0;
-       }
-       /* calculate pitch & volume parameters */
-       voices[v].note = note_num;
-       voices[v].velocity = volume;
-       awe_calc_pitch(v);
-       awe_calc_volume(v);
 
-       DISABLE_INTR(flags);
+       /* allocate voices */
+       if (awe_channel_mode)
+               awe_alloc_multi_voices(voice, note, velocity);
+       else
+               awe_alloc_one_voice(voice, note, velocity);
+
        /* turn off other voices (for drums) */
-       awe_exclusive_off(v);
-       /* turn on the voice */
-       awe_note_on(v);
-       voices[v].state = 1;    /* flag up */
-       RESTORE_INTR(flags);
+       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 */
+
+       /* 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);
+               }
 
        return 0;
 }
@@ -1623,30 +1738,60 @@ awe_search_instr(int bank, int preset)
 }
 
 
+/* assign the instrument to a voice */
+static int
+awe_set_instr_2(int dev, int voice, int instr_no)
+{
+       if (awe_channel_mode == 2)
+               voice = voice_alloc->map[voice] >> 8;
+       return awe_set_instr(dev, voice, instr_no);
+}
+
 /* assign the instrument to a voice */
 static int
 awe_set_instr(int dev, int voice, int instr_no)
 {
-       awe_voice_list *rec;
+       awe_chan_info *cinfo;
+       int def_bank;
 
-       if (voice < 0 || voice >= awe_max_voices)
-               return RET_ERROR(EINVAL);
+       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 (instr_no < 0 || instr_no >= AWE_MAX_PRESETS)
                return RET_ERROR(EINVAL);
 
-       if ((rec = awe_search_instr(voices[voice].bank, instr_no)) == NULL) {
-               /* if bank is not defined, use the default bank 0 */
-               if (voices[voice].bank != AWE_DEFAULT_BANK &&
-                   (rec = awe_search_instr(AWE_DEFAULT_BANK, instr_no)) == NULL) {
-                       DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
-                       return 0;
-               }
+       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);
+       }
+       if (cinfo->vrec == NULL && cinfo->def_vrec == NULL) {
+               DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
+               return 0;
        }
 
-       voices[voice].instr = instr_no;
-       voices[voice].vrec = rec;
-       voices[voice].sample = NULL;  /* not set yet */
+       cinfo->instr = instr_no;
 
        return 0;
 }
@@ -1657,10 +1802,15 @@ static void
 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_NORMAL_VOICES; i++)
                awe_terminate(i);
-               awe_voice_init(i, 0);
+       for (i = 0; i < AWE_MAX_CHANNELS; i++)
+               awe_channel_init(i);
+       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;
        }
        awe_init_fm();
        awe_tweak();
@@ -1676,10 +1826,15 @@ awe_hw_control(int dev, unsigned char *event)
        int cmd = event[2];
        if (cmd & _AWE_MODE_FLAG)
                awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
        else
                awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
+#endif
 }
 
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
 /* GUS compatible controls */
 static void
 awe_hw_gus_control(int dev, int cmd, unsigned char *event)
@@ -1688,7 +1843,6 @@ awe_hw_gus_control(int dev, int cmd, unsigned char *event)
        unsigned short p1;
        short p2;
        int plong;
-       DECL_INTR_FLAGS(flags);
 
        voice = event[3];
        p1 = *(unsigned short *) &event[4];
@@ -1698,7 +1852,7 @@ awe_hw_gus_control(int dev, int cmd, unsigned char *event)
        switch (cmd) {
        case _GUS_NUMVOICES:
                if (p1 >= awe_max_voices)
-                       printk("AWE32: num_voices: voices out of range %d\n", p1);
+                       DEBUG(0,printk("AWE32: num_voices: voices out of range %d\n", p1));
                break;
        case _GUS_VOICESAMPLE:
                if (voice < awe_max_voices)
@@ -1706,19 +1860,13 @@ awe_hw_gus_control(int dev, int cmd, unsigned char *event)
                break;
 
        case _GUS_VOICEON:
-               if (voice < awe_max_voices) {
-                       DISABLE_INTR(flags);
+               if (voice < awe_max_voices)
                        awe_note_on(voice);
-                       RESTORE_INTR(flags);
-               }
                break;
                
        case _GUS_VOICEOFF:
-               if (voice < awe_max_voices) {
-                       DISABLE_INTR(flags);
+               if (voice < awe_max_voices)
                        awe_note_off(voice);
-                       RESTORE_INTR(flags);
-               }
                break;
                
        case _GUS_VOICEMODE:
@@ -1726,9 +1874,9 @@ awe_hw_gus_control(int dev, int cmd, unsigned char *event)
                break;
 
        case _GUS_VOICEBALA:
-               /* -128 to 127 */
+               /* 0 to 15 --> -128 to 127 */
                if (voice < awe_max_voices)
-                       awe_panning(dev, voice, (short)p1);
+                       awe_panning(dev, voice, ((int)p1 << 4) - 128);
                break;
 
        case _GUS_VOICEFREQ:
@@ -1754,13 +1902,35 @@ awe_hw_gus_control(int dev, int cmd, unsigned char *event)
 
        case _GUS_VOICE_POS:
                if (voice < awe_max_voices) {
-                       FX_SET(voice, AWE_FX_SAMPLE_START, (short)(plong & 0x7fff));
-                       FX_SET(voice, AWE_FX_COARSE_SAMPLE_START, (plong >> 15) & 0xffff);
+                       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);
                }
                break;
        }
 }
 
+#endif
+
+
+/* converter function table for realtime paramter change */
+
+static fx_affect_func fx_realtime[] = {
+       /* env1: delay, attack, hold, decay, release, sustain, pitch, cutoff*/
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       /* env2: delay, attack, hold, decay, release, sustain */
+       NULL, NULL, NULL, NULL, NULL, NULL,
+       /* lfo1: delay, freq, volume, pitch, cutoff */
+       NULL, awe_fx_tremfrq, awe_fx_tremfrq, awe_fx_fmmod, awe_fx_fmmod,
+       /* 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,
+       /* sample: start, loopstart, loopend */
+       NULL, NULL, NULL,
+};
+
 
 /* AWE32 specific controls */
 static void
@@ -1770,20 +1940,29 @@ awe_hw_awe_control(int dev, int cmd, unsigned char *event)
        unsigned short p1;
        short p2;
        int chn;
+       awe_chan_info *cinfo;
+       int i;
 
        chn = event[1];
        voice = event[3];
        p1 = *(unsigned short *) &event[4];
        p2 = *(short *) &event[6];
        
+       if (awe_channel_mode) {
+               if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+                       return;
+               cinfo = &channels[voice];
+       } else {
+               if (voice < 0 || voice >= awe_max_voices)
+                       return;
+               cinfo = voices[voice].cinfo;
+       }
 
-#ifdef AWE_DEBUG_ON
        switch (cmd) {
        case _AWE_DEBUG_MODE:
                debug_mode = p1;
                printk("AWE32: debug mode = %d\n", debug_mode);
                break;
-#endif
        case _AWE_REVERB_MODE:
                if (p1 <= 7) {
                        reverb_mode = p1;
@@ -1810,32 +1989,36 @@ awe_hw_awe_control(int dev, int cmd, unsigned char *event)
                break;
 
        case _AWE_SEND_EFFECT:
-               if (voice < awe_max_voices && p1 < AWE_FX_END) {
-                       FX_SET(voice, p1, p2);
-                       DEBUG(0,printk("AWE32: effects (%d) %d %d\n", voice, p1, voices[voice].fx[p1]));
+               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]) {
                                DEBUG(0,printk("AWE32: fx_realtime (%d)\n", voice));
-                               fx_realtime[p1](voice);
+                               awe_voice_change(voice, fx_realtime[p1]);
                        }
                }
                break;
 
-       case _AWE_TERMINATE_CHANNEL:
-               if (voice < awe_max_voices) {
-                       DEBUG(0,printk("AWE32: terminate (%d)\n", voice));
-                       awe_terminate(voice);
-                       awe_voice_init(voice, 1);
-               }
-               break;
-
        case _AWE_TERMINATE_ALL:
                DEBUG(0,printk("AWE32: terminate all\n"));
                awe_reset(0);
                break;
 
+       case _AWE_TERMINATE_CHANNEL:
+               awe_voice_change(voice, (fx_affect_func)awe_terminate);
+               break;
+
+       case _AWE_NOTEOFF_ALL:
+               for (i = 0; i < awe_max_voices; i++)
+                       awe_note_off(i);
+               break;
+
        case _AWE_INITIAL_VOLUME:
                DEBUG(0,printk("AWE32: init attenuation %d\n", p1));
                init_atten = p1;
+               for (i = 0; i < awe_max_voices; i++)
+                       awe_set_voice_vol(i, FALSE);
                break;
 
        case _AWE_SET_GUS_BANK:
@@ -1843,6 +2026,46 @@ awe_hw_awe_control(int dev, int cmd, unsigned char *event)
                awe_gus_bank = p1;
                break;
                
+        /* v0.3 stuffs */
+       case _AWE_CHANNEL_MODE:
+               DEBUG(0,printk("AWE32: channel mode = %d\n", p1));
+               awe_channel_mode = p1;
+               awe_reset(0);
+               break;
+
+       case _AWE_DRUM_CHANNELS:
+               DEBUG(0,printk("AWE32: drum flags = %x\n", p1));
+               drum_flags = p1;
+               break;
+
+       case _AWE_EXCLUSIVE_SOUND:
+               DEBUG(0,printk("AWE32: exclusive mode = %d\n", p1));
+               awe_exclusive_sound = p1;
+               break;
+
+       case _AWE_GET_CURRENT_MODE:
+               {
+                       awe_mode_rec tmprec;
+                       tmprec.base_addr = awe_base;
+                       tmprec.mem_size = awe_mem_size / 2;
+                       tmprec.max_voices = awe_max_voices;
+                       tmprec.max_infos = AWE_MAX_INFOS;
+                       tmprec.max_samples = AWE_MAX_SAMPLES;
+                       tmprec.current_sf_id = current_sf_id;
+                       tmprec.free_mem = free_mem_ptr;
+                       tmprec.free_info = free_info;
+                       tmprec.free_sample = free_sample;
+                       tmprec.reverb_mode = reverb_mode;
+                       tmprec.chorus_mode = chorus_mode;
+                       tmprec.init_atten = init_atten;
+                       tmprec.channel_mode = awe_channel_mode;
+                       tmprec.gus_bank = awe_gus_bank;
+                       tmprec.exclusive_sound = awe_exclusive_sound;
+                       tmprec.drum_flags = drum_flags;
+                       tmprec.debug_mode = debug_mode;
+                       IOCTL_TO_USER(*(awe_mode_rec**)&event[4], 0, &tmprec, sizeof(tmprec));
+                       break;
+               }
        default:
                DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice));
                break;
@@ -1862,9 +2085,12 @@ awe_load_patch(int dev, int format, const char *addr,
        awe_patch_info patch;
        int rc = 0;
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
        if (format == GUS_PATCH) {
                return awe_load_guspatch(addr, offs, count, pmgr_flag);
-       } else if (format == SYSEX_PATCH) {
+       } else
+#endif
+       if (format == SYSEX_PATCH) {
                /* no system exclusive message supported yet */
                return 0;
        } else if (format != AWE_PATCH) {
@@ -1872,14 +2098,14 @@ awe_load_patch(int dev, int format, const char *addr,
                return RET_ERROR(EINVAL);
        }
        
-       if (count < sizeof(awe_patch_info)) {
+       if (count < AWE_PATCH_INFO_SIZE) {
                FATALERR(printk("AWE32 Error: Patch header too short\n"));
                return RET_ERROR(EINVAL);
        }
        COPY_FROM_USER(((char*)&patch) + offs, addr, offs, 
-                      sizeof(awe_patch_info) - offs);
+                      AWE_PATCH_INFO_SIZE - offs);
 
-       count -= sizeof(awe_patch_info);
+       count -= AWE_PATCH_INFO_SIZE;
        if (count < patch.len) {
                FATALERR(printk("AWE32 Warning: Patch record too short (%d<%d)\n",
                       count, (int)patch.len));
@@ -1919,12 +2145,12 @@ awe_load_info(awe_patch_info *patch, const char *addr)
        unsigned char bank, instr;
        int total_size;
 
-       if (patch->len < sizeof(awe_voice_rec)) {
+       if (patch->len < AWE_VOICE_REC_SIZE) {
                FATALERR(printk("AWE32 Error: invalid patch info length\n"));
                return RET_ERROR(EINVAL);
        }
 
-       offset = sizeof(awe_patch_info);
+       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;
@@ -1938,7 +2164,7 @@ awe_load_info(awe_patch_info *patch, const char *addr)
                return RET_ERROR(ENOSPC);
        }
 
-       total_size = sizeof(awe_voice_rec) + sizeof(awe_voice_info) * nvoices;
+       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));
@@ -1958,8 +2184,8 @@ awe_load_info(awe_patch_info *patch, const char *addr)
                rec->next_bank = NULL;
 
                /* copy awe_voice_info parameters */
-               COPY_FROM_USER(&rec->v, addr, offset, sizeof(awe_voice_info));
-               offset += sizeof(awe_voice_info);
+               COPY_FROM_USER(&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);
@@ -1988,11 +2214,11 @@ awe_load_data(awe_patch_info *patch, const char *addr)
                return RET_ERROR(ENOSPC);
        }
 
-       size = (patch->len - sizeof(awe_sample_info)) / 2;
-       offset = sizeof(awe_patch_info);
+       size = (patch->len - AWE_SAMPLE_INFO_SIZE) / 2;
+       offset = AWE_PATCH_INFO_SIZE;
        COPY_FROM_USER(&samples[free_sample], addr, offset,
-                      sizeof(awe_sample_info));
-       offset += sizeof(awe_sample_info);
+                      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));
@@ -2015,14 +2241,51 @@ awe_check_loaded(void)
 {
        if (!loaded_once) {
                /* it's the first time */
-               last_sample = free_sample;
-               last_info = free_info;
+               if (current_sf_id == 1) {
+                       last_sample = free_sample;
+                       last_info = free_info;
+               }
                current_sf_id++;
                loaded_once = 1;
        }
 }
 
 
+/*----------------------------------------------------------------*/
+
+static const char *readbuf_addr;
+static long readbuf_offs;
+static int readbuf_flags;
+
+/* initialize read buffer */
+static void
+awe_init_readbuf(const char *addr, long offset, int size, int mode_flags)
+{
+       readbuf_addr = addr;
+       readbuf_offs = offset;
+       readbuf_flags = mode_flags;
+}
+
+/* read directly from user buffer */
+static unsigned short
+awe_read_word(int pos)
+{
+       unsigned short c;
+       /* read from user buffer */
+       if (readbuf_flags & AWE_SAMPLE_8BITS) {
+               unsigned char cc;
+               GET_BYTE_FROM_USER(cc, readbuf_addr, readbuf_offs + pos);
+               c = cc << 8; /* convert 8bit -> 16bit */
+       } else {
+               GET_SHORT_FROM_USER(c, readbuf_addr, readbuf_offs + pos * 2);
+       }
+       if (readbuf_flags & AWE_SAMPLE_UNSIGNED)
+               c ^= 0x8000; /* unsigned -> signed */
+       return c;
+}
+
+
+
 #define BLANK_LOOP_START       8
 #define BLANK_LOOP_END         40
 #define BLANK_LOOP_SIZE                48
@@ -2035,7 +2298,6 @@ awe_write_wave_data(const char *addr, long offset, int size)
        int i, truesize;
        int rc;
        unsigned long csum1, csum2;
-       DECL_INTR_FLAGS(flags);
 
        /* be sure loop points start < end */
        if (sp->loopstart > sp->loopend) {
@@ -2066,42 +2328,33 @@ awe_write_wave_data(const char *addr, long offset, int size)
        sp->loopstart += free_mem_ptr + AWE_DRAM_OFFSET;
        sp->loopend += free_mem_ptr + AWE_DRAM_OFFSET;
 
-       DISABLE_INTR(flags);
        if ((rc = awe_open_dram_for_write(free_mem_ptr)) != 0) {
-               RESTORE_INTR(flags);
                return rc;
        }
 
+       awe_init_readbuf(addr, offset, size, sp->mode_flags);
        csum1 = 0;
        for (i = 0; i < size; i++) {
-               unsigned char cc;
                unsigned short c;
-               if (sp->mode_flags & AWE_SAMPLE_8BITS) {
-                       GET_BYTE_FROM_USER(cc, addr, offset); offset++;
-                       c = cc << 8; /* convert 8bit -> 16bit */
-               } else {
-                       GET_SHORT_FROM_USER(c, addr, offset); offset += 2;
-               }
-               if (sp->mode_flags & AWE_SAMPLE_UNSIGNED)
-                       c ^= 0x8000; /* unsigned -> signed */
+               c = awe_read_word(i);
                csum1 += c;
                awe_write_dram(c);
                if (i == sp->loopend &&
-                   (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP)) {
+                   (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++) {
-                               if (sp->mode_flags & AWE_SAMPLE_8BITS) {
-                                       GET_BYTE_FROM_USER(cc, addr, offset-k);
-                                       c = cc << 8;
-                               } else {
-                                       GET_SHORT_FROM_USER(c, addr, offset-k*2);
-                               }
-                               if (sp->mode_flags & AWE_SAMPLE_UNSIGNED)
-                                       c ^= 0x8000;
+                               /* non-buffered data */
+                               c = awe_read_word(i - k);
                                awe_write_dram(c);
                        }
+                       if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) {
+                               sp->end += looplen;
+                       } else {
+                               sp->start += looplen;
+                               sp->end += looplen;
+                       }
                }
        }
 
@@ -2117,19 +2370,15 @@ awe_write_wave_data(const char *addr, long offset, int size)
        }
 
        awe_close_dram();
-       RESTORE_INTR(flags);
        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)samples[free_sample].checksum,
-                              (int)csum1));
+                              free_sample, (int)sp->checksum, (int)csum1));
                        return RET_ERROR(NO_DATA_ERR);
                }
 #endif /* AWE_CHECKSUM_DATA */
 #ifdef AWE_CHECKSUM_MEMORY
-               DISABLE_INTR(flags);
                if (awe_open_dram_for_read(free_mem_ptr) == 0) {
                        csum2 = 0;
                        for (i = 0; i < size; i++) {
@@ -2138,30 +2387,28 @@ awe_write_wave_data(const char *addr, long offset, int size)
                                csum2 += c;
                        }
                        awe_close_dram_for_read();
-                       if (csum2 != samples[free_sample].checksum) {
-                               RESTORE_INTR(flags);
+                       if (csum1 != csum2) {
                                ERRMSG(printk("AWE32: [%d] checksum mismatch on DRAM %x:%x\n",
-                                             free_sample,
-                                             (int)samples[free_sample].checksum,
-                                             (int)csum2));
+                                             free_sample, (int)csum1, (int)csum2));
                                return RET_ERROR(NO_DATA_ERR);
                        }
                }
-               RESTORE_INTR(flags);
 #endif /* AWE_CHECKSUM_MEMORY */
        }
        free_mem_ptr += sp->size;
 
        /* re-initialize FM passthrough */
-       DISABLE_INTR(flags);
        awe_init_fm();
        awe_tweak();
-       RESTORE_INTR(flags);
 
        return 0;
 }
 
 
+/*----------------------------------------------------------------*/
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
 /* calculate GUS envelope time:
  * is this correct?  i have no idea..
  */
@@ -2219,7 +2466,7 @@ awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag)
        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;
+       samples[free_sample].loopend = patch.loop_end + 1;
        samples[free_sample].size = patch.len;
 
        /* set up mode flags */
@@ -2229,10 +2476,12 @@ awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag)
        if (patch.mode & WAVE_UNSIGNED)
                samples[free_sample].mode_flags |= AWE_SAMPLE_UNSIGNED;
        samples[free_sample].mode_flags |= AWE_SAMPLE_NO_BLANK;
-       if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP)))
+       if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
                samples[free_sample].mode_flags |= AWE_SAMPLE_SINGLESHOT;
        if (patch.mode & WAVE_BIDIR_LOOP)
                samples[free_sample].mode_flags |= AWE_SAMPLE_BIDIR_LOOP;
+       if (patch.mode & WAVE_LOOP_BACK)
+               samples[free_sample].mode_flags |= AWE_SAMPLE_REVERSE_LOOP;
 
        DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no,
                       samples[free_sample].mode_flags));
@@ -2336,8 +2585,11 @@ awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag)
        return 0;
 }
 
+#endif  /* AWE_HAS_GUS_COMPATIBILITY */
+
+/*----------------------------------------------------------------*/
 
-/* remove samples with current sf_id from instrument list */
+/* remove samples with different sf_id from instrument list */
 static awe_voice_list *
 awe_get_removed_list(awe_voice_list *curp)
 {
@@ -2347,7 +2599,7 @@ awe_get_removed_list(awe_voice_list *curp)
        prevp = &lastp;
        for (maxc = AWE_MAX_INFOS;
             curp && maxc; curp = curp->next_instr, maxc--) {
-               if (curp->v.sf_id == current_sf_id)
+               if (curp->v.sf_id != 1)
                        *prevp = curp->next_instr;
                else
                        prevp = &curp->next_instr;
@@ -2364,9 +2616,14 @@ awe_remove_samples(void)
        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];
@@ -2388,7 +2645,7 @@ awe_remove_samples(void)
 
        free_sample = last_sample;
        free_info = last_info;
-       current_sf_id--;
+       current_sf_id = 1;
        loaded_once = 0;
 }
 
@@ -2407,7 +2664,7 @@ awe_set_sample(awe_voice_info *vp)
                        vp->loopstart += samples[i].loopstart;
                        vp->loopend += samples[i].loopend;
                        /* copy mode flags */
-                       vp->mode |= (samples[i].mode_flags << 6);
+                       vp->mode = samples[i].mode_flags;
                        /* set index */
                        vp->index = i;
                        return i;
@@ -2421,16 +2678,17 @@ awe_set_sample(awe_voice_info *vp)
 static void
 awe_aftertouch(int dev, int voice, int pressure)
 {
-       DECL_INTR_FLAGS(flags);
-
        DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure));
-       if (voice < 0 || voice >= awe_max_voices)
-               return;
-       voices[voice].velocity = pressure;
-       awe_calc_volume(voice);
-       DISABLE_INTR(flags);
-       awe_set_volume(voice);
-       RESTORE_INTR(flags);
+       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);
+       }
 }
 
 
@@ -2438,77 +2696,98 @@ awe_aftertouch(int dev, int voice, int pressure)
 static void
 awe_controller(int dev, int voice, int ctrl_num, int value)
 {
-       DECL_INTR_FLAGS(flags);
+       awe_chan_info *cinfo;
 
-       if (voice < 0 || voice >= awe_max_voices)
-               return;
+       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:
+       case CTL_BANK_SELECT: /* SEQ1 control */
                DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value));
-               voices[voice].bank = value;
+               cinfo->bank = value;
+               awe_set_instr(dev, voice, cinfo->instr);
                break;
 
-       case CTRL_PITCH_BENDER:
+       case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */
                DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value));
                /* zero centered */
-               voices[voice].bender = value;
-               awe_calc_pitch(voice);
-               DISABLE_INTR(flags);
-               awe_set_pitch(voice);
-               RESTORE_INTR(flags);
+               cinfo->bender = value;
+               awe_voice_change(voice, awe_set_voice_pitch);
                break;
 
-       case CTRL_PITCH_BENDER_RANGE:
+       case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */
                DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value));
-               /* sense x 100 */
-               voices[voice].bender_range = value;
+               /* value = sense x 100 */
+               cinfo->bender_range = value;
                /* no audible pitch change yet.. */
                break;
 
-       case CTL_EXPRESSION:
-               value /= 128;
-       case CTRL_EXPRESSION:
+       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 */
-               voices[voice].expression_vol = value;
-               awe_calc_volume(voice);
-               DISABLE_INTR(flags);
-               awe_set_volume(voice);
-               RESTORE_INTR(flags);
+               cinfo->expression_vol = value;
+               awe_voice_change(voice, awe_set_voice_vol);
                break;
 
-       case CTL_PAN:
+       case CTL_PAN:   /* SEQ1 control */
                DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value));
                /* (0-127) -> signed 8bit */
-               voices[voice].panning = value * 2 - 128;
-               DISABLE_INTR(flags);
-               awe_set_pan(voice, 0);
-               RESTORE_INTR(flags);
+               cinfo->panning = value * 2 - 128;
+               awe_voice_change(voice, awe_set_pan);
                break;
 
-       case CTL_MAIN_VOLUME:
-               value = (value * 127) / 16383;
-       case CTRL_MAIN_VOLUME:
+       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 */
-               voices[voice].main_vol = value;
-               awe_calc_volume(voice);
-               DISABLE_INTR(flags);
-               awe_set_volume(voice);
-               RESTORE_INTR(flags);
+               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(voice, AWE_FX_REVERB, value * 2);
-               break;          
+               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(voice, AWE_FX_CHORUS, value * 2);
-               break;          
+               FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2);
+               break;
+
+#ifdef AWE_ACCEPT_ALL_SOUNDS_CONTROLL
+       case 120:  /* all sounds off */
+               {int i; for (i = 0; i < AWE_NORMAL_VOICES; i++)
+                       awe_terminate(i);
+               }
+               /*awe_reset(0);*/
+               break;
+       case 123:  /* all notes off */
+               {int i;
+               for (i = 0; i < awe_max_voices; i++)
+                       awe_note_off(i);
+               }
+               break;
+#endif
 
+       case CTL_SUSTAIN: /* sustain the channel */
+               cinfo->sustained = value;
+               if (value == 0)
+                       awe_voice_change(voice, awe_sustain_off);
+               break;
 
        default:
                DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n",
@@ -2518,18 +2797,48 @@ awe_controller(int dev, int voice, int ctrl_num, int value)
 }
 
 
+
+/* 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;
+       }
+
+       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)
+{
+       if (voices[voice].state == AWE_ST_SUSTAINED)
+               awe_note_off(voice);
+}
+
+
 /* voice pan change (value = -128 - 127) */
 static void
 awe_panning(int dev, int voice, int value)
 {
-       DECL_INTR_FLAGS(flags);
-       if (voice >= 0 || voice < awe_max_voices) {
-               voices[voice].panning = value;
-               DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, voices[voice].panning));
-               DISABLE_INTR(flags);
-               awe_set_pan(voice, 0);
-               RESTORE_INTR(flags);
+       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)
+                       return;
+               cinfo = voices[voice].cinfo;
        }
+
+       cinfo->panning = value;
+       DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning));
+       awe_voice_change(voice, awe_set_pan);
 }
 
 
@@ -2542,63 +2851,173 @@ awe_volume_method(int dev, int mode)
 }
 
 
+#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;
+}
+#endif
+
+
 /* pitch wheel change: 0-16384 */
 static void
 awe_bender(int dev, int voice, int value)
 {
-       DECL_INTR_FLAGS(flags);
-
-       if (voice < 0 || voice >= awe_max_voices)
-               return;
+       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;
+       }
        /* convert to zero centered value */
-       voices[voice].bender = value - 8192;
-       DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, voices[voice].bender));
-       awe_calc_pitch(voice);
-       DISABLE_INTR(flags);
-       awe_set_pitch(voice);
-       RESTORE_INTR(flags);
+       cinfo->bender = value - 8192;
+       DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender));
+       awe_voice_change(voice, awe_set_voice_pitch);
 }
 
-
-/* search an empty voice; used by sequencer2 */
+/* calculate the number of voices for this note & velocity */
 static int
-awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
+awe_search_multi_voices(awe_voice_list *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;
+                       nvoices++;
+                       if (nvoices >= AWE_MAX_VOICES)
+                               break;
+               }
+       }
+       return nvoices; 
+}
+
+/* allocate voices corresponding note and velocity; supports multiple insts. */
+static void
+awe_alloc_multi_voices(int ch, int note, int velocity)
 {
-       int i, p, best = -1, best_time = 0x7fffffff;
+       int i, v, nvoices;
+       awe_voice_info *vlist[AWE_MAX_VOICES];
 
-       p = alloc->ptr;
-       /* First look for a completely stopped voice */
+       if (channels[ch].vrec == NULL && channels[ch].def_vrec == NULL)
+               awe_set_instr(0, ch, channels[ch].instr);
 
-       for (i = 0; i < alloc->max_voice; i++) {
-               if (alloc->map[p] == 0) {
-                       alloc->ptr = p;
-                       return p;
+       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);
+
+       /* allocate 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].ch = ch;
+               voices[v].time = current_alloc_time;
+               voices[v].cinfo = &channels[ch];
+               voices[v].sample = vlist[i];
+               voices[v].state = AWE_ST_MARK;
+       }
+
+       /* clear the mark in allocated voices */
+       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 */
+static int
+awe_clear_voice(void)
+{
+       int i, time, best;
+
+       /* looking for the oldest empty voice */
+       best = -1;
+       time = 0x7fffffff;
+       for (i = 0; i < awe_max_voices; i++) {
+               if (voices[i].state == AWE_ST_OFF && voices[i].time < time) {
+                       best = i;
+                       time = voices[i].time;
                }
-               if (alloc->alloc_times[p] < best_time) {
-                       best = p;
-                       best_time = alloc->alloc_times[p];
+       }
+       if (best >= 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;
                }
-               p = (p + 1) % alloc->max_voice;
+       }
+       if (best >= 0) {
+               awe_note_off(best);
+               return best;
        }
 
-       /* Then look for a releasing voice */
-       for (i = 0; i < alloc->max_voice; i++) {
-               if (alloc->map[p] == 0xffff) {
-                       alloc->ptr = p;
-                       return p;
+       /* 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;
                }
-               p = (p + 1) % alloc->max_voice;
        }
+       /*awe_terminate(best);*/
+       awe_note_off(best);
+       return best;
+}
 
-       if (best >= 0)
-               p = best;
 
-       /* terminate the voice */
-       if (voices[p].state)
-               awe_terminate(p);
+/* allocate a voice corresponding note and velocity; single instrument */
+static void
+awe_alloc_one_voice(int voice, int note, int velocity)
+{
+       int nvoices;
+       awe_voice_info *vlist[AWE_MAX_VOICES];
 
-       alloc->ptr = p;
-       return p;
+       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);
+
+       if (nvoices > 0) {
+               voices[voice].time = ++current_alloc_time;
+               voices[voice].sample = vlist[0]; /* use the first one */
+       }
+}
+
+
+/* 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;
+       return awe_clear_voice();
 }
 
 
@@ -2610,18 +3029,27 @@ awe_setup_voice(int dev, int voice, int chn)
        if (synth_devs[dev] == NULL ||
            (info = &synth_devs[dev]->chn_info[chn]) == NULL)
                return;
+
        if (voice < 0 || voice >= awe_max_voices)
                return;
 
        DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn));
-       voices[voice].expression_vol = info->controllers[CTL_EXPRESSION];
-       voices[voice].main_vol =
-               (info->controllers[CTL_MAIN_VOLUME] * 100) / 128; /* 0 - 127 */
-       voices[voice].panning =
+       channels[chn].expression_vol = info->controllers[CTL_EXPRESSION];
+       channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME];
+       channels[chn].panning =
                info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */
-       voices[voice].bender = info->bender_value; /* zero center */
-       voices[voice].bank = info->controllers[CTL_BANK_SELECT];
-       awe_set_instr(dev, voice, info->pgm_num);
+       channels[chn].bender = info->bender_value; /* zero center */
+       channels[chn].bank = info->controllers[CTL_BANK_SELECT];
+       channels[chn].sustained = info->controllers[CTL_SUSTAIN];
+       if (info->controllers[CTL_EXT_EFF_DEPTH]) {
+               FX_SET(&channels[chn].fx, AWE_FX_REVERB,
+                      info->controllers[CTL_EXT_EFF_DEPTH] * 2);
+       }
+       if (info->controllers[CTL_CHORUS_DEPTH]) {
+               FX_SET(&channels[chn].fx, AWE_FX_CHORUS,
+                      info->controllers[CTL_CHORUS_DEPTH] * 2);
+       }
+       awe_set_instr(dev, chn, info->pgm_num);
 }
 
 
@@ -2637,9 +3065,10 @@ awe_init_audio(void)
 
        /* turn off envelope engines */
        for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
-               awe_poke(AWE_DCYSUSV(ch), 0x0080);
+               awe_poke(AWE_DCYSUSV(ch), 0x80);
        }
   
+       /* reset all other parameters to zero */
        for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
                awe_poke(AWE_ENVVOL(ch), 0);
                awe_poke(AWE_ENVVAL(ch), 0);
@@ -2672,14 +3101,14 @@ awe_init_audio(void)
 static void
 awe_init_dma(void)
 {
-       awe_poke_dw(AWE_SMALR, 0x00000000);
-       awe_poke_dw(AWE_SMARR, 0x00000000);
-       awe_poke_dw(AWE_SMALW, 0x00000000);
-       awe_poke_dw(AWE_SMARW, 0x00000000);
+       awe_poke_dw(AWE_SMALR, 0);
+       awe_poke_dw(AWE_SMARR, 0);
+       awe_poke_dw(AWE_SMALW, 0);
+       awe_poke_dw(AWE_SMARW, 0);
 }
 
 
-/* initialization arrays */
+/* initialization arrays; from ADIP */
 
 static unsigned short init1[128] = {
        0x03ff, 0x0030,  0x07ff, 0x0130, 0x0bff, 0x0230,  0x0fff, 0x0330,
@@ -2718,6 +3147,7 @@ static unsigned short init2[128] = {
        0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
        0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
        0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
+
        0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
        0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
        0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
@@ -2806,35 +3236,39 @@ awe_send_array(unsigned short *data)
  * set up awe32 channels to some known state.
  */
 
+/* set the envelope & LFO parameters to the default values; see ADIP */
+static void
+awe_tweak_voice(int i)
+{
+       /* set all mod/vol envelope shape to minimum */
+       awe_poke(AWE_ENVVOL(i), 0x8000);
+       awe_poke(AWE_ENVVAL(i), 0x8000);
+       awe_poke(AWE_DCYSUS(i), 0x7F7F);
+       awe_poke(AWE_ATKHLDV(i), 0x7F7F);
+       awe_poke(AWE_ATKHLD(i), 0x7F7F);
+       awe_poke(AWE_PEFE(i), 0);  /* mod envelope height to zero */
+       awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */
+       awe_poke(AWE_LFO2VAL(i), 0x8000);
+       awe_poke(AWE_IP(i), 0xE000);    /* no pitch shift */
+       awe_poke(AWE_IFATN(i), 0xFF00); /* volume to minimum */
+       awe_poke(AWE_FMMOD(i), 0);
+       awe_poke(AWE_TREMFRQ(i), 0);
+       awe_poke(AWE_FM2FRQ2(i), 0);
+}
+
 static void
 awe_tweak(void)
 {
        int i;
-
-       /* Set the envelope engine parameters to the "default" values for
-          simply playing back unarticulated audio at 44.1kHz.  Set all
-          of the channels: */
-
-       for (i = 0; i < AWE_MAX_VOICES; i++) {
-               awe_poke(AWE_ENVVOL(i)   , 0x8000);
-               awe_poke(AWE_ENVVAL(i)   , 0x8000);
-               awe_poke(AWE_DCYSUS(i)   , 0x7F7F);
-               awe_poke(AWE_ATKHLDV(i)  , 0x7F7F);
-               awe_poke(AWE_LFO1VAL(i)  , 0x8000);
-               awe_poke(AWE_ATKHLD(i)   , 0x7F7F);
-               awe_poke(AWE_LFO2VAL(i)  , 0x8000);
-               awe_poke(AWE_IP(i)       , 0xE000);
-               awe_poke(AWE_IFATN(i)    , 0xFF00);
-               awe_poke(AWE_PEFE(i)     , 0x0000);
-               awe_poke(AWE_FMMOD(i)    , 0x0000);
-               awe_poke(AWE_TREMFRQ(i)  , 0x0010);
-               awe_poke(AWE_FM2FRQ2(i)  , 0x0010);
-       }
+       /* reset all channels */
+       for (i = 0; i < awe_max_voices; i++)
+               awe_tweak_voice(i);
 }
 
 
 /*
- *  initializes the FM section of AWE32
+ *  initializes the FM section of AWE32;
+ *   see Vince Vu's unofficial AWE32 programming guide
  */
 
 static void
@@ -2845,39 +3279,32 @@ awe_init_fm(void)
        if (awe_mem_size <= 0)
                return;
 #endif
-       DEBUG(0,printk("AWE32: initializing FM\n"));
+       DEBUG(3,printk("AWE32: initializing FM\n"));
 
        /* Initialize the last two channels for DRAM refresh and producing
           the reverb and chorus effects for Yamaha OPL-3 synthesizer */
 
-       awe_poke(   AWE_DCYSUSV(30)   , 0x0080);
-       awe_poke_dw(AWE_PSST(30)      , 0xFFFFFFE0);
-       awe_poke_dw(AWE_CSL(30)       , 0xFFFFFFE8);
-       awe_poke_dw(AWE_PTRX(30)      , 0x00FFFF00);
-       awe_poke_dw(AWE_CPF(30)       , 0x00000000);
-       awe_poke_dw(AWE_CCCA(30)      , 0x00FFFFE3);
-
-       awe_poke(   AWE_DCYSUSV(31)   , 0x0080);
-       awe_poke_dw(AWE_PSST(31)      , 0x00FFFFE0);
-       awe_poke_dw(AWE_CSL(31)       , 0xFFFFFFE8);
-       awe_poke_dw(AWE_PTRX(31)      , 0x00FFFF00);
-       awe_poke_dw(AWE_CPF(31)       , 0x00000000);
-       awe_poke_dw(AWE_CCCA(31)      , 0x00FFFFE3);
-
-       /* Timing loop */
-
-       /* PTRX is 32 bit long but do not write to the MS byte */
-       awe_poke(AWE_PTRX(30)      , 0x0000);
-
-       while(! (inw(awe_base-0x620+Pointer) & 0x1000));
-       while(   inw(awe_base-0x620+Pointer) & 0x1000);
-
-       /* now write the MS byte of PTRX */
-       OUTW(0x4828, awe_base-0x620+Data0+0x002);
-
-       awe_poke(   AWE_IFATN(28)     , 0x0000);
-       awe_poke_dw(AWE_VTFT(30)      , 0x8000FFFF);
-       awe_poke_dw(AWE_VTFT(31)      , 0x8000FFFF);
+       /* 31: FM left channel, 0xffffe0-0xffffe8 */
+       awe_poke(AWE_DCYSUSV(30), 0x80);
+       awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */
+       awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 |
+                   (DEF_FM_CHORUS_DEPTH << 24));
+       awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8));
+       awe_poke_dw(AWE_CPF(30), 0);
+       awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3);
+
+       /* 32: FM right channel, 0xfffff0-0xfffff8 */
+       awe_poke(AWE_DCYSUSV(31), 0x80);
+       awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */
+       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_CCCA(31), 0x00FFFFF3);
+
+       /* skew volume & cutoff */
+       awe_poke_dw(AWE_VTFT(30), 0x8000FFFF);
+       awe_poke_dw(AWE_VTFT(31), 0x8000FFFF);
 
        /* change maximum channels to 30 */
        awe_max_voices = AWE_NORMAL_VOICES;
@@ -2933,22 +3360,19 @@ awe_open_dram_for_write(int offset)
 static void
 awe_open_dram_for_check(void)
 {
-       int k;
-       unsigned long scratch;
-
-       awe_poke(AWE_HWCF2 , 0x0020);
-
-       for (k = 0; k < AWE_NORMAL_VOICES; k++) {
-               awe_poke(AWE_DCYSUSV(k), 0x0080);
-               awe_poke_dw(AWE_VTFT(k), 0x00000000);
-               awe_poke_dw(AWE_CVCF(k), 0x00000000);
-               awe_poke_dw(AWE_PTRX(k), 0x40000000);
-               awe_poke_dw(AWE_CPF(k), 0x40000000);
-               awe_poke_dw(AWE_PSST(k), 0x00000000);
-               awe_poke_dw(AWE_CSL(k), 0x00000000);
-               scratch = (((k&1) << 9) + 0x400);
-               scratch = scratch << 16;
-               awe_poke_dw(AWE_CCCA(k), scratch);
+       int i;
+       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);
+               if (i & 1) /* DMA write */
+                       awe_poke_dw(AWE_CCCA(i), 0x06000000);
+               else       /* DMA read */
+                       awe_poke_dw(AWE_CCCA(i), 0x04000000);
        }
 }
 
@@ -3037,13 +3461,6 @@ awe_close_dram_for_read(void)
 static void
 awe_write_dram(unsigned short c)
 {
-       int k;
-       /* wait until FULL bit in SMAxW register be false */
-       for (k = 0; k < 10000; k++) {
-               if (!(awe_peek_dw(AWE_SMALW) & 0x80000000))
-                       break;
-               awe_wait(10);
-       }
        awe_poke(AWE_SMLD, c);
 }
 
@@ -3051,27 +3468,34 @@ awe_write_dram(unsigned short c)
  * detect presence of AWE32 and check memory size
  *================================================================*/
 
+/* detect emu8000 chip on the specified address; from VV's guide */
+
+static int
+awe_detect_base(int addr)
+{
+       awe_base = addr;
+       if ((awe_peek(AWE_U1) & 0x000F) != 0x000C)
+               return 0;
+       if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058)
+               return 0;
+       if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003)
+               return 0;
+        DEBUG(0,printk("AWE32 found at %x\n", awe_base));
+       return 1;
+}
+       
 static int
 awe_detect(void)
 {
+       int base;
 #ifdef AWE_DEFAULT_BASE_ADDR
-       awe_base = AWE_DEFAULT_BASE_ADDR;
-       if (((awe_peek(AWE_U1) & 0x000F) == 0x000C) &&
-           ((awe_peek(AWE_HWCF1) & 0x007E) == 0x0058) &&
-           ((awe_peek(AWE_HWCF2) & 0x0003) == 0x0003))
+       if (awe_detect_base(AWE_DEFAULT_BASE_ADDR))
                return 1;
 #endif
        if (awe_base == 0) {
-               for (awe_base = 0x620; awe_base <= 0x680; awe_base += 0x20) {
-                       if ((awe_peek(AWE_U1) & 0x000F) != 0x000C)
-                               continue;
-                       if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058)
-                               continue;
-                       if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003)
-                               continue;
-                       DEBUG(0,printk("AWE32 found at %x\n", awe_base));
-                       return 1;
-               }
+               for (base = 0x620; base <= 0x680; base += 0x20)
+                       if (awe_detect_base(base))
+                               return 1;
        }
        FATALERR(printk("AWE32 not found\n"));
        awe_base = 0;
@@ -3082,28 +3506,40 @@ awe_detect(void)
 /*================================================================
  * check dram size on AWE board
  *================================================================*/
+
+/* any three numbers you like */
+#define UNIQUE_ID1     0x1234
+#define UNIQUE_ID2     0x4321
+#define UNIQUE_ID3     0xFFFF
+
 static int
 awe_check_dram(void)
 {
        awe_open_dram_for_check();
 
-       awe_poke_dw(AWE_SMALW    , 0x00200000);     /* DRAM start address */
-       awe_poke(   AWE_SMLD     , 0x1234);
-       awe_poke(   AWE_SMLD     , 0x7777);
+       /* 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 < 28*1024) {     /* 28 MB is max onboard memory */
+       while (awe_mem_size < AWE_MAX_DRAM_SIZE) {
                awe_wait(2);
-               awe_poke_dw(AWE_SMALR, 0x00200000); /* Address for reading */
-               awe_peek(AWE_SMLD);             /* Discard stale data  */
-               if (awe_peek(AWE_SMLD) != 0x1234)
+               /* read a data on the DRAM start address */
+               awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET);
+               awe_peek(AWE_SMLD); /* discard stale data  */
+               if (awe_peek(AWE_SMLD) != UNIQUE_ID1)
                        break;
-               if (awe_peek(AWE_SMLD) != 0x7777)
+               if (awe_peek(AWE_SMLD) != UNIQUE_ID2)
                        break;
-               awe_mem_size += 32;
-               /* Address for writing */
-               awe_poke_dw(AWE_SMALW, 0x00200000+awe_mem_size*512L);
-               awe_poke(AWE_SMLD, 0xFFFF);
+               awe_mem_size += 32;  /* increment 32 Kbytes */
+               /* Write a unique data on the test address;
+                * if the address is out of range, the data is written on
+                * 0x200000(=AWE_DRAM_OFFSET).  Then the two id words are
+                * broken by this data.
+                */
+               awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L);
+               awe_poke(AWE_SMLD, UNIQUE_ID3);
        }
        awe_close_dram();
 
@@ -3119,113 +3555,107 @@ awe_check_dram(void)
 
 
 /*================================================================
- * chorus and reverb controls
+ * chorus and reverb controls; from VV's guide
  *================================================================*/
 
-static unsigned short ChorusEffects[24] =
-{
-  0xE600,0x03F6,0xBC2C,0xE608,0x031A,0xBC6E,0xE610,0x031A,
-  0xBC84,0xE620,0x0269,0xBC6E,0xE680,0x04D3,0xBCA6,0xE6E0,
-  0x044E,0xBC37,0xE600,0x0B06,0xBC00,0xE6C0,0x0B06,0xBC00
+/* 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 unsigned long ChorusEffects2[] =
+static void awe_set_chorus_mode(int effect)
 {
-  0x0000 ,0x006D,0x8000,0x0000,0x0000 ,0x017C,0x8000,0x0000,
-  0x0000 ,0x0083,0x8000,0x0000,0x0000 ,0x017C,0x8000,0x0000,
-  0x0000 ,0x005B,0x8000,0x0000,0x0000 ,0x0026,0x8000,0x0000,
-  0x6E000,0x0083,0x8000,0x0000,0x6E000,0x0083,0x8000,0x0000
-};
+       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]);
+       awe_poke_dw(AWE_HWCF6, 0x8000);
+       awe_poke_dw(AWE_HWCF7, 0x0000);
+}
 
-static unsigned short ChorusCommand[14] =
-{
-  0x69,0xA20,0x6C,0xA20,0x63,0xA22,0x29,
-  0xA20,0x2A,0xA20,0x2D,0xA20,0x2E,0xA20
-};
+/*----------------------------------------------------------------*/
 
-static unsigned short ReverbEffects[224] =
-{
-  /* 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 */
-  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 */
-  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 */
-  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 */
-  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 */
-  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 */
-  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 */
-  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
+/* 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 */
+       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 */
+       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 */
+       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 */
+       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 */
+       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 */
+       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 */
+       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 */
+       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 unsigned short ReverbCommand[56] =
-{
-  0x43,0xA20,0x45,0xA20,0x7F,0xA22,0x47,0xA20,
-  0x54,0xA22,0x56,0xA22,0x4F,0xA20,0x57,0xA20,
-  0x5F,0xA20,0x47,0xA22,0x4F,0xA22,0x57,0xA22,
-  0x5D,0xA22,0x5F,0xA22,0x61,0xA20,0x63,0xA20,
-  0x49,0xA20,0x4B,0xA20,0x51,0xA20,0x53,0xA20,
-  0x59,0xA20,0x5B,0xA20,0x41,0xA22,0x43,0xA22,
-  0x49,0xA22,0x4B,0xA22,0x51,0xA22,0x53,0xA22
+static struct ReverbCmdPair {
+       unsigned short cmd, port;
+} reverb_cmds[28] = {
+  {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
+  {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
+  {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
+  {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
+  {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
+  {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
+  {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
 };
 
-static void awe_set_chorus_mode(int effect)
-{
-       int k;
-       DECL_INTR_FLAGS(flags);
-
-       DISABLE_INTR(flags);
-       for (k = 0; k < 3; k++)
-               awe_poke(ChorusCommand[k*2],
-                        ChorusCommand[k*2+1],
-                        ChorusEffects[k+effect*3]);
-       for (k = 0; k < 4; k++)
-               awe_poke_dw(ChorusCommand[6+k*2],
-                           ChorusCommand[6+k*2+1],
-                           ChorusEffects2[k+effect*4]);
-       RESTORE_INTR(flags);
-}
 
 static void awe_set_reverb_mode(int effect)
 {
-       int k;
-       DECL_INTR_FLAGS(flags);
-
-       DISABLE_INTR(flags);
-       for (k = 0; k < 28; k++)
-               awe_poke(ReverbCommand[k*2],
-                        ReverbCommand[k*2+1],
-                        ReverbEffects[k+effect*28]);
-       RESTORE_INTR(flags);
+       int i;
+       for (i = 0; i < 28; i++)
+               awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port,
+                        reverb_parm[effect][i]);
 }
 
 #endif /* CONFIG_AWE32_SYNTH */
index 9f924b16a140e582aa0187d30374ad789583b783..4b96bf0322f0cd431a5b0bf295974297e99ab50c 100644 (file)
@@ -16,6 +16,8 @@ extern int attach_aci(void);
 extern void unload_aci(void);
 extern int attach_awe(void);
 extern void unload_awe(void);
+extern int init_aedsp16(void);
+extern void uninit_aedsp16(void);
 
 /*
  * There are two places where you can insert initialization calls of
@@ -26,6 +28,9 @@ extern void unload_awe(void);
 void
 sound_preinit_lowlevel_drivers(void)
 {
+#ifdef CONFIG_AEDSP16
+   init_aedsp16();
+#endif
 }
 
 void
@@ -50,5 +55,10 @@ sound_unload_lowlevel_drivers(void)
 #ifdef CONFIG_AWE32_SYNTH
    unload_awe();
 #endif
+
+#ifdef CONFIG_AEDSP16
+   uninit_aedsp16();
+#endif
+
 }
 #endif
index 4cdb3d3045111ddaf0e9ec94da81993308b00509..f4f86ff578769709ed68814a42567ce52f017ca0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
index 8c6e1ef8026546d0618f4948dc0134289239eb19..8c416a632d2f0b780498467f08fd0fe3e7a07d12 100644 (file)
@@ -4,7 +4,7 @@
  * The low level driver for Turtle Beach Maui and Tropez.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -127,7 +127,7 @@ maui_write (unsigned char data)
   return 0;
 }
 
-void
+static void
 mauiintr (int irq, void *dev_id, struct pt_regs *dummy)
 {
   irq_ok = 1;
@@ -286,7 +286,7 @@ maui_short_wait (int mask)
   return 0;
 }
 
-int
+static int
 maui_load_patch (int dev, int format, const char *addr,
                 int offs, int count, int pmgr_flag)
 {
@@ -459,6 +459,7 @@ attach_maui (struct address_info *hw_config)
        */
 
       synth = midi_devs[this_dev]->converter;
+      synth->id = "MAUI";
 
       if (synth != NULL)
        {
index 8b68c7d91db3958a87ba7199ec644c7d93f79227..3353e5a67c248dfcd041a7413357cb4d6aeaaa35 100644 (file)
@@ -1,7 +1,7 @@
 static unsigned char ctrl_def_values[128] =
 {
-       0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,      /*   0 to   7 */
-       0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,      /*   8 to  15 */
+       0x40,0x00,0x40,0x40,  0x40,0x40,0x40,0x7f,      /*   0 to   7 */
+       0x40,0x40,0x40,0x7f,  0x40,0x40,0x40,0x40,      /*   8 to  15 */
        0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,      /*  16 to  23 */
        0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,      /*  24 to  31 */
 
@@ -15,7 +15,7 @@ static unsigned char ctrl_def_values[128] =
        0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,      /*  80 to  87 */
        0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,      /*  88 to  95 */
 
-       0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,      /*  96 to 103 */
+       0x00,0x00,0x7f,0x7f,  0x7f,0x7f,0x00,0x00,      /*  96 to 103 */
        0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,      /* 104 to 111 */
        0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,      /* 112 to 119 */
        0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,      /* 120 to 127 */
index 4e26b3464da73a6ffc3b54c764c22b19ea87470e..512fc2bb73360bce06420a6f6493b18615ff157f 100644 (file)
@@ -4,7 +4,7 @@
  * High level midi sequencer manager for dumb MIDI interfaces.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -275,10 +275,7 @@ midi_synth_ioctl (int dev,
     {
 
     case SNDCTL_SYNTH_INFO:
-      {
-       char           *fixit = (char *) synth_devs[dev]->info;
-       copy_to_user (&((char *) arg)[0], fixit, sizeof (struct synth_info));
-      };
+      memcpy ((&((char *) arg)[0]), (char *) synth_devs[dev]->info, sizeof (struct synth_info));
 
       return 0;
       break;
index e29618fd542a6603b5dd6a472a47f880b1cf0bd5..1509b2255a34ecf3506f33a1e03ad9218f739cda 100644 (file)
@@ -22,6 +22,7 @@ static struct synth_info std_synth_info =
 
 static struct synth_operations std_midi_synth =
 {
+  "MIDI",
   &std_synth_info,
   0,
   SYNTH_TYPE_MIDI,
index c477d5a05e4d776b21fc92dd1a0b9b04c6be2980..cfe2879d1e4e84da8bebd195d5a2372317b1f824 100644 (file)
@@ -4,14 +4,13 @@
  * Device file manager for /dev/midi#
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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 <linux/poll.h>
 
 
 #include "sound_config.h"
@@ -84,7 +83,7 @@ static volatile int open_devs = 0;
          restore_flags(flags); \
        }
 
-void
+static void
 drain_midi_queue (int dev)
 {
 
@@ -484,13 +483,13 @@ MIDIbuf_ioctl (int dev, struct fileinfo *file,
       {
 
       case SNDCTL_MIDI_PRETIME:
-       get_user (val, (int *) arg);
+       val = *(int *) arg;
        if (val < 0)
          val = 0;
 
        val = (HZ * val) / 10;
        parms[dev].prech_timeout = val;
-       return ioctl_out (arg, val);
+       return (*(int *) arg = val);
        break;
 
       default:
@@ -498,21 +497,40 @@ MIDIbuf_ioctl (int dev, struct fileinfo *file,
       }
 }
 
-unsigned int
-MIDIbuf_poll (kdev_t dev, struct fileinfo *file, poll_table * wait)
+int
+MIDIbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
 {
-  unsigned int mask = 0;
-
-  input_sleep_flag[dev].opts = WK_SLEEP;
-  poll_wait (&input_sleeper[dev], wait);
-  midi_sleep_flag[dev].opts = WK_SLEEP;
-  poll_wait (&midi_sleeper[dev], wait);
-
-  if (DATA_AVAIL (midi_in_buf[dev]))
-    mask |= POLLIN | POLLRDNORM;
-  if (!SPACE_AVAIL (midi_out_buf[dev]))
-    mask |= POLLOUT | POLLWRNORM;
-  return mask;
+  dev = dev >> 4;
+
+  switch (sel_type)
+    {
+    case SEL_IN:
+      if (!DATA_AVAIL (midi_in_buf[dev]))
+       {
+
+         input_sleep_flag[dev].opts = WK_SLEEP;
+         poll_wait (&input_sleeper[dev], wait);
+         return 0;
+       }
+      return 1;
+      break;
+
+    case SEL_OUT:
+      if (SPACE_AVAIL (midi_out_buf[dev]))
+       {
+
+         midi_sleep_flag[dev].opts = WK_SLEEP;
+         poll_wait (&midi_sleeper[dev], wait);
+         return 0;
+       }
+      return 1;
+      break;
+
+    case SEL_EX:
+      return 0;
+    }
+
+  return 0;
 }
 
 
index 9256685ce9640c583d74742000fbe22c7b9ecdab..7c611100312a7a1585de280903c7ae22968d82e6 100644 (file)
@@ -4,7 +4,7 @@
  * The low level driver for Roland MPU-401 compatible Midi cards.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -785,7 +785,7 @@ mpu401_ioctl (int dev, unsigned cmd, caddr_t arg)
          printk ("MPU-401: Intelligent mode not supported by the HW\n");
          return -EINVAL;
        }
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
       set_uart_mode (dev, devc, !val);
       return 0;
       break;
@@ -795,16 +795,12 @@ mpu401_ioctl (int dev, unsigned cmd, caddr_t arg)
        int             ret;
        mpu_command_rec rec;
 
-       copy_from_user ((char *) &rec, &((char *) arg)[0], sizeof (rec));
+       memcpy ((char *) &rec, (&((char *) arg)[0]), sizeof (rec));
 
        if ((ret = mpu401_command (dev, &rec)) < 0)
          return ret;
 
-       {
-         char           *fixit = (char *) &rec;
-
-         copy_to_user (&((char *) arg)[0], fixit, sizeof (rec));
-       };
+       memcpy ((&((char *) arg)[0]), (char *) &rec, sizeof (rec));
        return 0;
       }
       break;
@@ -845,10 +841,7 @@ mpu_synth_ioctl (int dev,
     {
 
     case SNDCTL_SYNTH_INFO:
-      {
-       char           *fixit = (char *) &mpu_synth_info[midi_dev];
-       copy_to_user (&((char *) arg)[0], fixit, sizeof (struct synth_info));
-      };
+      memcpy ((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof (struct synth_info));
 
       return 0;
       break;
@@ -954,6 +947,7 @@ mpu_synth_close (int dev)
 
 static struct synth_operations mpu401_synth_proto =
 {
+  "MPU401",
   NULL,
   0,
   SYNTH_TYPE_MIDI,
@@ -1104,6 +1098,7 @@ attach_mpu401 (struct address_info *hw_config)
 
 
   mpu401_synth_operations[num_midis] = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -1594,7 +1589,7 @@ mpu_timer_ioctl (int dev,
       {
        int             parm;
 
-       get_user (parm, (int *) arg);
+       parm = *(int *) arg;
        parm &= timer_caps;
 
        if (parm != 0)
@@ -1607,7 +1602,7 @@ mpu_timer_ioctl (int dev,
              mpu_cmd (midi_dev, 0x3d, 0);      /* Use SMPTE sync */
          }
 
-       return ioctl_out (arg, timer_mode);
+       return (*(int *) arg = timer_mode);
       }
       break;
 
@@ -1635,11 +1630,11 @@ mpu_timer_ioctl (int dev,
       {
        int             val;
 
-       get_user (val, (int *) arg);
+       val = *(int *) arg;
        if (val)
          set_timebase (midi_dev, val);
 
-       return ioctl_out (arg, curr_timebase);
+       return (*(int *) arg = curr_timebase);
       }
       break;
 
@@ -1648,7 +1643,7 @@ mpu_timer_ioctl (int dev,
        int             val;
        int             ret;
 
-       get_user (val, (int *) arg);
+       val = *(int *) arg;
 
        if (val)
          {
@@ -1665,7 +1660,7 @@ mpu_timer_ioctl (int dev,
            curr_tempo = val;
          }
 
-       return ioctl_out (arg, curr_tempo);
+       return (*(int *) arg = curr_tempo);
       }
       break;
 
@@ -1673,20 +1668,20 @@ mpu_timer_ioctl (int dev,
       {
        int             val;
 
-       get_user (val, (int *) arg);
+       val = *(int *) arg;
        if (val != 0)           /* Can't change */
          return -EINVAL;
 
-       return ioctl_out (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+       return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
       }
       break;
 
     case SNDCTL_SEQ_GETTIME:
-      return ioctl_out (arg, curr_ticks);
+      return (*(int *) arg = curr_ticks);
       break;
 
     case SNDCTL_TMR_METRONOME:
-      get_user (metronome_mode, (int *) arg);
+      metronome_mode = *(int *) arg;
       setup_metronome (midi_dev);
       return 0;
       break;
index afd19317fd03fb3a3a4ded0de26ac9a9710061b2..28ce6da15c1f230ef88fbcefc938325996d2599b 100644 (file)
@@ -4,7 +4,7 @@
  * A low level driver for Yamaha YM3812 and OPL-3 -chips
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -68,8 +68,6 @@ opl_devinfo;
 
 static struct opl_devinfo *devc = NULL;
 
-static int      force_opl3_mode = 0;
-
 static int      detected_model;
 
 static int      store_instr (int instr_no, struct sbi_instrument *instr);
@@ -77,12 +75,6 @@ static void     freq_to_fnum (int freq, int *block, int *fnum);
 static void     opl3_command (int io_addr, unsigned int addr, unsigned int val);
 static int      opl3_kill_note (int dev, int voice, int note, int velocity);
 
-void
-enable_opl3_mode (int left, int right, int both)
-{
-  force_opl3_mode = 1;
-}
-
 static void
 enter_4op_mode (void)
 {
@@ -119,7 +111,7 @@ opl3_ioctl (int dev,
       {
        struct sbi_instrument ins;
 
-       copy_from_user ((char *) &ins, &((char *) arg)[0], sizeof (ins));
+       memcpy ((char *) &ins, (&((char *) arg)[0]), sizeof (ins));
 
        if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
          {
@@ -134,11 +126,7 @@ opl3_ioctl (int dev,
     case SNDCTL_SYNTH_INFO:
       devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
 
-      {
-       char           *fixit = (char *) &devc->fm_info;
-
-       copy_to_user (&((char *) arg)[0], fixit, sizeof (devc->fm_info));
-      };
+      memcpy ((&((char *) arg)[0]), (char *) &devc->fm_info, sizeof (devc->fm_info));
       return 0;
       break;
 
@@ -180,6 +168,7 @@ opl3_detect (int ioaddr, int *osp)
 
 
   devc = (struct opl_devinfo *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (*devc)));
+  sound_mem_sizes[sound_nblocks] = sizeof (*devc);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
 
@@ -200,17 +189,18 @@ opl3_detect (int ioaddr, int *osp)
 
   signature = stat1 = inb (ioaddr);    /* Status register */
 
-  if (signature != 0x00 && signature != 0x06 && signature != 0x02)
+  if (signature != 0x00 && signature != 0x06 && signature != 0x02 &&
+      signature != 0x0f)
     {
       DDB (printk ("OPL3 not detected %x\n", signature));
       return 0;
     }
 
-  if (signature == 0x06 && !force_opl3_mode)   /* OPL2 */
+  if (signature == 0x06)       /* OPL2 */
     {
       detected_model = 2;
     }
-  else if (signature == 0x00)  /* OPL3 or OPL4 */
+  else if (signature == 0x00 || signature == 0x0f)     /* OPL3 or OPL4 */
     {
       unsigned char   tmp;
 
@@ -341,7 +331,7 @@ opl3_set_instr (int dev, int voice, int instr_no)
  * volume -8 it was implemented as a table because it is only 128 bytes and
  * it saves a lot of log() calculations. (RH)
  */
-char            fm_volume_table[128] =
+static char     fm_volume_table[128] =
 {-64, -48, -40, -35, -32, -29, -27, -26,
  -24, -23, -21, -20, -19, -18, -18, -17,
  -16, -15, -15, -14, -13, -13, -12, -12,
@@ -664,7 +654,7 @@ opl3_start_note (int dev, int voice, int note, int volume)
    * have to calculate the bending now.
    */
 
-  freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range);
+  freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
   devc->voc[voice].current_freq = freq;
 
   freq_to_fnum (freq, &block, &fnum);
@@ -741,7 +731,7 @@ opl3_command (int io_addr, unsigned int addr, unsigned int val)
 
   outb (((unsigned char) (addr & 0xff)), io_addr);
 
-  if (!devc->model != 2)
+  if (devc->model != 2)
     tenmicrosec (devc->osp);
   else
     for (i = 0; i < 2; i++)
@@ -973,7 +963,7 @@ bend_pitch (int dev, int voice, int value)
                                 * Not keyed on
                                 */
 
-  freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range);
+  freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
   devc->voc[voice].current_freq = freq;
 
   freq_to_fnum (freq, &block, &fnum);
@@ -1099,7 +1089,8 @@ opl3_setup_voice (int dev, int voice, int chn)
   opl3_set_instr (dev, voice,
                  info->pgm_num);
 
-  devc->voc[voice].bender = info->bender_value;
+  devc->voc[voice].bender = 0;
+  devc->voc[voice].bender_range = info->bender_range;
   devc->voc[voice].volume =
     info->controllers[CTL_MAIN_VOLUME];
   devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128;
@@ -1107,6 +1098,7 @@ opl3_setup_voice (int dev, int voice, int chn)
 
 static struct synth_operations opl3_operations =
 {
+  "OPL",
   NULL,
   0,
   SYNTH_TYPE_FM,
index ae1234b442dac279cb3d20479742125e9b41bdaf..5529d19d8a65c738964e6c7d0f6c1dba79080300 100644 (file)
@@ -2,7 +2,7 @@
  *     opl3.h  - Definitions of the OPL-3 registers
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
index d07f6735c42455c2313743cd5d6ea288951033c9..4ab565960d6d82f8e9ca21d97444422811c3fa33 100644 (file)
@@ -1,4 +1,8 @@
 
+#ifdef __alpha__
+#else
+#endif
+
 #define ALLOW_SELECT
 #undef NO_INLINE_ASM
 #define SHORT_BANNERS
 #define LINUX21X
 #endif
 
-#include <linux/param.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/sched.h>
-#include <linux/ctype.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-#include <asm/system.h>
+#include <linux/utsname.h>
+#include <linux/string.h>
+#include <linux/fs.h>
 #include <asm/dma.h>
-#include <linux/wait.h>
+#include <asm/param.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
 #include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#ifdef __alpha__
+#include <asm/segment.h>
+#endif
 #include <linux/vmalloc.h>
 #include <asm/uaccess.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/utsname.h>
+#include <linux/poll.h>
 
 #include <linux/wrapper.h>
 
 #include <linux/soundcard.h>
 
+
 #define FALSE  0
 #define TRUE   1
 
 
 
 struct snd_wait {
-         int opts;
+         volatile int opts;
        };
 
 extern int sound_alloc_dma(int chn, char *deviceID);
@@ -54,10 +58,14 @@ extern void sound_free_dma(int chn);
 extern void sound_close_dma(int chn);
 
 #define RUNTIME_DMA_ALLOC
+#define USE_AUTOINIT_DMA
 
 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 be5a169ca1c314aa7b318b4b5c2caf6e1450221a..a162ce9932b299400fdbccb508be3635ed62df96 100644 (file)
@@ -1,4 +1,3 @@
-#define _PAS2_CARD_C_
 /*
  * sound/pas2_card.c
  *
@@ -55,7 +54,7 @@ pas_write (unsigned char data, int ioaddr)
 
 /******************* Begin of the Interrupt Handler ********************/
 
-void
+static void
 pasintr (int irq, void *dev_id, struct pt_regs *dummy)
 {
   int             status;
@@ -107,7 +106,7 @@ pas_remove_intr (int mask)
 
 /******************* Begin of the Initialization Code ******************/
 
-int
+static int
 config_pas_hw (struct address_info *hw_config)
 {
   char            ok = 1;
@@ -116,26 +115,11 @@ config_pas_hw (struct address_info *hw_config)
   pas_irq = hw_config->irq;
 
   pas_write (0x00, 0x0B8B);
-
-  pas_write (0x36, 0x138B);    /*
-                                  * Local timer control *
-                                  * register
-                                */
-
-  pas_write (0x36, 0x1388);    /*
-                                  * Sample rate timer (16 bit)
-                                */
+  pas_write (0x36, 0x138B);
+  pas_write (0x36, 0x1388);
   pas_write (0, 0x1388);
-
-  pas_write (0x74, 0x138B);    /*
-                                  * Local timer control *
-                                  * register
-                                */
-
-  pas_write (0x74, 0x1389);    /*
-                                  * Sample count register (16
-                                  * * bit)
-                                */
+  pas_write (0x74, 0x138B);
+  pas_write (0x74, 0x1389);
   pas_write (0, 0x1389);
 
   pas_write (0x80 | 0x40 | 0x20 | 1, 0x0B8A);
@@ -153,7 +137,7 @@ config_pas_hw (struct address_info *hw_config)
 
   if (pas_irq < 0 || pas_irq > 15)
     {
-      printk ("PAS2: Invalid IRQ %d", pas_irq);
+      printk ("PAS16: Invalid IRQ %d", pas_irq);
       ok = 0;
     }
   else
@@ -163,7 +147,7 @@ config_pas_hw (struct address_info *hw_config)
       pas_write (int_ptrs, 0xF38A);
       if (!irq_bits[pas_irq])
        {
-         printk ("PAS2: Invalid IRQ %d", pas_irq);
+         printk ("PAS16: Invalid IRQ %d", pas_irq);
          ok = 0;
        }
       else
@@ -175,7 +159,7 @@ config_pas_hw (struct address_info *hw_config)
 
   if (hw_config->dma < 0 || hw_config->dma > 7)
     {
-      printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+      printk ("PAS16: Invalid DMA selection %d", hw_config->dma);
       ok = 0;
     }
   else
@@ -183,7 +167,7 @@ config_pas_hw (struct address_info *hw_config)
       pas_write (dma_bits[hw_config->dma], 0xF389);
       if (!dma_bits[hw_config->dma])
        {
-         printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+         printk ("PAS16: Invalid DMA selection %d", hw_config->dma);
          ok = 0;
        }
       else
@@ -268,7 +252,7 @@ config_pas_hw (struct address_info *hw_config)
   return ok;
 }
 
-int
+static int
 detect_pas_hw (struct address_info *hw_config)
 {
   unsigned char   board_id, foo;
@@ -291,8 +275,8 @@ detect_pas_hw (struct address_info *hw_config)
     return 0;
 
   /*
-   * We probably have a PAS-series board, now check for a PAS2-series board
-   * by trying to change the board revision bits. PAS2-series hardware won't
+   * We probably have a PAS-series board, now check for a PAS16-series board
+   * by trying to change the board revision bits. PAS16-series hardware won't
    * let you do this - the bits are read-only.
    */
 
@@ -302,9 +286,7 @@ detect_pas_hw (struct address_info *hw_config)
   foo = inb (0x0B8B);
   pas_write (board_id, 0x0B8B);
 
-  if (board_id != foo)         /*
-                                * Not a PAS2
-                                */
+  if (board_id != foo)
     return 0;
 
   pas_model = pas_read (0xFF88);
@@ -332,7 +314,6 @@ attach_pas_card (struct address_info *hw_config)
 
       if (config_pas_hw (hw_config))
        {
-
 #ifdef CONFIG_AUDIO
          pas_pcm_init (hw_config);
 #endif
@@ -342,15 +323,12 @@ attach_pas_card (struct address_info *hw_config)
          sb_dsp_disable_midi (pas_sb_base);    /* No MIDI capability */
 #endif
 
-
 #ifdef CONFIG_MIDI
          pas_midi_init ();
 #endif
-
          pas_init_mixer ();
        }
     }
-
 }
 
 int
index 74d04750e432a7151d845e7a8f3033e308afc4e0..b9dce0b416653966b7185b6c27543b56e3d6c472 100644 (file)
@@ -4,7 +4,7 @@
  * The low level driver for the PAS Midi Interface.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -39,7 +39,7 @@ pas_midi_open (int dev, int mode,
 
   if (midi_busy)
     {
-      printk ("PAS2: Midi busy\n");
+      printk ("PAS16: Midi busy\n");
       return -EBUSY;
     }
 
index 917d37809e4869a779f25f74e2de48ecfc2bbe2c..8a240e28b3273b9a3790a8578a8f741f07d2084e 100644 (file)
@@ -1,5 +1,3 @@
-#define _PAS2_MIXER_C_
-
 /*
  * sound/pas2_mixer.c
  *
@@ -7,7 +5,7 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -85,17 +83,13 @@ mixer_output (int right_vol, int left_vol, int div, int bits,
 
 
   if (bits & 0x10)
-    {                          /*
-                                * Select input or output mixer
-                                */
+    {
       left |= mixer;
       right |= mixer;
     }
 
   if (bits == 0x03 || bits == 0x04)
-    {                          /*
-                                * Bass and treble are mono devices
-                                */
+    {
       mix_write (0x80 | bits, 0x078B);
       mix_write (left, 0x078B);
       right_vol = left_vol;
@@ -111,7 +105,7 @@ mixer_output (int right_vol, int left_vol, int div, int bits,
   return (left_vol | (right_vol << 8));
 }
 
-void
+static void
 set_mode (int new_mode)
 {
   mix_write (0x80 | 0x05, 0x078B);
@@ -219,7 +213,7 @@ pas_mixer_reset (void)
   set_mode (0x04 | 0x01);
 }
 
-int
+static int
 pas_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   DEB (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
@@ -228,14 +222,14 @@ pas_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
     {
       int             level;
 
-      get_user (level, (int *) arg);
+      level = *(int *) arg;
 
       if (level == -1)         /* Return current settings */
        {
          if (mode_control & 0x04)
-           return ioctl_out (arg, 1);
+           return (*(int *) arg = 1);
          else
-           return ioctl_out (arg, 0);
+           return (*(int *) arg = 0);
        }
       else
        {
@@ -243,7 +237,7 @@ pas_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
          if (level)
            mode_control |= 0x04;
          set_mode (mode_control);
-         return ioctl_out (arg, !!level);      /* 0 or 1 */
+         return (*(int *) arg = !!level);      /* 0 or 1 */
        }
     }
 
@@ -252,13 +246,13 @@ pas_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
     {
       int             level;
 
-      get_user (level, (int *) arg);
+      level = *(int *) arg;
 
       if (level == -1)         /* Return current settings */
        {
          if (!(mode_control & 0x03))
-           return ioctl_out (arg, 0);
-         return ioctl_out (arg, ((mode_control & 0x03) + 1) * 20);
+           return (*(int *) arg = 0);
+         return (*(int *) arg = ((mode_control & 0x03) + 1) * 20);
        }
       else
        {
@@ -283,11 +277,11 @@ pas_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
     {
       int             level;
 
-      get_user (level, (int *) arg);
+      level = *(int *) arg;
 
       if (level == -1)         /* Return current settings */
        {
-         return ioctl_out (arg, !(pas_read (0x0B8A) & 0x20));
+         return (*(int *) arg = !(pas_read (0x0B8A) & 0x20));
        }
       else
        {
@@ -306,41 +300,39 @@ pas_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
     {
       int             v;
 
-      get_user (v, (int *) arg);
+      v = *(int *) arg;
 
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
-       return ioctl_out (arg, pas_mixer_set (cmd & 0xff, v));
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
+       return (*(int *) arg = pas_mixer_set (cmd & 0xff, v));
       else
-       {                       /*
-                                * Read parameters
-                                */
+       {
 
          switch (cmd & 0xff)
            {
 
            case SOUND_MIXER_RECSRC:
-             return ioctl_out (arg, rec_devices);
+             return (*(int *) arg = rec_devices);
              break;
 
            case SOUND_MIXER_STEREODEVS:
-             return ioctl_out (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
+             return (*(int *) arg = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
              break;
 
            case SOUND_MIXER_DEVMASK:
-             return ioctl_out (arg, SUPPORTED_MIXER_DEVICES);
+             return (*(int *) arg = SUPPORTED_MIXER_DEVICES);
              break;
 
            case SOUND_MIXER_RECMASK:
-             return ioctl_out (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
+             return (*(int *) arg = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
              break;
 
            case SOUND_MIXER_CAPS:
-             return ioctl_out (arg, 0);        /* No special capabilities */
+             return (*(int *) arg = 0);        /* No special capabilities */
              break;
 
 
            default:
-             return ioctl_out (arg, levels[cmd & 0xff]);
+             return (*(int *) arg = levels[cmd & 0xff]);
            }
        }
     }
index 63f6762a563583f57b3faf634e184bcbc7a30f81..87446e147c2030ca260c6423b7a42c56c84f51ac 100644 (file)
@@ -1,11 +1,15 @@
-#define _PAS2_PCM_C_
 /*
- * sound/pas2_pcm.c
+ * pas2_pcm.c Audio routines for PAS16
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
- * The low level driver for the Pro Audio Spectrum ADC/DAC.
+ * 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"
 
 #if defined(CONFIG_PAS) && defined(CONFIG_AUDIO)
@@ -34,7 +38,7 @@ static int      pcm_busy = 0;
 int             pas_audiodev = 0;
 static int      open_mode = 0;
 
-int
+static int
 pcm_set_speed (int arg)
 {
   int             foo, tmp;
@@ -106,7 +110,7 @@ pcm_set_speed (int arg)
   return pcm_speed;
 }
 
-int
+static int
 pcm_set_channels (int arg)
 {
 
@@ -118,15 +122,13 @@ pcm_set_channels (int arg)
       pas_write (pas_read (0xF8A) ^ 0x20, 0xF8A);
 
       pcm_channels = arg;
-      pcm_set_speed (pcm_speed);       /*
-                                          * The speed must be reinitialized
-                                        */
+      pcm_set_speed (pcm_speed);       /* The speed must be reinitialized */
     }
 
   return pcm_channels;
 }
 
-int
+static int
 pcm_set_bits (int arg)
 {
   if (arg == 0)
@@ -146,7 +148,7 @@ pcm_set_bits (int arg)
 }
 
 static int
-pas_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+pas_audio_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   int             val;
 
@@ -155,62 +157,35 @@ pas_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
   switch (cmd)
     {
     case SOUND_PCM_WRITE_RATE:
-      if (local)
-       return pcm_set_speed ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, pcm_set_speed (val));
+      val = *(int *) arg;
+      return (*(int *) arg = pcm_set_speed (val));
       break;
 
     case SOUND_PCM_READ_RATE:
-      if (local)
-       return pcm_speed;
-      return ioctl_out (arg, pcm_speed);
+      return (*(int *) arg = pcm_speed);
       break;
 
     case SNDCTL_DSP_STEREO:
-      if (local)
-       return pcm_set_channels ((int) arg + 1) - 1;
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, pcm_set_channels (val + 1) - 1);
+      val = *(int *) arg;
+      return (*(int *) arg = pcm_set_channels (val + 1) - 1);
       break;
 
     case SOUND_PCM_WRITE_CHANNELS:
-      if (local)
-       return pcm_set_channels ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, pcm_set_channels (val));
+      val = *(int *) arg;
+      return (*(int *) arg = pcm_set_channels (val));
       break;
 
     case SOUND_PCM_READ_CHANNELS:
-      if (local)
-       return pcm_channels;
-      return ioctl_out (arg, pcm_channels);
+      return (*(int *) arg = pcm_channels);
       break;
 
     case SNDCTL_DSP_SETFMT:
-      if (local)
-       return pcm_set_bits ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, pcm_set_bits (val));
+      val = *(int *) arg;
+      return (*(int *) arg = pcm_set_bits (val));
       break;
 
     case SOUND_PCM_READ_BITS:
-      if (local)
-       return pcm_bits;
-      return ioctl_out (arg, pcm_bits);
-
-    case SOUND_PCM_WRITE_FILTER:       /*
-                                        * NOT YET IMPLEMENTED
-                                        */
-      get_user (val, (int *) arg);
-      if (val > 1)
-       return -EINVAL;
-      pcm_filter = val;
-      break;
-
-    case SOUND_PCM_READ_FILTER:
-      return ioctl_out (arg, pcm_filter);
-      break;
+      return (*(int *) arg = pcm_bits);
 
     default:
       return -EINVAL;
@@ -224,7 +199,7 @@ pas_audio_reset (int dev)
 {
   DEB (printk ("pas2_pcm.c: static void pas_audio_reset(void)\n"));
 
-  pas_write (pas_read (0xF8A) & ~0x40, 0xF8A);
+  pas_write (pas_read (0xF8A) & ~0x40, 0xF8A); /* Disable PCM */
 }
 
 static int
@@ -276,22 +251,20 @@ pas_audio_close (int dev)
 
 static void
 pas_audio_output_block (int dev, unsigned long buf, int count,
-                       int intrflag, int restart_dma)
+                       int intrflag)
 {
   unsigned long   flags, cnt;
 
   DEB (printk ("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
 
   cnt = count;
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     cnt >>= 1;
 
   if (audio_devs[dev]->flags & DMA_AUTOMODE &&
       intrflag &&
       cnt == pcm_count)
-    return;                    /*
-                                * Auto mode on. No need to react
-                                */
+    return;
 
   save_flags (flags);
   cli ();
@@ -299,10 +272,9 @@ pas_audio_output_block (int dev, unsigned long buf, int count,
   pas_write (pas_read (0xF8A) & ~0x40,
             0xF8A);
 
-  if (restart_dma)
-    DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
 
   if (count != pcm_count)
@@ -327,7 +299,7 @@ pas_audio_output_block (int dev, unsigned long buf, int count,
 
 static void
 pas_audio_start_input (int dev, unsigned long buf, int count,
-                      int intrflag, int restart_dma)
+                      int intrflag)
 {
   unsigned long   flags;
   int             cnt;
@@ -335,23 +307,20 @@ pas_audio_start_input (int dev, unsigned long buf, int count,
   DEB (printk ("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
 
   cnt = count;
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     cnt >>= 1;
 
   if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
       intrflag &&
       cnt == pcm_count)
-    return;                    /*
-                                * Auto mode on. No need to react
-                                */
+    return;
 
   save_flags (flags);
   cli ();
 
-  if (restart_dma)
-    DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
 
   if (count != pcm_count)
@@ -398,12 +367,14 @@ pas_audio_trigger (int dev, int state)
 static int
 pas_audio_prepare_for_input (int dev, int bsize, int bcount)
 {
+  pas_audio_reset (dev);
   return 0;
 }
 
 static int
 pas_audio_prepare_for_output (int dev, int bsize, int bcount)
 {
+  pas_audio_reset (dev);
   return 0;
 }
 
@@ -417,7 +388,6 @@ static struct audio_driver pas_audio_driver =
   pas_audio_prepare_for_input,
   pas_audio_prepare_for_output,
   pas_audio_reset,
-  pas_audio_reset,
   NULL,
   NULL,
   NULL,
@@ -425,15 +395,6 @@ static struct audio_driver pas_audio_driver =
   pas_audio_trigger
 };
 
-static struct audio_operations pas_audio_operations =
-{
-  "Pro Audio Spectrum",
-  DMA_AUTOMODE,
-  AFMT_U8 | AFMT_S16_LE,
-  NULL,
-  &pas_audio_driver
-};
-
 void
 pas_pcm_init (struct address_info *hw_config)
 {
@@ -447,20 +408,28 @@ pas_pcm_init (struct address_info *hw_config)
 
   if (num_audiodevs < MAX_AUDIO_DEV)
     {
-      audio_devs[pas_audiodev = num_audiodevs++] = &pas_audio_operations;
-      audio_devs[pas_audiodev]->dmachan1 = hw_config->dma;
-      audio_devs[pas_audiodev]->buffsize = DSP_BUFFSIZE;
+
+      if ((pas_audiodev = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
+                                                 "Pro Audio Spectrum",
+                                                 &pas_audio_driver,
+                                              sizeof (struct audio_driver),
+                                                 DMA_AUTOMODE,
+                                                 AFMT_U8 | AFMT_S16_LE,
+                                                 NULL,
+                                                 hw_config->dma,
+                                                 hw_config->dma)) < 0)
+       {
+         return;
+       }
     }
   else
-    printk ("PAS2: Too many PCM devices available\n");
+    printk ("PAS16: Too many PCM devices available\n");
 }
 
 void
 pas_pcm_interrupt (unsigned char status, int cause)
 {
-  if (cause == 1)              /*
-                                * PCM buffer done
-                                */
+  if (cause == 1)
     {
       /*
        * Halt the PCM first. Otherwise we don't have time to start a new
index f7b36e50110133b8e2e56265cc07c090b50ad678..52374790f7bbac3d8d3f3cadedd20c1fac676ece 100644 (file)
@@ -4,7 +4,7 @@
  * The low level driver for the Personal Sound System (ECHO ESC614).
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -348,7 +348,7 @@ attach_pss (struct address_info *hw_config)
   /*
      * Disable all emulations. Will be enabled later (if required).
    */
-  outw (0x0000, REG (CONF_PSS));
+  outw (0x0000, REG (CONF_PSS));       /* 0x0400 enables joystick */
   outw (0x0000, REG (CONF_WSS));
   outw (0x0000, REG (CONF_SB));
   outw (0x0000, REG (CONF_MIDI));
@@ -536,7 +536,7 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
        if (buf == NULL)
          return -ENOSPC;
 
-       copy_from_user ((char *) buf, &((char *) arg)[0], sizeof (*buf));
+       memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
        err = download_boot_block (dev_info, buf);
        vfree (buf);
        return err;
@@ -554,7 +554,7 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
        if (buf == NULL)
          return -ENOSPC;
 
-       copy_from_user ((char *) buf, &((char *) arg)[0], sizeof (*buf));
+       memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
 
        data = (unsigned short *) (buf->data);
 
@@ -567,11 +567,7 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
              {
                restore_flags (flags);
                buf->len = i;   /* feed back number of WORDs sent */
-               {
-                 char           *fixit = (char *) buf;
-
-                 copy_to_user (&((char *) arg)[0], fixit, sizeof (*buf));
-               };
+               memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
                vfree (buf);
                return -EIO;
              }
@@ -616,11 +612,7 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
 
        restore_flags (flags);
 
-       {
-         char           *fixit = (char *) buf;
-
-         copy_to_user (&((char *) arg)[0], fixit, sizeof (*buf));
-       };
+       memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
        vfree (buf);
 
        return err;
@@ -634,7 +626,7 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
        unsigned long   flags;
        unsigned short  tmp;
 
-       copy_from_user ((char *) &buf, &((char *) arg)[0], sizeof (buf));
+       memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
 
        save_flags (flags);
        cli ();
@@ -659,11 +651,7 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
        buf.parm1 = tmp;
        restore_flags (flags);
 
-       {
-         char           *fixit = (char *) &buf;
-
-         copy_to_user (&((char *) arg)[0], fixit, sizeof (buf));
-       };
+       memcpy ((&((char *) arg)[0]), (char *) &buf, sizeof (buf));
        return 0;
       }
       break;
@@ -674,7 +662,7 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
        unsigned long   flags;
        unsigned short  tmp;
 
-       copy_from_user ((char *) &buf, &((char *) arg)[0], sizeof (buf));
+       memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
 
        save_flags (flags);
        cli ();
@@ -708,7 +696,7 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
        unsigned long   flags;
        unsigned short  tmp;
 
-       copy_from_user ((char *) &buf, &((char *) arg)[0], sizeof (buf));
+       memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
 
        save_flags (flags);
        cli ();
@@ -749,7 +737,7 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
        unsigned long   flags;
        unsigned short  tmp;
 
-       copy_from_user ((char *) &buf, &((char *) arg)[0], sizeof (buf));
+       memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
 
        save_flags (flags);
        cli ();
@@ -783,11 +771,7 @@ pss_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
 
        restore_flags (flags);
 
-       {
-         char           *fixit = (char *) &buf;
-
-         copy_to_user (&((char *) arg)[0], fixit, sizeof (buf));
-       };
+       memcpy ((&((char *) arg)[0]), (char *) &buf, sizeof (buf));
        return 0;
       }
       break;
index fc43428779696598f908a71269e8971a22898f9b..b229549bb4793e51c8d95dd798955666f0f6ef0f 100644 (file)
@@ -41,6 +41,8 @@
 #define MDL_SMW                11      /* Logitech SoundMan Wave (Jazz16) */
 #define MDL_ESS                12      /* ESS ES688 and ES1688 */
 #define MDL_AZTECH     13      /* Aztech Sound Galaxy family */
+#define MDL_ES1868MIDI 14      /* MIDI port of ESS1868 */
+#define MDL_AEDSP      15      /* Audio Excel DSP 16 */
 
 /*
  * Config flags
@@ -107,7 +109,6 @@ typedef struct sb_devc {
        } sb_devc;
 
 int sb_dsp_command (sb_devc *devc, unsigned char val);
-int sb_dsp_get_byte (sb_devc *devc);
 int sb_dsp_reset (sb_devc *devc);
 void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value);
 unsigned int sb_getmixer (sb_devc *devc, unsigned int port);
@@ -115,6 +116,7 @@ int sb_dsp_detect (struct address_info *hw_config);
 void sb_dsp_init (struct address_info *hw_config);
 void sb_dsp_unload(struct address_info *hw_config);
 int sb_mixer_init(sb_devc *devc);
+void sb_mixer_set_stereo (sb_devc *devc, int mode);
 void smw_mixer_init(sb_devc *devc);
 void sb_dsp_midi_init (sb_devc *devc);
 void sb_audio_init (sb_devc *devc, char *name);
index 650ce75ef54dcc551e00212d566f6b79bd40d1f4..a4e29ba8c6a205a6685111f0e88e3993b71a9149 100644 (file)
@@ -4,7 +4,7 @@
  * Audio routines for Sound Blaster compatible cards.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -67,8 +67,8 @@ sb_audio_close (int dev)
 {
   sb_devc        *devc = audio_devs[dev]->devc;
 
-  audio_devs[dev]->dmachan1 =
-    audio_devs[dev]->dmachan2 =
+  audio_devs[dev]->dmap_in->dma =
+    audio_devs[dev]->dmap_out->dma =
     devc->dma8;
 
   if (devc->dma16 != -1 && devc->dma16 != devc->dma8)
@@ -79,27 +79,24 @@ sb_audio_close (int dev)
 
 static void
 sb_set_output_parms (int dev, unsigned long buf, int nr_bytes,
-                    int intrflag, int restart_dma)
+                    int intrflag)
 {
   sb_devc        *devc = audio_devs[dev]->devc;
 
   devc->trg_buf = buf;
   devc->trg_bytes = nr_bytes;
   devc->trg_intrflag = intrflag;
-  devc->trg_restart = restart_dma;
   devc->irq_mode = IMODE_OUTPUT;
 }
 
 static void
-sb_set_input_parms (int dev, unsigned long buf, int count, int intrflag,
-                   int restart_dma)
+sb_set_input_parms (int dev, unsigned long buf, int count, int intrflag)
 {
   sb_devc        *devc = audio_devs[dev]->devc;
 
   devc->trg_buf = buf;
   devc->trg_bytes = count;
   devc->trg_intrflag = intrflag;
-  devc->trg_restart = restart_dma;
   devc->irq_mode = IMODE_INPUT;
 }
 
@@ -109,15 +106,15 @@ sb_set_input_parms (int dev, unsigned long buf, int count, int intrflag,
 
 static void
 sb1_audio_output_block (int dev, unsigned long buf, int nr_bytes,
-                       int intrflag, int restart_dma)
+                       int intrflag)
 {
   unsigned long   flags;
   int             count = nr_bytes;
   sb_devc        *devc = audio_devs[dev]->devc;
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -137,8 +134,7 @@ sb1_audio_output_block (int dev, unsigned long buf, int nr_bytes,
 }
 
 static void
-sb1_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag,
-                      int restart_dma)
+sb1_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag)
 {
   unsigned long   flags;
   int             count = nr_bytes;
@@ -148,9 +144,9 @@ sb1_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag,
    * Start a DMA input to the buffer pointed by dmaqtail
    */
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -185,12 +181,12 @@ sb1_audio_trigger (int dev, int bits)
        {
        case IMODE_INPUT:
          sb1_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
-                                devc->trg_intrflag, devc->trg_restart);
+                                devc->trg_intrflag);
          break;
 
        case IMODE_OUTPUT:
          sb1_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
-                                 devc->trg_intrflag, devc->trg_restart);
+                                 devc->trg_intrflag);
          break;
        }
     }
@@ -279,9 +275,13 @@ sb1_audio_set_bits (int dev, unsigned int bits)
 static void
 sb1_audio_halt_xfer (int dev)
 {
+  unsigned long   flags;
   sb_devc        *devc = audio_devs[dev]->devc;
 
+  save_flags (flags);
+  cli ();
   sb_dsp_reset (devc);
+  restore_flags (flags);
 }
 
 /*
@@ -290,16 +290,16 @@ sb1_audio_halt_xfer (int dev)
 
 static void
 sb20_audio_output_block (int dev, unsigned long buf, int nr_bytes,
-                        int intrflag, int restart_dma)
+                        int intrflag)
 {
   unsigned long   flags;
   int             count = nr_bytes;
   sb_devc        *devc = audio_devs[dev]->devc;
   unsigned char   cmd;
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -328,8 +328,7 @@ sb20_audio_output_block (int dev, unsigned long buf, int nr_bytes,
 }
 
 static void
-sb20_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag,
-                       int restart_dma)
+sb20_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag)
 {
   unsigned long   flags;
   int             count = nr_bytes;
@@ -340,9 +339,9 @@ sb20_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag,
    * Start a DMA input to the buffer pointed by dmaqtail
    */
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -385,12 +384,12 @@ sb20_audio_trigger (int dev, int bits)
        {
        case IMODE_INPUT:
          sb20_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
-                                 devc->trg_intrflag, devc->trg_restart);
+                                 devc->trg_intrflag);
          break;
 
        case IMODE_OUTPUT:
          sb20_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
-                                  devc->trg_intrflag, devc->trg_restart);
+                                  devc->trg_intrflag);
          break;
        }
     }
@@ -444,8 +443,8 @@ sbpro_audio_prepare_for_input (int dev, int bsize, int bcount)
   unsigned char   bits = 0;
 
   if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
-    audio_devs[dev]->dmachan1 =
-      audio_devs[dev]->dmachan2 =
+    audio_devs[dev]->dmap_out->dma =
+      audio_devs[dev]->dmap_in->dma =
       devc->bits == 16 ? devc->dma16 : devc->dma8;
 
   if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
@@ -476,9 +475,11 @@ sbpro_audio_prepare_for_output (int dev, int bsize, int bcount)
   unsigned char   bits = 0;
 
   if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
-    audio_devs[dev]->dmachan1 =
-      audio_devs[dev]->dmachan2 =
+    audio_devs[dev]->dmap_out->dma =
+      audio_devs[dev]->dmap_in->dma =
       devc->bits == 16 ? devc->dma16 : devc->dma8;
+  if (devc->model == MDL_SBPRO)
+    sb_mixer_set_stereo (devc, devc->channels == 2);
 
   save_flags (flags);
   cli ();
@@ -747,18 +748,15 @@ ess_audio_prepare_for_output (int dev, int bsize, int bcount)
 
 static void
 ess_audio_output_block (int dev, unsigned long buf, int nr_bytes,
-                       int intrflag, int restart_dma)
+                       int intrflag)
 {
   int             count = nr_bytes;
   sb_devc        *devc = audio_devs[dev]->devc;
   short           c = -nr_bytes;
 
-  if (!restart_dma)
-    return;
-
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -772,23 +770,19 @@ ess_audio_output_block (int dev, unsigned long buf, int nr_bytes,
 }
 
 static void
-ess_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag,
-                      int restart_dma)
+ess_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag)
 {
   int             count = nr_bytes;
   sb_devc        *devc = audio_devs[dev]->devc;
   short           c = -nr_bytes;
 
-  if (!restart_dma)
-    return;
-
   /*
    * Start a DMA input to the buffer pointed by dmaqtail
    */
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -816,12 +810,12 @@ ess_audio_trigger (int dev, int bits)
        {
        case IMODE_INPUT:
          ess_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
-                                devc->trg_intrflag, devc->trg_restart);
+                                devc->trg_intrflag);
          break;
 
        case IMODE_OUTPUT:
          ess_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
-                                 devc->trg_intrflag, devc->trg_restart);
+                                 devc->trg_intrflag);
          break;
        }
     }
@@ -871,8 +865,8 @@ sb16_audio_prepare_for_input (int dev, int bsize, int bcount)
 {
   sb_devc        *devc = audio_devs[dev]->devc;
 
-  audio_devs[dev]->dmachan1 =
-    audio_devs[dev]->dmachan2 =
+  audio_devs[dev]->dmap_out->dma =
+    audio_devs[dev]->dmap_in->dma =
     devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8;
 
   devc->trigger_bits = 0;
@@ -884,8 +878,8 @@ sb16_audio_prepare_for_output (int dev, int bsize, int bcount)
 {
   sb_devc        *devc = audio_devs[dev]->devc;
 
-  audio_devs[dev]->dmachan1 =
-    audio_devs[dev]->dmachan2 =
+  audio_devs[dev]->dmap_out->dma =
+    audio_devs[dev]->dmap_in->dma =
     devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8;
 
   devc->trigger_bits = 0;
@@ -894,7 +888,7 @@ sb16_audio_prepare_for_output (int dev, int bsize, int bcount)
 
 static void
 sb16_audio_output_block (int dev, unsigned long buf, int count,
-                        int intrflag, int restart_dma)
+                        int intrflag)
 {
   unsigned long   flags, cnt;
   sb_devc        *devc = audio_devs[dev]->devc;
@@ -902,9 +896,6 @@ sb16_audio_output_block (int dev, unsigned long buf, int count,
   devc->irq_mode = IMODE_OUTPUT;
   devc->intr_active = 1;
 
-  if (!restart_dma)
-    return;
-
   cnt = count;
   if (devc->bits == AFMT_S16_LE)
     cnt >>= 1;
@@ -913,8 +904,7 @@ sb16_audio_output_block (int dev, unsigned long buf, int count,
   save_flags (flags);
   cli ();
 
-  if (restart_dma)
-    DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 
   sb_dsp_command (devc, 0x41);
   sb_dsp_command (devc, (unsigned char) ((devc->speed >> 8) & 0xff));
@@ -930,8 +920,7 @@ sb16_audio_output_block (int dev, unsigned long buf, int count,
 }
 
 static void
-sb16_audio_start_input (int dev, unsigned long buf, int count, int intrflag,
-                       int restart_dma)
+sb16_audio_start_input (int dev, unsigned long buf, int count, int intrflag)
 {
   unsigned long   flags, cnt;
   sb_devc        *devc = audio_devs[dev]->devc;
@@ -939,9 +928,6 @@ sb16_audio_start_input (int dev, unsigned long buf, int count, int intrflag,
   devc->irq_mode = IMODE_INPUT;
   devc->intr_active = 1;
 
-  if (!restart_dma)
-    return;
-
   cnt = count;
   if (devc->bits == AFMT_S16_LE)
     cnt >>= 1;
@@ -950,8 +936,7 @@ sb16_audio_start_input (int dev, unsigned long buf, int count, int intrflag,
   save_flags (flags);
   cli ();
 
-  if (restart_dma)
-    DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
   sb_dsp_command (devc, 0x42);
   sb_dsp_command (devc, (unsigned char) ((devc->speed >> 8) & 0xff));
@@ -981,12 +966,12 @@ sb16_audio_trigger (int dev, int bits)
        {
        case IMODE_INPUT:
          sb16_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
-                                 devc->trg_intrflag, devc->trg_restart);
+                                 devc->trg_intrflag);
          break;
 
        case IMODE_OUTPUT:
          sb16_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
-                                  devc->trg_intrflag, devc->trg_restart);
+                                  devc->trg_intrflag);
          break;
        }
     }
@@ -995,23 +980,11 @@ sb16_audio_trigger (int dev, int bits)
 }
 
 static int
-sb_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+sb_audio_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   return -EINVAL;
 }
 
-static void
-sb_audio_reset (int dev)
-{
-  unsigned long   flags;
-  sb_devc        *devc = audio_devs[dev]->devc;
-
-  save_flags (flags);
-  cli ();
-  sb_dsp_reset (devc);
-  restore_flags (flags);
-}
-
 static struct audio_driver sb1_audio_driver =  /* SB1.x */
 {
   sb_audio_open,
@@ -1021,7 +994,6 @@ static struct audio_driver sb1_audio_driver =      /* SB1.x */
   sb_audio_ioctl,
   sb1_audio_prepare_for_input,
   sb1_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,                                /* local_qlen */
   NULL,                                /* copy_from_user */
@@ -1042,7 +1014,6 @@ static struct audio_driver sb20_audio_driver =    /* SB2.0 */
   sb_audio_ioctl,
   sb1_audio_prepare_for_input,
   sb1_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,                                /* local_qlen */
   NULL,                                /* copy_from_user */
@@ -1063,7 +1034,6 @@ static struct audio_driver sb201_audio_driver =           /* SB2.01 */
   sb_audio_ioctl,
   sb1_audio_prepare_for_input,
   sb1_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,                                /* local_qlen */
   NULL,                                /* copy_from_user */
@@ -1084,7 +1054,6 @@ static struct audio_driver sbpro_audio_driver =           /* SB Pro */
   sb_audio_ioctl,
   sbpro_audio_prepare_for_input,
   sbpro_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,                                /* local_qlen */
   NULL,                                /* copy_from_user */
@@ -1105,7 +1074,6 @@ static struct audio_driver jazz16_audio_driver =  /* Jazz16 and SM Wave */
   sb_audio_ioctl,
   sbpro_audio_prepare_for_input,
   sbpro_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,                                /* local_qlen */
   NULL,                                /* copy_from_user */
@@ -1126,7 +1094,6 @@ static struct audio_driver sb16_audio_driver =    /* SB16 */
   sb_audio_ioctl,
   sb16_audio_prepare_for_input,
   sb16_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,                                /* local_qlen */
   NULL,                                /* copy_from_user */
@@ -1147,7 +1114,6 @@ static struct audio_driver ess_audio_driver =     /* ESS ES688/1688 */
   sb_audio_ioctl,
   ess_audio_prepare_for_input,
   ess_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,                                /* local_qlen */
   NULL,                                /* copy_from_user */
index 7f87dd34a5503e3981391b19b43cf756b51c5def..d6e4bc67ae163c8cbcf9793cbdd06eee3df270b6 100644 (file)
@@ -4,7 +4,7 @@
  * Detection routine for the Sound Blaster cards.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
index f449286e5b6c608ccf13d4b5e11be897ee07e78c..3faf6b690b04ce2130d9d58d2dc95d85c0243d94 100644 (file)
@@ -4,7 +4,7 @@
  * Common routines for Sound Blaster compatible cards.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -47,8 +47,8 @@ static unsigned char jazz16_bits = 0; /* I/O relocation bits */
 #ifdef SMW_MIDI0001_INCLUDED
 #include "smw-midi0001.h"
 #else
-unsigned char  *smw_ucode = NULL;
-int             smw_ucodeLen = 0;
+static unsigned char *smw_ucode = NULL;
+static int      smw_ucodeLen = 0;
 
 #endif
 
@@ -83,7 +83,7 @@ sb_dsp_command (sb_devc * devc, unsigned char val)
   return 0;
 }
 
-int
+static int
 sb_dsp_get_byte (sb_devc * devc)
 {
   int             i;
@@ -122,7 +122,7 @@ ess_read (sb_devc * devc, unsigned char reg)
   return sb_dsp_get_byte (devc);
 }
 
-void
+static void
 sbintr (int irq, void *dev_id, struct pt_regs *dummy)
 {
   int             status;
@@ -315,8 +315,6 @@ sb16_set_irq_hw (sb_devc * devc, int level)
       ival = 8;
       break;
     default:
-      if (devc->type == MDL_SBPNP)
-       return 1;
       printk ("SB16 IRQ%d is not possible\n", level);
       return 0;
     }
@@ -430,6 +428,7 @@ init_Jazz16 (sb_devc * devc, struct address_info *hw_config)
 
 
   hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc (strlen (name + 1)));
+  sound_mem_sizes[sound_nblocks] = strlen (name + 1);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (hw_config->name != NULL)
@@ -496,6 +495,7 @@ ess_init (sb_devc * devc, struct address_info *hw_config)
   devc->submodel = ess_minor & 0x0f;
 
   hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc (strlen (name + 1)));
+  sound_mem_sizes[sound_nblocks] = strlen (name + 1);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (hw_config->name != NULL)
@@ -621,19 +621,21 @@ sb_dsp_detect (struct address_info *hw_config)
     dsp_get_vers (devc);
 
   if (devc->major == 3 && devc->minor == 1)
-    if (devc->type == MDL_AZTECH)      /* SG Washington? */
-      {
-       if (sb_dsp_command (devc, 0x09))
-         if (sb_dsp_command (devc, 0x00))      /* Enter WSS mode */
-           {
-             int             i;
+    {
+      if (devc->type == MDL_AZTECH)    /* SG Washington? */
+       {
+         if (sb_dsp_command (devc, 0x09))
+           if (sb_dsp_command (devc, 0x00))    /* Enter WSS mode */
+             {
+               int             i;
 
-             /* Have some delay */
-             for (i = 0; i < 10000; i++)
-               inb (DSP_DATA_AVAIL);
-             devc->caps = SB_NO_AUDIO | SB_NO_MIDI;    /* Mixer only */
-           }
-      }
+               /* Have some delay */
+               for (i = 0; i < 10000; i++)
+                 inb (DSP_DATA_AVAIL);
+               devc->caps = SB_NO_AUDIO | SB_NO_MIDI;  /* Mixer only */
+             }
+       }
+    }
 
 /*
  * Save device information for sb_dsp_init()
@@ -641,6 +643,7 @@ sb_dsp_detect (struct address_info *hw_config)
 
 
   detected_devc = (sb_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (sb_devc)));
+  sound_mem_sizes[sound_nblocks] = sizeof (sb_devc);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
 
@@ -724,6 +727,10 @@ sb_dsp_init (struct address_info *hw_config)
              }
        }
 
+#ifdef __SMP__
+      /* Skip IRQ detection if SMP (doesn't work) */
+      devc->irq_ok = 1;
+#else
       if (devc->major == 4 && devc->minor <= 11)       /* Won't work */
        devc->irq_ok = 1;
       else
@@ -738,15 +745,16 @@ sb_dsp_init (struct address_info *hw_config)
 
          if (!devc->irq_ok)
            {
-             printk ("sb: Interrupt test on IRQ%d failed - Propable IRQ conflict\n", devc->irq);
+             printk ("sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq);
            }
          else
            {
              DDB (printk ("IRQ test OK (IRQ%d)\n", devc->irq));
            }
 
-       }                       /* IRQ setup */
-    }
+       }
+#endif /* __SMP__ */
+    }                          /* IRQ setup */
 
   request_region (hw_config->io_base, 16, "soundblaster");
 
index 12ec822bf1493570ee8569f9803368bad8edd0f7..2bdeef1ba7eb92bddafc67e39ce40bde573cf623 100644 (file)
@@ -4,7 +4,7 @@
  * The low level driver for the Sound Blaster DS chips.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -28,7 +28,6 @@
  * future version of this driver.
  */
 
-void            (*midi_input_intr) (int dev, unsigned char data);
 
 static int
 sb_midi_open (int dev, int mode,
@@ -188,6 +187,7 @@ sb_dsp_midi_init (sb_devc * devc)
 
 
   midi_devs[num_midis] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct midi_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -204,6 +204,7 @@ sb_dsp_midi_init (sb_devc * devc)
 
 
   midi_devs[num_midis]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -217,6 +218,7 @@ sb_dsp_midi_init (sb_devc * devc)
   memcpy ((char *) midi_devs[num_midis]->converter, (char *) &std_midi_synth,
          sizeof (struct synth_operations));
 
+  midi_devs[num_midis]->converter->id = "SBMIDI";
   num_midis++;
 }
 
index 0c156753955ebfb297ecb4c644b615bbe292256c..5e6c578996816f78d880dbf4e7dff7d5ac68b7a1 100644 (file)
@@ -5,7 +5,7 @@
  * The low level mixer driver for the Sound Blaster compatible cards.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -24,7 +24,7 @@
 
 static int      sbmixnum = 1;
 
-void            sb_mixer_reset (sb_devc * devc);
+static void     sb_mixer_reset (sb_devc * devc);
 
 void
 sb_mixer_set_stereo (sb_devc * devc, int mode)
@@ -311,7 +311,7 @@ sb_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
     {
       int             tmp;
 
-      get_user (tmp, (int *) arg);
+      tmp = *(int *) arg;
 
       sb_setmixer (devc, 0x43, (~tmp) & 0x01);
       return 0;
@@ -319,48 +319,48 @@ sb_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
 
   if (((cmd >> 8) & 0xff) == 'M')
     {
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
        switch (cmd & 0xff)
          {
          case SOUND_MIXER_RECSRC:
-           get_user (val, (int *) arg);
-           return ioctl_out (arg, set_recmask (devc, val));
+           val = *(int *) arg;
+           return (*(int *) arg = set_recmask (devc, val));
            break;
 
          default:
 
-           get_user (val, (int *) arg);
-           return ioctl_out (arg, sb_mixer_set (devc, cmd & 0xff, val));
+           val = *(int *) arg;
+           return (*(int *) arg = sb_mixer_set (devc, cmd & 0xff, val));
          }
       else
        switch (cmd & 0xff)
          {
 
          case SOUND_MIXER_RECSRC:
-           return ioctl_out (arg, devc->recmask);
+           return (*(int *) arg = devc->recmask);
            break;
 
          case SOUND_MIXER_DEVMASK:
-           return ioctl_out (arg, devc->supported_devices);
+           return (*(int *) arg = devc->supported_devices);
            break;
 
          case SOUND_MIXER_STEREODEVS:
            if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
-             return ioctl_out (arg, devc->supported_devices);
+             return (*(int *) arg = devc->supported_devices);
            else
-             return ioctl_out (arg, devc->supported_devices & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
+             return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
            break;
 
          case SOUND_MIXER_RECMASK:
-           return ioctl_out (arg, devc->supported_rec_devices);
+           return (*(int *) arg = devc->supported_rec_devices);
            break;
 
          case SOUND_MIXER_CAPS:
-           return ioctl_out (arg, devc->mixer_caps);
+           return (*(int *) arg = devc->mixer_caps);
            break;
 
          default:
-           return ioctl_out (arg, sb_mixer_get (devc, cmd & 0xff));
+           return (*(int *) arg = sb_mixer_get (devc, cmd & 0xff));
          }
     }
   else
@@ -374,7 +374,7 @@ static struct mixer_operations sb_mixer_operations =
   sb_mixer_ioctl
 };
 
-void
+static void
 sb_mixer_reset (sb_devc * devc)
 {
   char            name[32];
@@ -445,6 +445,7 @@ sb_mixer_init (sb_devc * devc)
 
 
   mixer_devs[num_mixers] = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct mixer_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct mixer_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
index ccd3bb264df5c069973146f124189491afbe15cc..5711b91055d229ad6316bd2ee0280658ed8e1401 100644 (file)
@@ -4,7 +4,7 @@
  * Definitions for the SB Pro and SB16 mixers
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -98,7 +98,7 @@
        {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
 
 #ifdef __SB_MIXER_C__
-mixer_tab sbpro_mix = {
+static mixer_tab sbpro_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,    0x22, 7, 4, 0x22, 3, 4),
 MIX_ENT(SOUND_MIXER_BASS,      0x00, 0, 0, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,    0x00, 0, 0, 0x00, 0, 0),
@@ -112,7 +112,7 @@ MIX_ENT(SOUND_MIXER_IMIX,   0x00, 0, 0, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_ALTPCM,    0x00, 0, 0, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_RECLEV,    0x00, 0, 0, 0x00, 0, 0)
 };
-mixer_tab es688_mix = {
+static mixer_tab es688_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,    0x32, 7, 4, 0x32, 3, 4),
 MIX_ENT(SOUND_MIXER_BASS,      0x00, 0, 0, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,    0x00, 0, 0, 0x00, 0, 0),
@@ -133,7 +133,7 @@ MIX_ENT(SOUND_MIXER_LINE3,  0x00, 0, 0, 0x00, 0, 0)
 };
 
 #ifdef __SGNXPRO__
-mixer_tab sgnxpro_mix = {
+static mixer_tab sgnxpro_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,    0x22, 7, 4, 0x22, 3, 4),
 MIX_ENT(SOUND_MIXER_BASS,      0x46, 2, 3, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,    0x44, 2, 3, 0x00, 0, 0),
@@ -151,7 +151,7 @@ MIX_ENT(SOUND_MIXER_OGAIN,  0x00, 0, 0, 0x00, 0, 0)
 };
 #endif
 
-mixer_tab sb16_mix = {
+static mixer_tab sb16_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,    0x30, 7, 5, 0x31, 7, 5),
 MIX_ENT(SOUND_MIXER_BASS,      0x46, 7, 4, 0x47, 7, 4),
 MIX_ENT(SOUND_MIXER_TREBLE,    0x44, 7, 4, 0x45, 7, 4),
index 9ed04ca704e64fdce4b280f4f426bfbd2e176666..6ffced88e5ba315038711c8f1c44ddb2bca81c17 100644 (file)
@@ -4,14 +4,13 @@
  * The sequencer personality manager.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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 <linux/poll.h>
 
 
 #define SEQUENCER_C
@@ -200,6 +199,7 @@ sequencer_midi_input (int dev, unsigned char data)
     return;
 
   tstamp = jiffies - seq_time;
+
   if (tstamp != prev_input_time)
     {
       tstamp = (tstamp << 8) | SEQ_WAIT;
@@ -267,7 +267,7 @@ sequencer_write (int dev, struct fileinfo *file, const char *buf, int count)
 
       if (ev_code == SEQ_FULLSIZE)
        {
-         int             err;
+         int             err, fmt;
 
          dev = *(unsigned short *) &event_rec[2];
          if (dev < 0 || dev >= max_synthdev)
@@ -276,7 +276,8 @@ sequencer_write (int dev, struct fileinfo *file, const char *buf, int count)
          if (!(synth_open_mask & (1 << dev)))
            return -ENXIO;
 
-         err = synth_devs[dev]->load_patch (dev, *(short *) &event_rec[0], buf, p + 4, c, 0);
+         fmt = (*(short *) &event_rec[0]) & 0xffff;
+         err = synth_devs[dev]->load_patch (dev, fmt, buf, p + 4, c, 0);
          if (err < 0)
            return err;
 
@@ -364,9 +365,7 @@ sequencer_write (int dev, struct fileinfo *file, const char *buf, int count)
   if (!seq_playing)
     seq_startplay ();
 
-  return count;                        /* This will "eat" chunks shorter than 4 bytes (if written
-                                  * alone) Should we really do that ?
-                                */
+  return count;
 }
 
 static int
@@ -429,6 +428,11 @@ extended_event (unsigned char *q)
       if (q[4] > 127 && q[4] != 255)
        return 0;
 
+      if (q[5] == 0)
+       {
+         synth_devs[dev]->kill_note (dev, q[3], q[4], q[5]);
+         break;
+       }
       synth_devs[dev]->start_note (dev, q[3], q[4], q[5]);
       break;
 
@@ -545,13 +549,7 @@ seq_chn_voice_event (unsigned char *event_rec)
            {
              synth_devs[dev]->set_instr (dev, voice, 128 + note);
              synth_devs[dev]->chn_info[chn].pgm_num = 128 + note;
-             note = 60;        /* Middle C */
-
            }
-       }
-
-      if (seq_mode == SEQ_2)
-       {
          synth_devs[dev]->setup_voice (dev, voice, chn);
        }
 
@@ -574,6 +572,7 @@ seq_chn_voice_event (unsigned char *event_rec)
     }
 }
 
+
 static void
 seq_chn_common_event (unsigned char *event_rec)
 {
@@ -806,13 +805,13 @@ seq_sysex_message (unsigned char *event_rec)
     return;
   if (!synth_devs[dev])
     return;
-  if (!synth_devs[dev]->send_sysex)
-    return;
 
   l = 0;
   for (i = 0; i < 6 && buf[i] != 0xff; i++)
     l = i + 1;
 
+  if (!synth_devs[dev]->send_sysex)
+    return;
   if (l > 0)
     synth_devs[dev]->send_sysex (dev, buf, l);
 }
@@ -890,7 +889,7 @@ play_event (unsigned char *q)
       break;
 
     case SEQ_SYNCTIMER:        /*
-                                  * Reset timer
+                                * Reset timer
                                 */
       seq_time = jiffies;
       prev_input_time = 0;
@@ -1044,6 +1043,9 @@ setup_mode2 (void)
     {
       int             chn;
 
+      synth_devs[dev]->sysex_ptr = 0;
+      synth_devs[dev]->emulation = 0;
+
       for (chn = 0; chn < 16; chn++)
        {
          synth_devs[dev]->chn_info[chn].pgm_num = 0;
@@ -1051,6 +1053,7 @@ setup_mode2 (void)
                             synth_devs[dev]->chn_info[chn].controllers,
                             0);
          synth_devs[dev]->chn_info[chn].bender_value = (1 << 7);       /* Neutral */
+         synth_devs[dev]->chn_info[chn].bender_range = 200;
        }
     }
 
@@ -1065,6 +1068,9 @@ sequencer_open (int dev, struct fileinfo *file)
   int             level, tmp;
   unsigned long   flags;
 
+  if (!sequencer_ok)
+    sequencer_init ();
+
   level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
 
   dev = dev >> 4;
@@ -1083,6 +1089,14 @@ sequencer_open (int dev, struct fileinfo *file)
       return -ENXIO;
     }
 
+  if (mode == OPEN_READ)
+    if (!num_midis)
+      {
+       printk ("Sequencer: No MIDI devices. Input not possible\n");
+       sequencer_busy = 0;
+       return -ENXIO;
+      }
+
   save_flags (flags);
   cli ();
   if (sequencer_busy)
@@ -1133,14 +1147,6 @@ sequencer_open (int dev, struct fileinfo *file)
       setup_mode2 ();
     }
 
-  if (seq_mode == SEQ_1 && mode == OPEN_READ)
-    if (!max_mididev)
-      {
-       printk ("Sequencer: No Midi devices. Input not possible\n");
-       sequencer_busy = 0;
-       return -ENXIO;
-      }
-
   if (!max_synthdev && !max_mididev)
     return -ENXIO;
 
@@ -1152,28 +1158,24 @@ sequencer_open (int dev, struct fileinfo *file)
       midi_written[i] = 0;
     }
 
-  /*
-   * if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
-   */
-  for (i = 0; i < max_synthdev; i++)   /*
-                                        * Open synth devices
-                                        */
-    if ((tmp = synth_devs[i]->open (i, mode)) < 0)
-      {
-       printk ("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
-       if (synth_devs[i]->midi_dev)
-         printk ("(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
-      }
-    else
-      {
-       synth_open_mask |= (1 << i);
-       if (synth_devs[i]->midi_dev)    /*
-                                        * Is a midi interface
-                                        */
-         midi_opened[synth_devs[i]->midi_dev] = 1;
-      }
+  for (i = 0; i < max_synthdev; i++)
+    {
+      if ((tmp = synth_devs[i]->open (i, mode)) < 0)
+       {
+         printk ("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
+         if (synth_devs[i]->midi_dev)
+           printk ("(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
+       }
+      else
+       {
+         synth_open_mask |= (1 << i);
+         if (synth_devs[i]->midi_dev)
+           midi_opened[synth_devs[i]->midi_dev] = 1;
+       }
+    }
 
   seq_time = jiffies;
+
   prev_input_time = 0;
   prev_event_time = 0;
 
@@ -1260,7 +1262,7 @@ sequencer_release (int dev, struct fileinfo *file)
   DEB (printk ("sequencer_release(dev=%d)\n", dev));
 
   /*
-   * Wait until the queue is empty (if we don't have nonblock)
+   * Wait until the queue is empty (if we don't have nonblock)
    */
 
   if (mode != OPEN_READ && !(file->flags & (O_NONBLOCK) ?
@@ -1268,6 +1270,23 @@ sequencer_release (int dev, struct fileinfo *file)
     while (!(current->signal & ~current->blocked) && qlen > 0)
       {
        seq_sync ();
+
+       {
+         unsigned long   tlimit;
+
+         if (3 * HZ)
+           current->timeout = tlimit = jiffies + (3 * HZ);
+         else
+           tlimit = (unsigned long) -1;
+         seq_sleep_flag.opts = WK_SLEEP;
+         interruptible_sleep_on (&seq_sleeper);
+         if (!(seq_sleep_flag.opts & WK_WAKEUP))
+           {
+             if (jiffies >= tlimit)
+               seq_sleep_flag.opts |= WK_TIMEOUT;
+           }
+         seq_sleep_flag.opts &= ~WK_SLEEP;
+       };                      /* Extra delay */
       }
 
   if (mode != OPEN_READ)
@@ -1397,6 +1416,7 @@ seq_reset (void)
   unsigned long   flags;
 
   sound_stop_timer ();
+
   seq_time = jiffies;
   prev_input_time = 0;
   prev_event_time = 0;
@@ -1519,7 +1539,7 @@ sequencer_ioctl (int dev, struct fileinfo *file,
 
       if (seq_mode != SEQ_2)
        return -EINVAL;
-      get_user (pending_timer, (int *) arg);
+      pending_timer = *(int *) arg;
 
       if (pending_timer < 0 || pending_timer >= num_sound_timers)
        {
@@ -1527,7 +1547,7 @@ sequencer_ioctl (int dev, struct fileinfo *file,
          return -EINVAL;
        }
 
-      return ioctl_out (arg, pending_timer);
+      return (*(int *) arg = pending_timer);
       break;
 
     case SNDCTL_SEQ_PANIC:
@@ -1553,7 +1573,7 @@ sequencer_ioctl (int dev, struct fileinfo *file,
       break;
 
     case SNDCTL_SEQ_TESTMIDI:
-      get_user (midi_dev, (int *) arg);
+      midi_dev = *(int *) arg;
       if (midi_dev < 0 || midi_dev >= max_mididev)
        return -ENXIO;
 
@@ -1576,21 +1596,21 @@ sequencer_ioctl (int dev, struct fileinfo *file,
     case SNDCTL_SEQ_GETINCOUNT:
       if (mode == OPEN_WRITE)
        return 0;
-      return ioctl_out (arg, iqlen);
+      return (*(int *) arg = iqlen);
       break;
 
     case SNDCTL_SEQ_GETOUTCOUNT:
 
       if (mode == OPEN_READ)
        return 0;
-      return ioctl_out (arg, SEQ_MAX_QUEUE - qlen);
+      return (*(int *) arg = SEQ_MAX_QUEUE - qlen);
       break;
 
     case SNDCTL_SEQ_GETTIME:
       if (seq_mode == SEQ_2)
        return tmr->ioctl (tmr_no, cmd, arg);
 
-      return ioctl_out (arg, jiffies - seq_time);
+      return (*(int *) arg = jiffies - seq_time);
       break;
 
     case SNDCTL_SEQ_CTRLRATE:
@@ -1600,18 +1620,18 @@ sequencer_ioctl (int dev, struct fileinfo *file,
       if (seq_mode == SEQ_2)
        return tmr->ioctl (tmr_no, cmd, arg);
 
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
       if (val != 0)
        return -EINVAL;
 
-      return ioctl_out (arg, HZ);
+      return (*(int *) arg = HZ);
       break;
 
     case SNDCTL_SEQ_RESETSAMPLES:
       {
        int             err;
 
-       get_user (dev, (int *) arg);
+       dev = *(int *) arg;
        if (dev < 0 || dev >= num_synths)
          {
            return -ENXIO;
@@ -1628,18 +1648,18 @@ sequencer_ioctl (int dev, struct fileinfo *file,
       break;
 
     case SNDCTL_SEQ_NRSYNTHS:
-      return ioctl_out (arg, max_synthdev);
+      return (*(int *) arg = max_synthdev);
       break;
 
     case SNDCTL_SEQ_NRMIDIS:
-      return ioctl_out (arg, max_mididev);
+      return (*(int *) arg = max_mididev);
       break;
 
     case SNDCTL_SYNTH_MEMAVL:
       {
        int             dev;
 
-       get_user (dev, (int *) arg);
+       dev = *(int *) arg;
 
        if (dev < 0 || dev >= num_synths)
          return -ENXIO;
@@ -1647,7 +1667,7 @@ sequencer_ioctl (int dev, struct fileinfo *file,
        if (!(synth_open_mask & (1 << dev)) && !orig_dev)
          return -EBUSY;
 
-       return ioctl_out (arg, synth_devs[dev]->ioctl (dev, cmd, arg));
+       return (*(int *) arg = synth_devs[dev]->ioctl (dev, cmd, arg));
       }
       break;
 
@@ -1655,7 +1675,7 @@ sequencer_ioctl (int dev, struct fileinfo *file,
       {
        int             dev;
 
-       get_user (dev, (int *) arg);
+       dev = *(int *) arg;
 
        if (dev < 0 || dev >= num_synths)
          return -ENXIO;
@@ -1673,7 +1693,7 @@ sequencer_ioctl (int dev, struct fileinfo *file,
        struct synth_info inf;
        int             dev;
 
-       copy_from_user ((char *) &inf, &((char *) arg)[0], sizeof (inf));
+       memcpy ((char *) &inf, (&((char *) arg)[0]), sizeof (inf));
        dev = inf.device;
 
        if (dev < 0 || dev >= max_synthdev)
@@ -1686,12 +1706,35 @@ sequencer_ioctl (int dev, struct fileinfo *file,
       }
       break;
 
+
+      /* Like SYNTH_INFO but returns ID in the name field */
+    case SNDCTL_SYNTH_ID:
+      {
+       struct synth_info inf;
+       int             dev;
+
+       memcpy ((char *) &inf, (&((char *) arg)[0]), sizeof (inf));
+       dev = inf.device;
+
+       if (dev < 0 || dev >= max_synthdev)
+         return -ENXIO;
+
+       if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+         return -EBUSY;
+
+       memcpy ((char *) &inf, (char *) synth_devs[dev]->info, sizeof (inf));
+       strcpy (inf.name, synth_devs[dev]->id);
+       memcpy ((&((char *) arg)[0]), (char *) &inf, sizeof (inf));
+       return 0;
+      }
+      break;
+
     case SNDCTL_SEQ_OUTOFBAND:
       {
        struct seq_event_rec event_rec;
        unsigned long   flags;
 
-       copy_from_user ((char *) &event_rec, &((char *) arg)[0], sizeof (event_rec));
+       memcpy ((char *) &event_rec, (&((char *) arg)[0]), sizeof (event_rec));
 
        save_flags (flags);
        cli ();
@@ -1708,18 +1751,14 @@ sequencer_ioctl (int dev, struct fileinfo *file,
        int             dev;
        char           *pp;
 
-       copy_from_user ((char *) &inf, &((char *) arg)[0], sizeof (inf));
+       memcpy ((char *) &inf, (&((char *) arg)[0]), sizeof (inf));
        dev = inf.device;
 
        if (dev < 0 || dev >= max_mididev)
          return -ENXIO;
 
        pp = (char *) &midi_devs[dev]->info;
-       {
-         char           *fixit = pp;
-
-         copy_to_user (&((char *) arg)[0], fixit, sizeof (inf));
-       };
+       memcpy ((&((char *) arg)[0]), pp, sizeof (inf));
        return 0;
       }
       break;
@@ -1728,7 +1767,7 @@ sequencer_ioctl (int dev, struct fileinfo *file,
       {
        int             tmp;
 
-       get_user (tmp, (int *) arg);
+       tmp = *(int *) arg;
 
        if (tmp < 1)
          tmp = 1;
@@ -1743,14 +1782,14 @@ sequencer_ioctl (int dev, struct fileinfo *file,
       {
        int             val;
 
-       get_user (val, (int *) arg);
+       val = *(int *) arg;
 
        if (val < 0)
          val = 0;
 
        val = (HZ * val) / 10;
        pre_event_timeout = val;
-       return ioctl_out (arg, val);
+       return (*(int *) arg = val);
       }
       break;
 
@@ -1769,28 +1808,50 @@ sequencer_ioctl (int dev, struct fileinfo *file,
   return -EINVAL;
 }
 
-unsigned int
-sequencer_poll (kdev_t dev, struct fileinfo *file, poll_table * wait)
+int
+sequencer_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
 {
-  unsigned int   mask = 0;
   unsigned long   flags;
 
-  save_flags (flags);
-  cli ();
+  dev = dev >> 4;
 
-  midi_sleep_flag.opts = WK_SLEEP;
-  poll_wait (&midi_sleeper, wait);
-  seq_sleep_flag.opts = WK_SLEEP;
-  poll_wait (&seq_sleeper, wait);
+  switch (sel_type)
+    {
+    case SEL_IN:
+      save_flags (flags);
+      cli ();
+      if (!iqlen)
+       {
 
-  restore_flags (flags);
+         midi_sleep_flag.opts = WK_SLEEP;
+         poll_wait (&midi_sleeper, wait);
+         restore_flags (flags);
+         return 0;
+       }
+      restore_flags (flags);
+      return 1;
+      break;
 
-  if (iqlen)
-    mask |= POLLIN | POLLRDNORM;
-  if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
-    mask |= POLLOUT | POLLWRNORM;
+    case SEL_OUT:
+      save_flags (flags);
+      cli ();
+      if ((SEQ_MAX_QUEUE - qlen) < output_threshold)
+       {
 
-  return mask;
+         seq_sleep_flag.opts = WK_SLEEP;
+         poll_wait (&seq_sleeper, wait);
+         restore_flags (flags);
+         return 0;
+       }
+      restore_flags (flags);
+      return 1;
+      break;
+
+    case SEL_EX:
+      return 0;
+    }
+
+  return 0;
 }
 
 
@@ -1835,7 +1896,8 @@ note_to_freq (int note_num)
 }
 
 unsigned long
-compute_finetune (unsigned long base_freq, int bend, int range)
+compute_finetune (unsigned long base_freq, int bend, int range,
+                 int vibrato_cents)
 {
   unsigned long   amount;
   int             negative, semitones, cents, multiplier = 1;
@@ -1851,7 +1913,9 @@ compute_finetune (unsigned long base_freq, int bend, int range)
   if (range >= 8192)
     range = 8192;
 
-  bend = bend * range / 8192;
+  bend = bend * range / 8192;  /* Convert to cents */
+  bend += vibrato_cents;
+
   if (!bend)
     return base_freq;
 
@@ -1888,9 +1952,12 @@ compute_finetune (unsigned long base_freq, int bend, int range)
 void
 sequencer_init (void)
 {
+  if (sequencer_ok)
+    return;
 
 
   queue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * EV_SZ));
+  sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * EV_SZ;
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (queue == NULL)
@@ -1901,6 +1968,7 @@ sequencer_init (void)
 
 
   iqueue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * IEV_SZ));
+  sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * IEV_SZ;
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (queue == NULL)
@@ -1909,6 +1977,7 @@ sequencer_init (void)
       return;
     }
 
+
   sequencer_ok = 1;
 }
 
index bc47cc0bfc6bddefaef37fc25a786b2a3e4111bf..f5587cc3f9e6795ffecf1f8cff267bf443d34485 100644 (file)
@@ -5,27 +5,30 @@
 int DMAbuf_open(int dev, int mode);
 int DMAbuf_release(int dev, int mode);
 int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock);
-int DMAbuf_get_curr_buffer(int dev, int *buff_no, char **dma_buf, int *buff_ptr, int *buff_size);
 int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock);
 int DMAbuf_rmchars(int dev, int buff_no, int c);
 int DMAbuf_start_output(int dev, int buff_no, int l);
-int DMAbuf_set_count(int dev, int buff_no, int l);
-int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local);
+int DMAbuf_move_wrpointer(int dev, int l);
+/* int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local); */
 void DMAbuf_init(void);
+void DMAbuf_deinit(int dev);
 int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
 int DMAbuf_open_dma (int dev);
 void DMAbuf_close_dma (int dev);
-void DMAbuf_reset_dma (int dev);
 void DMAbuf_inputintr(int dev);
 void DMAbuf_outputintr(int dev, int underflow_flag);
-unsigned int DMAbuf_poll(kdev_t dev, struct fileinfo *file, poll_table * wait);
-void DMAbuf_start_device(int dev);
+struct dma_buffparms;
+int DMAbuf_space_in_queue (int dev);
+int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap);
+int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap);
+void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap);
+int DMAbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
 void DMAbuf_start_devices(unsigned int devmask);
 void DMAbuf_reset (int dev);
 int DMAbuf_sync (int dev);
 
 /*
- *     System calls for /dev/dsp and /dev/audio
+ *     System calls for /dev/dsp and /dev/audio (audio.c)
  */
 
 int audio_read (int dev, struct fileinfo *file, char *buf, int count);
@@ -36,7 +39,8 @@ int audio_ioctl (int dev, struct fileinfo *file,
           unsigned int cmd, caddr_t arg);
 void audio_init_devices (void);
 
-unsigned int audio_poll(kdev_t dev, struct fileinfo *file, poll_table * wait);
+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);
 
 /*
  *     System calls for the /dev/sequencer
@@ -52,11 +56,12 @@ int sequencer_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
 void sequencer_init (void);
 void sequencer_timer(unsigned long dummy);
 int note_to_freq(int note_num);
-unsigned long compute_finetune(unsigned long base_freq, int bend, int range);
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
+                              int vibrato_bend);
 void seq_input_event(unsigned char *event, int len);
 void seq_copy_to_input (unsigned char *event, int len);
 
-unsigned int sequencer_poll(kdev_t dev, struct fileinfo *file, poll_table * wait);
+int sequencer_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
 
 /*
  *     System calls for the /dev/midi
@@ -72,7 +77,7 @@ int MIDIbuf_lseek (int dev, struct fileinfo *file, off_t offset, int orig);
 void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
 void MIDIbuf_init(void);
 
-unsigned int MIDIbuf_poll(kdev_t dev, struct fileinfo *file, poll_table * wait);
+int MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
 
 /*
  *
@@ -80,11 +85,9 @@ unsigned int MIDIbuf_poll(kdev_t dev, struct fileinfo *file, poll_table * wait);
  */
 
 /*     From soundcard.c        */
-void soundcard_init(void);
 void tenmicrosec(int *osp);
 void request_sound_timer (int count);
 void sound_stop_timer(void);
-int snd_ioctl_return(int *addr, int value);
 int snd_set_irq_handler (int interrupt_level, void(*iproc)(int, void*, struct pt_regs *), char *name, int *osp);
 void snd_release_irq(int vect);
 void sound_dma_malloc(int dev);
@@ -159,7 +162,6 @@ int gus_wave_detect(int baseaddr);
 void gus_wave_init(struct address_info *hw_config);
 void gus_wave_unload (void);
 void gus_voice_irq(void);
-unsigned char gus_read8 (int reg);
 void gus_write8(int reg, unsigned int data);
 void guswave_dma_irq(void);
 void gus_delay(void);
index 27903e67f175afdbd093b1ab48337cc36a14fefd..baa04be613969f7574fbae697ee7ce24d082c0e6 100644 (file)
@@ -3,7 +3,7 @@
  * A driver for Soundcards, misc configuration parameters.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -95,9 +95,9 @@
 
 #define MAX_AUDIO_DEV  5
 #define MAX_MIXER_DEV  5
-#define MAX_SYNTH_DEV  3
+#define MAX_SYNTH_DEV  5
 #define MAX_MIDI_DEV   6
-#define MAX_TIMER_DEV  3
+#define MAX_TIMER_DEV  4
 
 struct fileinfo {
                  int mode;           /* Open mode */
@@ -132,6 +132,7 @@ struct voice_alloc_info {
 struct channel_info {
                int pgm_num;
                int bender_value;
+               int bender_range;
                unsigned char controllers[128];
        };
 
index ce9a19c226088fc40f14dfaa5c975f5cf051f871..12282544026d944b04010f2a4ad4da38234b7210 100644 (file)
@@ -4,7 +4,7 @@
  * The system call switch handler
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -73,15 +73,11 @@ set_mixer_levels (caddr_t arg)
   if ((buf = (mixer_vol_table *) vmalloc (sizeof (mixer_vol_table))) == NULL)
     return -ENOSPC;
 
-  copy_from_user ((char *) buf, &((char *) arg)[0], sizeof (*buf));
+  memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
 
   load_mixer_volumes (buf->name, buf->levels, 0);
 
-  {
-    char           *fixit = (char *) buf;
-
-    copy_to_user (&((char *) arg)[0], fixit, sizeof (*buf));
-  };
+  memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
   vfree (buf);
 
   return err;
@@ -96,7 +92,7 @@ get_mixer_levels (caddr_t arg)
   if ((buf = (mixer_vol_table *) vmalloc (sizeof (mixer_vol_table))) == NULL)
     return -ENOSPC;
 
-  copy_from_user ((char *) buf, &((char *) arg)[0], sizeof (*buf));
+  memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
 
   n = buf->num;
   if (n < 0 || n >= num_mixer_volumes)
@@ -106,11 +102,7 @@ get_mixer_levels (caddr_t arg)
       memcpy ((char *) buf, (char *) &mixer_vols[n], sizeof (*buf));
     }
 
-  {
-    char           *fixit = (char *) buf;
-
-    copy_to_user (&((char *) arg)[0], fixit, sizeof (*buf));
-  };
+  memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
   vfree (buf);
 
   return err;
@@ -358,7 +350,7 @@ init_status (void)
     }
 #endif
 
-#ifdef CONFIG_MIDI
+#ifdef CONFIG_SEQUENCER
   if (!put_status ("\nTimers:\n"))
     return;
 
@@ -612,11 +604,7 @@ get_mixer_info (int dev, caddr_t arg)
   strcpy (info.name, mixer_devs[dev]->name);
   info.modify_counter = mixer_devs[dev]->modify_counter;
 
-  {
-    char           *fixit = (char *) &info;
-
-    copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-  };
+  memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
   return 0;
 }
 
@@ -631,11 +619,7 @@ get_old_mixer_info (int dev, caddr_t arg)
   strcpy (info.id, mixer_devs[dev]->id);
   strcpy (info.name, mixer_devs[dev]->name);
 
-  {
-    char           *fixit = (char *) &info;
-
-    copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-  };
+  memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
   return 0;
 }
 
@@ -648,7 +632,7 @@ sound_mixer_ioctl (int mixdev,
   if (cmd == SOUND_OLD_MIXER_INFO)
     return get_old_mixer_info (mixdev, arg);
 
-  if (_IOC_DIR (cmd) & _IOC_WRITE)
+  if (_SIOC_DIR (cmd) & _SIOC_WRITE)
     mixer_devs[mixdev]->modify_counter++;
 
   return mixer_devs[mixdev]->ioctl (mixdev, cmd, arg);
@@ -661,7 +645,7 @@ sound_ioctl_sw (int dev, struct fileinfo *file,
   DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
 
   if (cmd == OSS_GETVERSION)
-    return ioctl_out (arg, SOUND_VERSION);
+    return (*(int *) arg = SOUND_VERSION);
 
 
   if (((cmd >> 8) & 0xff) == 'M' && num_mixers > 0)    /* Mixer ioctl */
index eea01c3fe80937d9a926ff9f432ec6a3a400f444..e72fef1927d418963f2cfe2d7744c62e920df2e5 100644 (file)
@@ -2,7 +2,7 @@
  * sound/sound_timer.c
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -11,7 +11,6 @@
 #include <linux/config.h>
 
 
-#define SEQUENCER_C
 #include "sound_config.h"
 
 #if defined(CONFIG_SEQUENCER)
@@ -31,7 +30,7 @@ static unsigned long
 tmr2ticks (int tmr_value)
 {
   /*
-     *    Convert timer ticks to MIDI ticks
+   *    Convert timer ticks to MIDI ticks
    */
 
   unsigned long   tmp;
@@ -195,7 +194,7 @@ timer_ioctl (int dev,
   switch (cmd)
     {
     case SNDCTL_TMR_SOURCE:
-      return ioctl_out (arg, TMR_INTERNAL);
+      return (*(int *) arg = TMR_INTERNAL);
       break;
 
     case SNDCTL_TMR_START:
@@ -215,7 +214,7 @@ timer_ioctl (int dev,
       break;
 
     case SNDCTL_TMR_TIMEBASE:
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
 
       if (val)
        {
@@ -226,11 +225,11 @@ timer_ioctl (int dev,
          curr_timebase = val;
        }
 
-      return ioctl_out (arg, curr_timebase);
+      return (*(int *) arg = curr_timebase);
       break;
 
     case SNDCTL_TMR_TEMPO:
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
 
       if (val)
        {
@@ -245,20 +244,20 @@ timer_ioctl (int dev,
          reprogram_timer ();
        }
 
-      return ioctl_out (arg, curr_tempo);
+      return (*(int *) arg = curr_tempo);
       break;
 
     case SNDCTL_SEQ_CTRLRATE:
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
 
       if (val != 0)            /* Can't change */
        return -EINVAL;
 
-      return ioctl_out (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+      return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
       break;
 
     case SNDCTL_SEQ_GETTIME:
-      return ioctl_out (arg, curr_ticks);
+      return (*(int *) arg = curr_ticks);
       break;
 
     case SNDCTL_TMR_METRONOME:
@@ -286,7 +285,7 @@ timer_arm (int dev, long time)
 
 static struct sound_timer_operations sound_timer =
 {
-  {"GUS Timer", 0},
+  {"Sound Timer", 0},
   1,                           /* Priority */
   0,                           /* Local device link */
   timer_open,
@@ -323,10 +322,15 @@ sound_timer_init (struct sound_lowlev_timer *t, char *name)
 {
   int             n;
 
-  if (initialized || t == NULL)
-    return;                    /* There is already a similar timer */
-
+  if (initialized)
+    {
+      if (t->priority <= tmr->priority)
+       return;                 /* There is already a similar or better timer */
+      tmr = t;
+      return;
+    }
   initialized = 1;
+
   tmr = t;
 
   if (num_sound_timers >= MAX_TIMER_DEV)
index 260ab5b367ff1d1e3bfb20025d8fa6f9ea903b7a..a47f38766cb90a30f2d47655dd73e6ad30c3a3c5 100644 (file)
@@ -4,7 +4,7 @@
  * Soundcard driver for Linux
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
 
 
 #include "sound_config.h"
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/ctype.h>
+#ifdef __KERNEL__
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <linux/wait.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#endif /* __KERNEL__ */
+#include <linux/delay.h>
 
 #include <linux/major.h>
 
 
-int            *sound_osp = NULL;
 static int      chrdev_registered = 0;
 static int      sound_major = SOUND_MAJOR;
 
@@ -28,6 +40,7 @@ static int      is_unloading = 0;
  * Table for permanently allocated memory (used when unloading the module)
  */
 caddr_t         sound_mem_blocks[1024];
+int             sound_mem_sizes[1024];
 int             sound_nblocks = 0;
 
 static int      soundcard_configured = 0;
@@ -42,31 +55,6 @@ static char     dma_alloc_map[8] =
 #define DMA_MAP_BUSY           2
 
 
-int
-ioctl_in (caddr_t arg)
-{
-  int             xx;
-
-  get_user (xx, (int *) arg);
-  return xx;
-}
-
-int
-ioctl_out (caddr_t arg, int result)
-{
-  put_user (result, (int *) arg);
-  return 0;
-}
-
-int
-snd_ioctl_return (int *addr, int value)
-{
-  if (value < 0)
-    return value;
-
-  put_user (value, (int *) &((addr)[0]));
-  return 0;
-}
 
 static long
 sound_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
@@ -159,27 +147,37 @@ sound_ioctl (struct inode *inode, struct file *file,
             unsigned int cmd, unsigned long arg)
 {
   int             dev, err;
+  int             len = 0;
+  int             alloced = 0;
+  char           *ptr = (char *) arg;
 
   dev = MINOR (inode->i_rdev);
 
   files[dev].flags = file->f_flags;
 
-  if (_IOC_DIR (cmd) != _IOC_NONE)
+  if (_SIOC_DIR (cmd) != _SIOC_NONE)
     {
       /*
          * Have to validate the address given by the process.
        */
-      int             len;
 
-      len = _IOC_SIZE (cmd);
+      len = _SIOC_SIZE (cmd);
+      if (len < 1 || len > 65536 || arg == 0)
+       return -EFAULT;
 
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      ptr = vmalloc (len);
+      alloced = 1;
+      if (ptr == NULL)
+       return -EFAULT;
+
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
        {
          if ((err = verify_area (VERIFY_READ, (void *) arg, len)) < 0)
            return err;
+         copy_from_user (ptr, (char *) arg, len);
        }
 
-      if (_IOC_DIR (cmd) & _IOC_READ)
+      if (_SIOC_DIR (cmd) & _SIOC_READ)
        {
          if ((err = verify_area (VERIFY_WRITE, (void *) arg, len)) < 0)
            return err;
@@ -187,34 +185,42 @@ sound_ioctl (struct inode *inode, struct file *file,
 
     }
 
-  err = sound_ioctl_sw (dev, &files[dev], cmd, (caddr_t) arg);
+  err = sound_ioctl_sw (dev, &files[dev], cmd, (caddr_t) ptr);
+
+  if (_SIOC_DIR (cmd) & _SIOC_READ)
+    {
+      copy_to_user ((char *) arg, ptr, len);
+    }
+
+  if (ptr != NULL && alloced)
+    vfree (ptr);
 
   return err;
 }
 
-static unsigned int
-sound_poll (struct file *file, poll_table * wait)
+static int
+sound_select (struct inode *inode, struct file *file, int sel_type, poll_table * wait)
 {
   int             dev;
 
-  dev = MINOR (file->f_inode->i_rdev);
+  dev = MINOR (inode->i_rdev);
 
   files[dev].flags = file->f_flags;
 
-  DEB (printk ("sound_poll(dev=%d)\n", dev));
+  DEB (printk ("sound_select(dev=%d, type=0x%x)\n", dev, sel_type));
 
   switch (dev & 0x0f)
     {
 #ifdef CONFIG_SEQUENCER
     case SND_DEV_SEQ:
     case SND_DEV_SEQ2:
-      return sequencer_poll (dev, &files[dev], wait);
+      return sequencer_select (dev, &files[dev], sel_type, wait);
       break;
 #endif
 
 #ifdef CONFIG_MIDI
     case SND_DEV_MIDIN:
-      return MIDIbuf_poll (dev, &files[dev], wait);
+      return MIDIbuf_select (dev, &files[dev], sel_type, wait);
       break;
 #endif
 
@@ -222,7 +228,7 @@ sound_poll (struct file *file, poll_table * wait)
     case SND_DEV_DSP:
     case SND_DEV_DSP16:
     case SND_DEV_AUDIO:
-      return audio_poll (dev, &files[dev], wait);
+      return audio_select (dev, &files[dev], sel_type, wait);
       break;
 #endif
 
@@ -233,6 +239,21 @@ sound_poll (struct file *file, poll_table * wait)
   return 0;
 }
 
+static unsigned int
+sound_poll (struct file *file, poll_table * wait)
+{
+  struct inode   *inode;
+  int             ret = 0;
+
+  inode = file->f_inode;
+
+  if (sound_select (inode, file, SEL_IN, wait))
+    ret |= POLLIN;
+  if (sound_select (inode, file, SEL_OUT, wait))
+    ret |= POLLOUT;
+  return ret;
+}
+
 static int
 sound_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma)
 {
@@ -334,7 +355,11 @@ static struct file_operations sound_fops =
   sound_release
 };
 
+#ifdef MODULE
+static void
+#else
 void
+#endif
 soundcard_init (void)
 {
 #ifndef MODULE
@@ -444,6 +469,11 @@ cleanup_module (void)
 {
   int             i;
 
+  if (MOD_IN_USE)
+    {
+      return;
+    }
+
   if (chrdev_registered)
     unregister_chrdev (sound_major, "sound");
 
@@ -460,9 +490,6 @@ cleanup_module (void)
 #endif
   sound_unload_drivers ();
 
-  for (i = 0; i < sound_nblocks; i++)
-    vfree (sound_mem_blocks[i]);
-
   free_all_irqs ();            /* If something was left allocated by accident */
 
   for (i = 0; i < 8; i++)
@@ -473,16 +500,18 @@ cleanup_module (void)
       }
 
 
+  for (i = 0; i < sound_nblocks; i++)
+    {
+      vfree (sound_mem_blocks[i]);
+    }
+
 }
 #endif
 
 void
 tenmicrosec (int *osp)
 {
-  int             i;
-
-  for (i = 0; i < 16; i++)
-    inb (0x80);
+  udelay (10);
 }
 
 int
@@ -493,7 +522,7 @@ snd_set_irq_handler (int interrupt_level, void (*iproc) (int, void *, struct pt_
 
   save_flags (flags);
   cli ();
-  retcode = request_irq (interrupt_level, iproc, 0 /* SA_INTERRUPT */ , name, NULL);
+  retcode = request_irq (interrupt_level, iproc, 0, name, NULL);
   if (retcode < 0)
     {
       printk ("Sound: IRQ%d already in use\n", interrupt_level);
@@ -544,7 +573,7 @@ sound_open_dma (int chn, char *deviceID)
 
   if (dma_alloc_map[chn] != DMA_MAP_FREE)
     {
-      printk ("sound_open_dma: DMA channel %d busy or not allocated\n", chn);
+      printk ("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]);
       restore_flags (flags);
       return 1;
     }
@@ -557,7 +586,7 @@ sound_open_dma (int chn, char *deviceID)
 void
 sound_free_dma (int chn)
 {
-  if (dma_alloc_map[chn] != DMA_MAP_FREE)
+  if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL)
     {
       /* printk ("sound_free_dma: Bad access to DMA channel %d\n", chn); */
       return;
@@ -628,14 +657,7 @@ sound_stop_timer (void)
 
 #ifdef CONFIG_AUDIO
 
-#ifdef KMALLOC_DMA_BROKEN
-fatal_error__This_version_is_not_compatible_with_this_kernel;
-#endif
-
 static int      dma_buffsize = DSP_BUFFSIZE;
-#ifdef MODULE_PARM
-MODULE_PARM(dma_buffsize, "i");
-#endif
 
 int
 sound_alloc_dmap (int dev, struct dma_buffparms *dmap, int chan)
@@ -753,15 +775,6 @@ sound_free_dmap (int dev, struct dma_buffparms *dmap, int chan)
   dmap->raw_buf = NULL;
 }
 
-int
-sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc * info)
-{
-  printk ("Entered sound_map_buffer()\n");
-  printk ("Exited sound_map_buffer()\n");
-  return -EINVAL;
-}
-#endif
-
 void
 conf_printf (char *name, struct address_info *hw_config)
 {
@@ -803,3 +816,29 @@ conf_printf2 (char *name, int base, int irq, int dma, int dma2)
 
   printk ("\n");
 }
+
+/* Intel version !!!!!!!!! */
+int 
+sound_start_dma (int dev, struct dma_buffparms *dmap, int chan,
+                unsigned long physaddr,
+                int count, int dma_mode, int autoinit)
+{
+  unsigned long   flags;
+
+/* printk("Start DMA %d, %d\n", (int)(physaddr-dmap->raw_buf_phys), count); */
+  if (autoinit)
+    dma_mode |= DMA_AUTOINIT;
+  save_flags (flags);
+  cli ();
+  disable_dma (chan);
+  clear_dma_ff (chan);
+  set_dma_mode (chan, dma_mode);
+  set_dma_addr (chan, physaddr);
+  set_dma_count (chan, count);
+  enable_dma (chan);
+  restore_flags (flags);
+
+  return 0;
+}
+
+#endif
index 7c6870d25c2b5635da94725731555be7050267e2..6bcb5c3b65716e84f361d7758206ee7d3f44ca32 100644 (file)
@@ -1,2 +1,2 @@
-#define SOUND_VERSION_STRING "3.7-beta12-961115"
-#define SOUND_INTERNAL_VERSION 0x030707
+#define SOUND_VERSION_STRING "3.8-beta9-970226"
+#define SOUND_INTERNAL_VERSION 0x030803
index 644e25934fdc568b1d645fdd58007d4a692ab326..ea8d6b80012ea7acf6b4e2447ea26513a7083ea7 100644 (file)
@@ -4,7 +4,7 @@
  * Low level driver for Ensoniq SoundScape
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -284,45 +284,6 @@ get_board_type (struct sscape_info *devc)
   return tmp;
 }
 
-void
-sscapeintr (int irq, void *dev_id, struct pt_regs *dummy)
-{
-  unsigned char   bits, tmp;
-  static int      debug = 0;
-
-  bits = sscape_read (devc, GA_INTSTAT_REG);
-  if ((sscape_sleep_flag.opts & WK_SLEEP))
-    {
-      {
-       sscape_sleep_flag.opts = WK_WAKEUP;
-       wake_up (&sscape_sleeper);
-      };
-    }
-
-  if (bits & 0x02)             /* Host interface interrupt */
-    {
-      printk ("SSCAPE: Host interrupt, data=%02x\n", host_read (devc));
-    }
-
-#if defined(CONFIG_MPU_EMU) && defined(CONFIG_MIDI)
-  if (bits & 0x01)
-    {
-      mpuintr (irq, NULL, NULL);
-      if (debug++ > 10)                /* Temporary debugging hack */
-       {
-         sscape_write (devc, GA_INTENA_REG, 0x00);     /* Disable all interrupts */
-       }
-    }
-#endif
-
-  /*
-     * Acknowledge interrupts (toggle the interrupt bits)
-   */
-
-  tmp = sscape_read (devc, GA_INTENA_REG);
-  sscape_write (devc, GA_INTENA_REG, (~bits & 0x0e) | (tmp & 0xf1));
-
-}
 
 
 static void
@@ -652,7 +613,7 @@ sscape_coproc_ioctl (void *dev_info, unsigned int cmd, caddr_t arg, int local)
        buf = (copr_buffer *) vmalloc (sizeof (copr_buffer));
        if (buf == NULL)
          return -ENOSPC;
-       copy_from_user ((char *) buf, &((char *) arg)[0], sizeof (*buf));
+       memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
        err = download_boot_block (dev_info, buf);
        vfree (buf);
        return err;
index bec7445880e5a57f63d7fb658179a302bdb997e6..ca21cfa60a53e3dbdb2d639aee54d2391d2a7a77 100644 (file)
@@ -5,7 +5,7 @@
  * Uses the (1/HZ sec) timer of kernel.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -14,7 +14,6 @@
 #include <linux/config.h>
 
 
-#define SEQUENCER_C
 #include "sound_config.h"
 
 #ifdef CONFIG_SEQUENCER
@@ -192,7 +191,7 @@ def_tmr_ioctl (int dev,
   switch (cmd)
     {
     case SNDCTL_TMR_SOURCE:
-      return ioctl_out (arg, TMR_INTERNAL);
+      return (*(int *) arg = TMR_INTERNAL);
       break;
 
     case SNDCTL_TMR_START:
@@ -215,7 +214,7 @@ def_tmr_ioctl (int dev,
       {
        int             val;
 
-       get_user (val, (int *) arg);
+       val = *(int *) arg;
 
        if (val)
          {
@@ -226,7 +225,7 @@ def_tmr_ioctl (int dev,
            curr_timebase = val;
          }
 
-       return ioctl_out (arg, curr_timebase);
+       return (*(int *) arg = curr_timebase);
       }
       break;
 
@@ -234,7 +233,7 @@ def_tmr_ioctl (int dev,
       {
        int             val;
 
-       get_user (val, (int *) arg);
+       val = *(int *) arg;
 
        if (val)
          {
@@ -248,7 +247,7 @@ def_tmr_ioctl (int dev,
            curr_tempo = val;
          }
 
-       return ioctl_out (arg, curr_tempo);
+       return (*(int *) arg = curr_tempo);
       }
       break;
 
@@ -256,16 +255,16 @@ def_tmr_ioctl (int dev,
       {
        int             val;
 
-       get_user (val, (int *) arg);
+       val = *(int *) arg;
        if (val != 0)           /* Can't change */
          return -EINVAL;
 
-       return ioctl_out (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+       return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
       }
       break;
 
     case SNDCTL_SEQ_GETTIME:
-      return ioctl_out (arg, curr_ticks);
+      return (*(int *) arg = curr_ticks);
       break;
 
     case SNDCTL_TMR_METRONOME:
index 974c476b839a02dabca88aed6916bc12eefdd284..0dcd88a25a49d9795d0cf8bb082d40434bbe5acb 100644 (file)
@@ -5,7 +5,7 @@
  * (MT-0002-PC Control Chip)
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -201,7 +201,12 @@ probe_trix_wss (struct address_info *hw_config)
   ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp);
 
   if (ret)
-    request_region (0x390, 2, "AudioTrix");
+    {
+#ifdef TRIX_ENABLE_JOYSTICK
+      trix_write (0x15, 0x80);
+#endif
+      request_region (0x390, 2, "AudioTrix");
+    }
 
   return ret;
 }
index 1d6abfb5c4ab646e3e38603e4aca003fa8c8cb19..f2fef6d07a2b7426a5b2a1b3d7cc438812e66c50 100644 (file)
@@ -4,7 +4,7 @@
  * MPU-401 UART driver (formerly uart401_midi.c)
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -263,6 +263,7 @@ attach_uart401 (struct address_info *hw_config)
 
 
   devc = (uart401_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (uart401_devc)));
+  sound_mem_sizes[sound_nblocks] = sizeof (uart401_devc);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (devc == NULL)
@@ -290,7 +291,7 @@ attach_uart401 (struct address_info *hw_config)
     if (snd_set_irq_handler (devc->irq, uart401intr, "uart401", devc->osp) < 0)
       {
        printk ("uart401: Failed to allocate IRQ%d\n", devc->irq);
-       return;
+       devc->share_irq = 1;
       }
 
   irq2devc[devc->irq] = devc;
@@ -311,6 +312,7 @@ attach_uart401 (struct address_info *hw_config)
 
 
   midi_devs[num_midis] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct midi_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -327,6 +329,7 @@ attach_uart401 (struct address_info *hw_config)
 
 
   midi_devs[num_midis]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -341,6 +344,7 @@ attach_uart401 (struct address_info *hw_config)
          sizeof (struct synth_operations));
 
   strcpy (midi_devs[num_midis]->info.name, name);
+  midi_devs[num_midis]->converter->id = "UART401";
   num_midis++;
   devc->opened = 0;
 }
@@ -397,6 +401,7 @@ int
 probe_uart401 (struct address_info *hw_config)
 {
   int             ok = 0;
+  unsigned long   flags;
 
   static uart401_devc hw_info;
   uart401_devc   *devc = &hw_info;
@@ -417,7 +422,10 @@ probe_uart401 (struct address_info *hw_config)
   devc->my_dev = 0;
   devc->share_irq = 0;
 
+  save_flags (flags);
+  cli ();
   ok = reset_uart401 (devc);
+  restore_flags (flags);
 
   if (ok)
     detected_devc = devc;
index 1043cfcb362907ef504be15d26705cd6a9f3be61..0610d1247f56c53592fcea0e85ea6ace4dfbc8f5 100644 (file)
@@ -2,7 +2,7 @@
  * sound/uart6850.c
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
@@ -14,7 +14,6 @@
  *      added 6850 support, used with COVOX SoundMaster II and custom cards.
  */
 
-#include <linux/config.h>
 #include "sound_config.h"
 
 #if defined(CONFIG_UART6850) && defined(CONFIG_MIDI)
diff --git a/drivers/sound/vivo.c b/drivers/sound/vivo.c
deleted file mode 100644 (file)
index d329606..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-
-/*
- * sound/vivo.c
- *
- * Support routines for Ensoniq Soundscape VIVO. The actual driver is
- * in the vivo subdirectory.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1996
- *
- * 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 "sound_config.h"
-
-extern void     otto_init (int options);
-extern void     synth_init (void);
index 829afebec4b31dc1b7524c8b9d05f2aa8649c735..e7270eae94f2961c3f4b554ce4123ddb1f7ac4b2 100644 (file)
@@ -1,3 +1,4 @@
+extern void allow_interrupts(void);
 /*
  *  linux/fs/buffer.c
  *
@@ -625,6 +626,7 @@ static void refill_freelist(int size)
        }
 
 repeat:
+       allow_interrupts();
        /* OK, we cannot grow the buffer cache, now try to get some
           from the lru list */
 
@@ -706,6 +708,7 @@ struct buffer_head * getblk(kdev_t dev, int block, int size)
           now so as to ensure that there are still clean buffers available
           for user processes to use (and dirty) */
 repeat:
+       allow_interrupts();
        bh = get_hash_table(dev, block, size);
        if (bh) {
                if (!buffer_dirty(bh)) {
@@ -717,7 +720,10 @@ repeat:
                return bh;
        }
 
-       while(!free_list[isize]) refill_freelist(size);
+       while(!free_list[isize]) {
+               allow_interrupts();
+               refill_freelist(size);
+       }
        
        if (find_buffer(dev,block,size))
                 goto repeat;
@@ -1510,6 +1516,8 @@ asmlinkage int sync_old_buffers(void)
                ndirty = 0;
                nwritten = 0;
        repeat:
+               allow_interrupts();
+       
                bh = lru_list[nlist];
                if(bh) 
                         for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) {
@@ -1651,6 +1659,8 @@ int bdflush(void * unused)
                         ndirty = 0;
                         refilled = 0;
                 repeat:
+                       allow_interrupts();
+                       
                         bh = lru_list[nlist];
                         if(bh) 
                                  for (i = nr_buffers_type[nlist]; i-- > 0 && ndirty < bdf_prm.b_un.ndirty; 
@@ -1711,7 +1721,6 @@ 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) {
                        current->signal = 0;
                        interruptible_sleep_on(&bdflush_wait);
diff --git a/include/linux/awe_voice.h b/include/linux/awe_voice.h
new file mode 100644 (file)
index 0000000..cdfb0ff
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * sound/awe_voice.h
+ *
+ * Voice information definitions for the low level driver for the 
+ * AWE32/Sound Blaster 32 wave table synth.
+ *   version 0.3.1b; Jan. 21, 1997
+ *
+ * Copyright (C) 1996,1997 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
+ * 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 program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef AWE_VOICE_H
+#define AWE_VOICE_H
+
+#ifndef SAMPLE_TYPE_AWE32
+#define SAMPLE_TYPE_AWE32      0x20
+#endif
+
+#ifndef _PATCHKEY
+#define _PATCHKEY(id) ((id<<8)|0xfd)
+#endif
+
+/*----------------------------------------------------------------
+ * patch information record
+ *----------------------------------------------------------------*/
+
+/* patch interface header: 16 bytes */
+typedef struct awe_patch_info {
+       short key;                      /* use AWE_PATCH here */
+#define AWE_PATCH      _PATCHKEY(0x07)
+
+       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 reserved;                 /* word alignment data */
+
+       /* the actual patch data begins after this */
+#if defined(AWE_COMPAT_030) && AWE_COMPAT_030
+       char data[0];
+#endif
+} awe_patch_info;
+
+#define AWE_PATCH_INFO_SIZE    16
+
+
+/*----------------------------------------------------------------
+ * raw voice information record
+ *----------------------------------------------------------------*/
+
+/* wave table envelope & effect parameters to control EMU8000 */
+typedef struct _awe_voice_parm {
+       unsigned short moddelay;        /* modulation delay (0x8000) */
+       unsigned short modatkhld;       /* modulation attack & hold time (0x7f7f) */
+       unsigned short moddcysus;       /* modulation decay & sustain (0x7f7f) */
+       unsigned short modrelease;      /* modulation release time (0x807f) */
+       short modkeyhold, modkeydecay;  /* envelope change per key (not used) */
+       unsigned short voldelay;        /* volume delay (0x8000) */
+       unsigned short volatkhld;       /* volume attack & hold time (0x7f7f) */
+       unsigned short voldcysus;       /* volume decay & sustain (0x7f7f) */
+       unsigned short volrelease;      /* volume release time (0x807f) */
+       short volkeyhold, volkeydecay;  /* envelope change per key (not used) */
+       unsigned short lfo1delay;       /* LFO1 delay (0x8000) */
+       unsigned short lfo2delay;       /* LFO2 delay (0x8000) */
+       unsigned short pefe;            /* modulation pitch & cutoff (0x0000) */
+       unsigned short fmmod;           /* LFO1 pitch & cutoff (0x0000) */
+       unsigned short tremfrq;         /* LFO1 volume & freq (0x0000) */
+       unsigned short fm2frq2;         /* LFO2 pitch & freq (0x0000) */
+       unsigned char cutoff;           /* initial cutoff (0xff) */
+       unsigned char filterQ;          /* initial filter Q [0-15] (0x0) */
+       unsigned char chorus;           /* chorus send (0x00) */
+       unsigned char reverb;           /* reverb send (0x00) */
+       unsigned short reserved[4];     /* not used */
+} awe_voice_parm;
+
+#define AWE_VOICE_PARM_SIZE    48
+
+
+/* wave table parameters: 92 bytes */
+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 */
+       short rate_offset;              /* sample rate pitch offset */
+       unsigned short mode;            /* sample mode */
+#define AWE_MODE_ROMSOUND              0x8000
+#define AWE_MODE_STEREO                        1
+#define AWE_MODE_LOOPING               2
+#define AWE_MODE_NORELEASE             4       /* obsolete */
+#define AWE_MODE_INIT_PARM             8
+
+       short root;                     /* midi root key */
+       short tune;                     /* pitch tuning (in cents) */
+       char low, high;                 /* key note range */
+       char vellow, velhigh;           /* velocity range */
+       char fixkey, fixvel;            /* fixed key, velocity */
+       char pan, fixpan;               /* panning, fixed panning */
+       short exclusiveClass;           /* exclusive class (0 = none) */
+       unsigned char amplitude;        /* sample volume (127 max) */
+       unsigned char attenuation;      /* attenuation (0.375dB) */
+       short scaleTuning;              /* pitch scale tuning(%), normally 100 */
+       awe_voice_parm parm;            /* voice envelope parameters */
+       short index;                    /* internal index (set by driver) */
+} awe_voice_info;
+
+#define AWE_VOICE_INFO_SIZE    92
+
+/*----------------------------------------------------------------*/
+
+/* The info entry of awe_voice_rec is changed from 0 to 1
+ * for some compilers refusing zero size array.
+ * Due to this change, sizeof(awe_voice_rec) becomes different
+ * from older versions.
+ * Use AWE_VOICE_REC_SIZE instead.
+ */
+#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
+ *----------------------------------------------------------------*/
+
+/* wave table sample header: 32 bytes */
+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) */
+       short checksum_flag;            /* use check sum = 1 */
+       unsigned short mode_flags;      /* mode flags */
+#define AWE_SAMPLE_8BITS       1       /* wave data is 8bits */
+#define AWE_SAMPLE_UNSIGNED    2       /* wave data is unsigned */
+#define AWE_SAMPLE_NO_BLANK    4       /* no blank loop is attached */
+#define AWE_SAMPLE_SINGLESHOT  8       /* single-shot w/o loop */
+#define AWE_SAMPLE_BIDIR_LOOP  16      /* bidirectional looping */
+#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 */
+#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
+
+
+/*----------------------------------------------------------------
+ * awe hardware controls
+ *----------------------------------------------------------------*/
+
+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;
+
+#define _AWE_DEBUG_MODE                        0x00
+#define _AWE_REVERB_MODE               0x01
+#define _AWE_CHORUS_MODE               0x02
+#define _AWE_REMOVE_LAST_SAMPLES       0x03
+#define _AWE_INITIALIZE_CHIP           0x04
+#define _AWE_SEND_EFFECT               0x05
+#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_NOTEOFF_ALL               0x0e
+#define _AWE_GET_CURRENT_MODE          0x10    /* v0.3 features */
+
+#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;\
+ _SEQ_ADVBUF(8);}
+
+#define AWE_DEBUG_MODE(dev,p1) _AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0)
+#define AWE_REVERB_MODE(dev,p1)        _AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0)
+#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)
+#define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value)
+#define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0)
+#define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0)
+#define AWE_NOTEOFF_ALL(dev) _AWE_CMD(dev, 0, _AWE_NOTEOFF_ALL, 0, 0)
+#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)
+
+/* 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);}
+
+/* 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)
+
+/* reverb mode */
+#define        AWE_REVERB_ROOM1        0
+#define AWE_REVERB_ROOM2       1
+#define        AWE_REVERB_ROOM3        2
+#define        AWE_REVERB_HALL1        3
+#define        AWE_REVERB_HALL2        4
+#define        AWE_REVERB_PLATE        5
+#define        AWE_REVERB_DELAY        6
+#define        AWE_REVERB_PANNINGDELAY 7
+
+/* chorus mode */
+#define AWE_CHORUS_1           0
+#define        AWE_CHORUS_2            1
+#define        AWE_CHORUS_3            2
+#define        AWE_CHORUS_4            3
+#define        AWE_CHORUS_FEEDBACK     4
+#define        AWE_CHORUS_FLANGER      5
+#define        AWE_CHORUS_SHORTDELAY   6
+#define        AWE_CHORUS_SHORTDELAY2  7
+
+/* effects */
+enum {
+
+/* modulation envelope parameters */
+/* 0*/ AWE_FX_ENV1_DELAY,      /* WORD: ENVVAL */
+/* 1*/ AWE_FX_ENV1_ATTACK,     /* BYTE: up ATKHLD */
+/* 2*/ AWE_FX_ENV1_HOLD,       /* BYTE: lw ATKHLD */
+/* 3*/ AWE_FX_ENV1_DECAY,      /* BYTE: lw DCYSUS */
+/* 4*/ AWE_FX_ENV1_RELEASE,    /* BYTE: lw DCYSUS */
+/* 5*/ AWE_FX_ENV1_SUSTAIN,    /* BYTE: up DCYSUS */
+/* 6*/ AWE_FX_ENV1_PITCH,      /* BYTE: up PEFE */
+/* 7*/ AWE_FX_ENV1_CUTOFF,     /* BYTE: lw PEFE */
+
+/* volume envelope parameters */
+/* 8*/ AWE_FX_ENV2_DELAY,      /* WORD: ENVVOL */
+/* 9*/ AWE_FX_ENV2_ATTACK,     /* BYTE: up ATKHLDV */
+/*10*/ AWE_FX_ENV2_HOLD,       /* BYTE: lw ATKHLDV */
+/*11*/ AWE_FX_ENV2_DECAY,      /* BYTE: lw DCYSUSV */
+/*12*/ AWE_FX_ENV2_RELEASE,    /* BYTE: lw DCYSUSV */
+/*13*/ AWE_FX_ENV2_SUSTAIN,    /* BYTE: up DCYSUSV */
+       
+/* LFO1 (tremolo & vibrato) parameters */
+/*14*/ AWE_FX_LFO1_DELAY,      /* WORD: LFO1VAL */
+/*15*/ AWE_FX_LFO1_FREQ,       /* BYTE: lo TREMFRQ */
+/*16*/ AWE_FX_LFO1_VOLUME,     /* BYTE: up TREMFRQ */
+/*17*/ AWE_FX_LFO1_PITCH,      /* BYTE: up FMMOD */
+/*18*/ AWE_FX_LFO1_CUTOFF,     /* BYTE: lo FMMOD */
+
+/* LFO2 (vibrato) parameters */
+/*19*/ AWE_FX_LFO2_DELAY,      /* WORD: LFO2VAL */
+/*20*/ AWE_FX_LFO2_FREQ,       /* BYTE: lo FM2FRQ2 */
+/*21*/ AWE_FX_LFO2_PITCH,      /* BYTE: up FM2FRQ2 */
+
+/* Other overall effect parameters */
+/*22*/ AWE_FX_INIT_PITCH,      /* SHORT: pitch offset */
+/*23*/ AWE_FX_CHORUS,          /* BYTE: chorus effects send (0-255) */
+/*24*/ AWE_FX_REVERB,          /* BYTE: reverb effects send (0-255) */
+/*25*/ AWE_FX_CUTOFF,          /* BYTE: up IFATN */
+/*26*/ AWE_FX_FILTERQ,         /* BYTE: up CCCA */
+
+/* Sample / loop offset changes */
+/*27*/ AWE_FX_SAMPLE_START,    /* SHORT: offset */
+/*28*/ AWE_FX_LOOP_START,      /* SHORT: offset */
+/*29*/ AWE_FX_LOOP_END,        /* SHORT: offset */
+/*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 */
+
+       AWE_FX_END,
+};
+
+
+#endif /* AWE_VOICE_H */
index 3b6af75fa41229b4bd80fd3feac0b8ef20629698..bb338c462469068f8a9e93d782dbed46bf19b32d 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef SOUNDCARD_H
 #define SOUNDCARD_H
 /*
- * Copyright by Hannu Savolainen 1993-1996
+ * Copyright by Hannu Savolainen 1993-1997
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -33,7 +33,7 @@
  * Use ioctl(fd, OSS_GETVERSION, &int) to get the version number of
  * the currently active driver.
  */
-#define SOUND_VERSION  0x030700
+#define SOUND_VERSION  0x0307f1
 #define OPEN_SOUND_SYSTEM
 
 /* In Linux we need to be prepared for cross compiling */
  */
 
 #ifndef _SIOWR
-#if defined(_IOWR) && !defined(sun)
+#if defined(_IOWR) && !defined(sun) && !defined(sparc)
 /* Use already defined ioctl defines if they exist (except with Sun) */
 #define        SIOCPARM_MASK   IOCPARM_MASK
 #define        SIOC_VOID       IOC_VOID
 #define        SIOC_OUT        IOC_OUT
 #define        SIOC_IN         IOC_IN
 #define        SIOC_INOUT      IOC_INOUT
+#define _SIOC_SIZE     _IOC_SIZE
+#define _SIOC_DIR      _IOC_DIR
+#define _SIOC_NONE     _IOC_NONE
+#define _SIOC_READ     _IOC_READ
+#define _SIOC_WRITE    _IOC_WRITE
 #define        _SIO            _IO
 #define        _SIOR           _IOR
 #define        _SIOW           _IOW
@@ -93,7 +98,7 @@
  * and the size of any in or out parameters in the upper
  * word.  The high 2 bits of the upper word are used
  * to encode the in/out status of the parameter; for now
- * we restrict parameters to at most 128 bytes.
+ * we restrict parameters to at most 8191 bytes.
  */
 /* #define     SIOCTYPE                (0xff<<8) */
 #define        SIOCPARM_MASK   0x1fff          /* parameters must be < 8192 bytes */
 #define        _SIOW(x,y,t)    ((int)(SIOC_IN|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y))
 /* this should be _SIORW, but stdio got there first */
 #define        _SIOWR(x,y,t)   ((int)(SIOC_INOUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y))
+#define _SIOC_SIZE(x)  ((x>>16)&SIOCPARM_MASK) 
+#define _SIOC_DIR(x)   (x & 0xf0000000)
+#define _SIOC_NONE     SIOC_VOID
+#define _SIOC_READ     SIOC_OUT
+#define _SIOC_WRITE    SIOC_IN
 #  endif /* _IOWR */
 #endif  /* !_SIOWR */
 
 #define SNDCTL_SEQ_PANIC               _SIO  ('Q',17)
 #define SNDCTL_SEQ_OUTOFBAND           _SIOW ('Q',18, struct seq_event_rec)
 #define SNDCTL_SEQ_GETTIME             _SIOR ('Q',19, int)
+#define SNDCTL_SYNTH_ID                        _SIOWR('Q',20, struct synth_info)
 
        struct seq_event_rec {
                        unsigned char arr[8];
  *     Sample loading mechanism for internal synthesizers (/dev/sequencer)
  *     The following patch_info structure has been designed to support
  *     Gravis UltraSound. It tries to be universal format for uploading
- *     sample based patches but is propably too limited.
+ *     sample based patches but is probably too limited.
  */
 
 struct patch_info {
-               unsigned short key;             /* Use GUS_PATCH here */
-#define GUS_PATCH      _PATCHKEY(0x04)
-#define OBSOLETE_GUS_PATCH     _PATCHKEY(0x02)
+               unsigned short key;             /* Use WAVE_PATCH here */
+#define WAVE_PATCH     _PATCHKEY(0x04)
+#define GUS_PATCH      WAVE_PATCH
 
                short device_no;        /* Synthesizer number */
                short instr_no;         /* Midi pgm# */
@@ -189,6 +200,7 @@ struct patch_info {
 #define WAVE_LOOP_BACK 0x10    /* bit 4 = Set is looping backward. */
 #define WAVE_SUSTAIN_ON        0x20    /* bit 5 = Turn sustaining on. (Env. pts. 3)*/
 #define WAVE_ENVELOPES 0x40    /* bit 6 = Enable envelopes - 1 */
+#define WAVE_FAST_RELEASE 0x80 /* bit 7 = Shut off immediately after note off */
                                /*      (use the env_rate/env_offs fields). */
 /* Linux specific bits */
 #define WAVE_VIBRATO   0x00010000      /* The vibrato info is valid */
@@ -257,7 +269,7 @@ struct patch_info {
        };
 
 struct sysex_info {
-               short key;              /* Use GUS_PATCH here */
+               short key;              /* Use SYSEX_PATCH or MAUI_PATCH here */
 #define SYSEX_PATCH    _PATCHKEY(0x05)
 #define MAUI_PATCH     _PATCHKEY(0x06)
                short device_no;        /* Synthesizer number */
@@ -413,8 +425,8 @@ struct sysex_info {
  *     Set the key field of the structure to FM_PATCH. The device field is used to
  *     route the patch to the corresponding device.
  *
- *     For Gravis UltraSound use struct patch_info. Initialize the key field
- *      to GUS_PATCH.
+ *     For wave table use struct patch_info. Initialize the key field
+ *      to WAVE_PATCH.
  */
 #define SEQ_PRIVATE            0xfe    /* Low level HW dependent events (8 bytes) */
 #define SEQ_EXTENDED           0xff    /* Extended events (8 bytes) OBSOLETE */
@@ -447,7 +459,8 @@ struct synth_info { /* Read only */
 #define FM_TYPE_OPL3                   0x01
 #define MIDI_TYPE_MPU401               0x401
 
-#define SAMPLE_TYPE_GUS                        0x10
+#define SAMPLE_TYPE_BASIC              0x10
+#define SAMPLE_TYPE_GUS                        SAMPLE_TYPE_BASIC
 
                int     perc_mode;      /* No longer supported */
                int     nr_voices;
@@ -572,6 +585,22 @@ typedef struct buffmem_desc {
 #define SNDCTL_DSP_SETSYNCRO           _SIO  ('P', 21)
 #define SNDCTL_DSP_SETDUPLEX           _SIO  ('P', 22)
 
+/*
+ * Application's profile defines the way how playback underrun situations should be handled.
+ * 
+ *     APF_NORMAL (the default) and APF_NETWORK make the driver to cleanup the
+ *     playback buffer whenever an underrun occurs. This consumes some time
+ *     preven's looping the existing buffer.
+ *     APF_CPUINTENS is intended to be set by CPU intensive applications which
+ *     are likely to run out of time occasionally. In this mode the buffer cleanup is
+ *     disabled which saves CPU time but also let's the previous buffer content to
+ *     be played during the "pause" after the underrun.
+ */
+#define SNDCTL_DSP_PROFILE             _SIOW ('P', 23, int)
+#define          APF_NORMAL    0       /* Normal applications */
+#define          APF_NETWORK   1       /* Underruns probably caused by an "external" delay */
+#define   APF_CPUINTENS 2      /* Underruns probably caused by "overheating" the CPU */
+
 #define SOUND_PCM_READ_RATE            _SIOR ('P', 2, int)
 #define SOUND_PCM_READ_CHANNELS                _SIOR ('P', 6, int)
 #define SOUND_PCM_READ_BITS            _SIOR ('P', 5, int)
@@ -928,10 +957,45 @@ typedef struct mixer_vol_table {
  *
  *     These macros define the API which should be used when possible.
  */
+#define SEQ_DECLAREBUF()               SEQ_USE_EXTBUF()
 
-#ifndef USE_SIMPLE_MACROS
 void seqbuf_dump(void);        /* This function must be provided by programs */
 
+extern int OSS_init(int seqfd, int buflen);
+extern void OSS_seqbuf_dump(int fd, unsigned char *buf, int buflen);
+extern void OSS_seq_advbuf(int len, int fd, unsigned char *buf, int buflen);
+extern void OSS_seq_needbuf(int len, int fd, unsigned char *buf, int buflen);
+extern void OSS_patch_caching(int dev, int chn, int patch,
+                             int fd, unsigned char *buf, int buflen);
+extern void OSS_drum_caching(int dev, int chn, int patch,
+                             int fd, unsigned char *buf, int buflen);
+extern void OSS_write_patch(int fd, unsigned char *buf, int len);
+extern int OSS_write_patch2(int fd, unsigned char *buf, int len);
+
+#define SEQ_PM_DEFINES int __foo_bar___
+#ifdef OSSLIB
+#  define SEQ_USE_EXTBUF() \
+               extern unsigned char *_seqbuf; \
+               extern int _seqbuflen;extern int _seqbufptr
+#  define SEQ_DEFINEBUF(len) SEQ_USE_EXTBUF();static int _requested_seqbuflen=len
+#  define _SEQ_ADVBUF(len) OSS_seq_advbuf(len, seqfd, _seqbuf, _seqbuflen)
+#  define _SEQ_NEEDBUF(len) OSS_seq_needbuf(len, seqfd, _seqbuf, _seqbuflen)
+#  define SEQ_DUMPBUF() OSS_seqbuf_dump(seqfd, _seqbuf, _seqbuflen)
+
+#  define SEQ_LOAD_GMINSTR(dev, instr) \
+               OSS_patch_caching(dev, -1, instr, seqfd, _seqbuf, _seqbuflen)
+#  define SEQ_LOAD_GMDRUM(dev, drum) \
+               OSS_drum_caching(dev, -1, drum, seqfd, _seqbuf, _seqbuflen)
+#else /* !OSSLIB */
+
+#  define SEQ_LOAD_GMINSTR(dev, instr)
+#  define SEQ_LOAD_GMDRUM(dev, drum)
+
+#  define SEQ_USE_EXTBUF() \
+               extern unsigned char _seqbuf[]; \
+               extern int _seqbuflen;extern int _seqbufptr
+
+#ifndef USE_SIMPLE_MACROS
 /* Sample seqbuf_dump() implementation:
  *
  *     SEQ_DEFINEBUF (2048);   -- Defines a buffer for 2048 bytes
@@ -952,8 +1016,6 @@ void seqbuf_dump(void);    /* This function must be provided by programs */
  */
 
 #define SEQ_DEFINEBUF(len)             unsigned char _seqbuf[len]; int _seqbuflen = len;int _seqbufptr = 0
-#define SEQ_USE_EXTBUF()               extern unsigned char _seqbuf[]; extern int _seqbuflen;extern int _seqbufptr
-#define SEQ_DECLAREBUF()               SEQ_USE_EXTBUF()
 #define _SEQ_NEEDBUF(len)              if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump()
 #define _SEQ_ADVBUF(len)               _seqbufptr += len
 #define SEQ_DUMPBUF                    seqbuf_dump
@@ -974,6 +1036,7 @@ void seqbuf_dump(void);    /* This function must be provided by programs */
  */
 #define _SEQ_NEEDBUF(len)      /* empty */
 #endif
+#endif /* !OSSLIB */
 
 #define SEQ_VOLUME_MODE(dev, mode)     {_SEQ_NEEDBUF(8);\
                                        _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
@@ -1041,18 +1104,30 @@ void seqbuf_dump(void); /* This function must be provided by programs */
  * cause fatal problems with some other devices (such as MPU401).
  */
 #define SEQ_SYSEX(dev, buf, len) \
-                                       {int i, l=(len); if (l>6)l=6;\
+                                       {int ii, ll=(len); \
+                                        unsigned char *bufp=buf;\
+                                        if (ll>6)ll=6;\
                                        _SEQ_NEEDBUF(8);\
                                        _seqbuf[_seqbufptr] = EV_SYSEX;\
-                                       for(i=0;i<l;i++)_seqbuf[_seqbufptr+i+1] = (buf)[i];\
-                                       for(i=l;i<6;i++)_seqbuf[_seqbufptr+i+1] = 0xff;\
+                                       _seqbuf[_seqbufptr+1] = (dev);\
+                                       for(ii=0;ii<ll;ii++)\
+                                          _seqbuf[_seqbufptr+ii+2] = bufp[ii];\
+                                       for(ii=ll;ii<6;ii++)\
+                                          _seqbuf[_seqbufptr+ii+2] = 0xff;\
                                        _SEQ_ADVBUF(8);}
 
 #define SEQ_CHN_PRESSURE(dev, chn, pressure) \
                _CHN_COMMON(dev, MIDI_CHN_PRESSURE, chn, pressure, 0, 0)
 
-#define SEQ_SET_PATCH(dev, chn, patch) \
+#define SEQ_SET_PATCH SEQ_PGM_CHANGE
+#ifdef OSSLIB
+#   define SEQ_PGM_CHANGE(dev, chn, patch) \
+               {OSS_patch_caching(dev, chn, patch, seqfd, _seqbuf, _seqbuflen); \
+                _CHN_COMMON(dev, MIDI_PGM_CHANGE, chn, patch, 0, 0);}
+#else
+#   define SEQ_PGM_CHANGE(dev, chn, patch) \
                _CHN_COMMON(dev, MIDI_PGM_CHANGE, chn, patch, 0, 0)
+#endif
 
 #define SEQ_CONTROL(dev, chn, controller, value) \
                _CHN_COMMON(dev, MIDI_CTL_CHANGE, chn, controller, 0, value)
@@ -1130,10 +1205,19 @@ void seqbuf_dump(void); /* This function must be provided by programs */
 /*
  * Patch loading.
  */
-#define SEQ_WRPATCH(patchx, len)               {if (_seqbufptr) seqbuf_dump();\
-                                       if (write(seqfd, (char*)(patchx), len)==-1) \
-                                          perror("Write patch: /dev/sequencer");}
-#define SEQ_WRPATCH2(patchx, len)      (seqbuf_dump(), write(seqfd, (char*)(patchx), len))
+#ifdef OSSLIB
+#   define SEQ_WRPATCH(patchx, len) \
+               OSS_write_patch(seqfd, (char*)(patchx), len)
+#   define SEQ_WRPATCH2(patchx, len) \
+               OSS_write_patch2(seqfd, (char*)(patchx), len)
+#else
+#   define SEQ_WRPATCH(patchx, len) \
+               {if (_seqbufptr) SEQ_DUMPBUF();\
+                if (write(seqfd, (char*)(patchx), len)==-1) \
+                   perror("Write patch: /dev/sequencer");}
+#   define SEQ_WRPATCH2(patchx, len) \
+               (SEQ_DUMPBUF(), write(seqfd, (char*)(patchx), len))
+#endif
 
 #endif
 #endif
index 9021ae9192b7d16e149d189040652591cec4e7ed..6b7703e75cecfbed566ab21c7a423c506ca8672e 100644 (file)
@@ -6,7 +6,7 @@
  *                     and not portable.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * 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
index 7cdef71299b7fe3e3e306092a937f9215dff53a9..728b6662e750c4d47fecb21fa2cd06a7fe65e023 100644 (file)
@@ -3,9 +3,6 @@
 
 extern struct proto_ops                inet_stream_ops;
 extern struct proto_ops                inet_dgram_ops;
-extern struct sock *           tcp_sock_array[SOCK_ARRAY_SIZE];
-extern struct sock *           udp_sock_array[SOCK_ARRAY_SIZE];
-
 
 /*
  *     INET4 prototypes used by INET6
index 8721a84ea49530b0cefde34bcd4e695d944f9f93..064b5bf7a658a78bc43a9c2a7058e6e9d900e91a 100644 (file)
 extern struct proto raw_prot;
 
 
-extern int     raw_err(struct sock *, struct sk_buff *);
+extern void    raw_err(struct sock *, struct sk_buff *);
 extern int     raw_rcv(struct sock *, struct sk_buff *);
 
+/* Note: v4 ICMP wants to get at this stuff, if you change the
+ *       hashing mechanism, make sure you update icmp.c as well.
+ */
+#define RAWV4_HTABLE_SIZE      MAX_INET_PROTOS
+extern struct sock *raw_v4_htable[RAWV4_HTABLE_SIZE];
+
+
+extern struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
+                                 unsigned long raddr, unsigned long laddr);
+
 #endif /* _RAW_H */
index 30ecc79627ee8aea9dc773f1ec1e777623acbc9a..36373719fbedf0f18f3b362b523c27ae3d0bf63d 100644 (file)
@@ -2,6 +2,14 @@
 #define _NET_RAWV6_H
 
 #ifdef __KERNEL__
+
+#define RAWV6_HTABLE_SIZE      MAX_INET_PROTOS
+extern struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
+
+
+extern struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
+                                 struct in6_addr *loc_addr, struct in6_addr *rmt_addr);
+
 extern int                     rawv6_rcv(struct sk_buff *skb, 
                                          struct device *dev,
                                          struct in6_addr *saddr, 
index 225f970bee2498789ae5a3dc63b8254d3e84cc37..f262f00d16b4cb37a6cdf6442093773aa718ca58 100644 (file)
@@ -22,6 +22,7 @@
  *             Alan Cox        :       New fields for options
  *     Pauline Middelink       :       identd support
  *             Alan Cox        :       Eliminate low level recv/recvfrom
+ *             David S. Miller :       New socket lookup architecture.
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
 
 #include <asm/atomic.h>
 
-/* Think big (also on some systems a byte is faster) */
-#define SOCK_ARRAY_SIZE        256
-
-
 /*
  *     The AF_UNIX specific socket options
  */
@@ -307,6 +304,10 @@ struct tcp_opt
 
 struct sock 
 {
+       /* This must be first. */
+       struct sock             *sklist_next;
+       struct sock             *sklist_prev;
+
        atomic_t                wmem_alloc;
        atomic_t                rmem_alloc;
        unsigned long           allocation;             /* Allocation mode */
@@ -341,8 +342,11 @@ struct sock
                                bsdism;
        unsigned long           lingertime;
        int                     proc;
+
+       struct sock             **hashtable;
+       int                     hashent;
        struct sock             *next;
-       struct sock             *prev; /* Doubly linked chain.. */
+       struct sock             *prev;
        struct sock             *pair;
 
        struct sk_buff          * send_head;
@@ -528,6 +532,10 @@ struct inet_options {
  
 struct proto 
 {
+       /* These must be first. */
+       struct sock             *sklist_next;
+       struct sock             *sklist_prev;
+
        void                    (*close)(struct sock *sk, 
                                        unsigned long timeout);
        int                     (*connect)(struct sock *sk,
@@ -562,11 +570,17 @@ struct proto
        int                     (*backlog_rcv) (struct sock *sk, 
                                                struct sk_buff *skb);
 
+       /* Keeping track of sk's, looking them up, and port selection methods. */
+       void                    (*hash)(struct sock *sk);
+       void                    (*unhash)(struct sock *sk);
+       void                    (*rehash)(struct sock *sk);
+       unsigned short          (*good_socknum)(void);
+       int                     (*verify_bind)(struct sock *sk, unsigned short snum);
+
        unsigned short          max_header;
        unsigned long           retransmits;
        char                    name[32];
        int                     inuse, highestinuse;
-       struct sock **          sock_array;
 };
 
 #define TIME_WRITE     1       /* Not yet used */
@@ -594,6 +608,46 @@ struct proto
 #define RCV_SHUTDOWN   1
 #define SEND_SHUTDOWN  2
 
+/* Per-protocol hash table implementations use this to make sure
+ * nothing changes.
+ */
+#define SOCKHASH_LOCK()                start_bh_atomic()
+#define SOCKHASH_UNLOCK()      end_bh_atomic()
+
+/* Some things in the kernel just want to get at a protocols
+ * entire socket list commensurate, thus...
+ */
+static __inline__ void add_to_prot_sklist(struct sock *sk)
+{
+       SOCKHASH_LOCK();
+       if(!sk->sklist_next) {
+               struct proto *p = sk->prot;
+
+               sk->sklist_prev = (struct sock *) p;
+               sk->sklist_next = p->sklist_next;
+               p->sklist_next->sklist_prev = sk;
+               p->sklist_next = sk;
+
+               /* Charge the protocol. */
+               sk->prot->inuse += 1;
+               if(sk->prot->highestinuse < sk->prot->inuse)
+                       sk->prot->highestinuse = sk->prot->inuse;
+       }
+       SOCKHASH_UNLOCK();
+}
+
+static __inline__ void del_from_prot_sklist(struct sock *sk)
+{
+       SOCKHASH_LOCK();
+       if(sk->sklist_next) {
+               sk->sklist_next->sklist_prev = sk->sklist_prev;
+               sk->sklist_prev->sklist_next = sk->sklist_next;
+               sk->sklist_next = NULL;
+               sk->prot->inuse--;
+       }
+       SOCKHASH_UNLOCK();
+}
+
 /*
  * Used by processes to "lock" a socket state, so that
  * interrupts and bottom half handlers won't change it
@@ -660,21 +714,6 @@ static __inline__ int max(unsigned int a, unsigned int b)
 extern struct sock *           sk_alloc(int priority);
 extern void                    sk_free(struct sock *sk);
 extern void                    destroy_sock(struct sock *sk);
-extern unsigned short          get_new_socknum(struct proto *,
-                                               unsigned short);
-extern void                    inet_put_sock(unsigned short, struct sock *); 
-extern struct sock             *get_sock(struct proto *, unsigned short,
-                                         unsigned long, unsigned short,
-                                         unsigned long);
-extern struct sock             *get_sock_proxy(struct proto *, unsigned short,
-                                         unsigned long, unsigned short,
-                                         unsigned long,
-                                         unsigned long, unsigned short);
-extern struct sock             *get_sock_mcast(struct sock *, unsigned short,
-                                         unsigned long, unsigned short,
-                                         unsigned long);
-extern struct sock             *get_sock_raw(struct sock *, unsigned short,
-                                         unsigned long, unsigned long);
 
 extern struct sk_buff          *sock_wmalloc(struct sock *sk,
                                              unsigned long size, int force,
index a01f1c3d5089e06c35eb238b1a6d86ef8dcac12b..b112394130a45344465ab7ab7dcbd2f017577dc5 100644 (file)
 #include <linux/tcp.h>
 #include <net/checksum.h>
 
+/* This is for all connections with a full identity, no wildcards. */
+#define TCP_HTABLE_SIZE                128
+
+/* This is for listening sockets, thus all sockets which possess wildcards. */
+#define TCP_LHTABLE_SIZE       16      /* Yes, really, this is all you need. */
+
+/* This is for all sockets, to keep track of the local port allocations. */
+#define TCP_BHTABLE_SIZE       64
+
+/* tcp_ipv4.c: These need to be shared by v4 and v6 because the lookup
+ *             and hashing code needs to work with different AF's yet
+ *             the port space is shared.
+ */
+extern struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
+extern struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
+extern struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE];
+
+/* These are AF independant. */
+static __inline__ int tcp_bhashfn(__u16 lport)
+{
+       return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE - 1);
+}
+
+static __inline__ int tcp_sk_bhashfn(struct sock *sk)
+{
+       __u16 lport = sk->num;
+       return tcp_bhashfn(lport);
+}
+
+/* These can have wildcards, don't try too hard.
+ * XXX deal with thousands of IP aliases for listening ports later
+ */
+static __inline__ int tcp_lhashfn(unsigned short num)
+{
+       return num & (TCP_LHTABLE_SIZE - 1);
+}
+
+static __inline__ int tcp_sk_listen_hashfn(struct sock *sk)
+{
+       return tcp_lhashfn(sk->num);
+}
+
+/* Only those holding the sockhash lock call these two things here.
+ * Note the slightly gross overloading of sk->prev, AF_UNIX is the
+ * only other main benefactor of that member of SK, so who cares.
+ */
+static __inline__ void tcp_sk_bindify(struct sock *sk)
+{
+       int hashent = tcp_sk_bhashfn(sk);
+
+       sk->prev = tcp_bound_hash[hashent];
+       tcp_bound_hash[hashent] = sk;
+}
+
+static __inline__ void tcp_sk_unbindify(struct sock *sk)
+{
+       int hashent = tcp_sk_bhashfn(sk);
+       struct sock **htable = &tcp_bound_hash[hashent];
+
+       while(*htable) {
+               if(*htable == sk) {
+                       *htable = sk->prev;
+                       break;
+               }
+               htable = &((*htable)->prev);
+       }
+}
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #define NETHDR_SIZE    sizeof(struct ipv6hdr)
 #define TCP_PROBEWAIT_LEN (1*HZ)/* time to wait between probes when
                                 * I've got something to write and
                                 * there is no window                   */
-#define TCP_KEEPALIVE_TIME (180*60*HZ) /* two hours */
-#define TCP_KEEPALIVE_PROBES   9       /* Max of 9 keepalive probes    */
-#define TCP_KEEPALIVE_PERIOD (75*HZ)   /* period of keepalive check    */
+#define TCP_KEEPALIVE_TIME (180*60*HZ)         /* two hours */
+#define TCP_KEEPALIVE_PROBES   9               /* Max of 9 keepalive probes    */
+#define TCP_KEEPALIVE_PERIOD ((75*HZ)>>2)      /* period of keepalive check    */
 #define TCP_NO_CHECK   0       /* turn to one if you want the default
                                 * to be no checksum                    */
 
@@ -236,6 +303,8 @@ extern __inline int between(__u32 seq1, __u32 seq2, __u32 seq3)
 extern struct proto tcp_prot;
 extern struct tcp_mib tcp_statistics;
 
+extern unsigned short          tcp_good_socknum(void);
+
 extern void                    tcp_v4_err(struct sk_buff *skb,
                                           unsigned char *);
 
@@ -325,9 +394,6 @@ extern int  tcp_send_skb(struct sock *, struct sk_buff *);
 extern void tcp_send_ack(struct sock *sk);
 extern void tcp_send_delayed_ack(struct sock *sk, int max_timeout);
 
-/* tcp_input.c */
-extern void tcp_cache_zap(void);
-
 /* CONFIG_IP_TRANSPARENT_PROXY */
 extern int tcp_chkaddr(struct sk_buff *);
 
@@ -443,7 +509,6 @@ static __inline__ void tcp_set_state(struct sock *sk, int state)
                break;
 
        case TCP_CLOSE:
-               tcp_cache_zap();
                /* Should be about 2 rtt's */
                net_reset_timer(sk, TIME_DONE, min(tp->srtt * 2, TCP_DONE_TIME));
                /* fall through */
index aedde17a57ae320ffd8bd857d5e4af280c851684..0fa99fb1c038c8e2862f7e5cc9b40d50250017af 100644 (file)
 
 #include <linux/udp.h>
 
+#define UDP_HTABLE_SIZE                128
+
+/* udp.c: This needs to be shared by v4 and v6 because the lookup
+ *        and hashing code needs to work with different AF's yet
+ *        the port space is shared.
+ */
+extern struct sock *udp_hash[UDP_HTABLE_SIZE];
+
+extern unsigned short udp_good_socknum(void);
 
 #define UDP_NO_CHECK   0
 
@@ -39,7 +48,6 @@ extern int    udp_sendmsg(struct sock *sk, struct msghdr *msg, int len);
 
 extern int     udp_rcv(struct sk_buff *skb, unsigned short len);
 extern int     udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
-extern void    udp_cache_zap(void);    /* Remove udp last socket cache */
 
 /* CONFIG_IP_TRANSPARENT_PROXY */
 extern int     udp_chkaddr(struct sk_buff *skb);
index a5993856b37bcd7147fdd57c1e907cb5c81f630b..55fa2d88776e4edf96c00819025f302a6dd02a05 100644 (file)
@@ -274,6 +274,25 @@ static inline int goodness(struct task_struct * p, struct task_struct * prev, in
        return weight;
 }
 
+void allow_interrupts(void)
+{
+#if defined(__SMP__) && defined(__i386__)
+/* UGH UGH UGH. Damn broken IRQ handling test-fix for x86.. */
+       int this_cpu=smp_processor_id();
+       int lock_depth = current_set[this_cpu]->lock_depth;
+       if (lock_depth) {
+               cli();
+               current_set[this_cpu]->lock_depth = 0;
+               active_kernel_processor = NO_PROC_ID;
+               __asm__ __volatile__("lock ; btrl    $0, kernel_flag");
+               sti();
+               /* interrupts should work here */
+               lock_kernel();
+               current_set[this_cpu]->lock_depth += lock_depth-1;
+       }
+#endif
+}              
+
 /*
  *  'schedule()' is the scheduler function. It's a very simple and nice
  * scheduler: it's not perfect, but certainly works for most things.
@@ -293,7 +312,7 @@ asmlinkage void schedule(void)
        int this_cpu=smp_processor_id();
 
 /* check alarm, wake up any interruptible tasks that have got a signal */
-
+       allow_interrupts();
        lock_kernel();
        if (intr_count)
                goto scheduling_in_interrupt;
index 953b09eec7d221a71b9e5512beba04243340bbcf..819c0ae8be839686c925536708824ec1f863288d 100644 (file)
@@ -328,17 +328,14 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                
                case SO_LINGER: 
                  {
-                       int err;
                        len=min(len,sizeof(ling));
                        if (put_user(len, optlen)) 
-                       if (!err) {
-                               ling.l_onoff=sk->linger;
-                               ling.l_linger=sk->lingertime;
-                               err = copy_to_user(optval,&ling,len);
-                               if (err)
-                                   err = -EFAULT;
-                       }
-                       return err;
+                               return -EFAULT;
+                       ling.l_onoff=sk->linger;
+                       ling.l_linger=sk->lingertime;
+                       if (copy_to_user(optval,&ling,len))
+                               return -EFAULT;
+                       return 0;
                  }
                
                case SO_BSDCOMPAT:
index 988028119d36ea99bc32e4e58457fe30bce6813b..30f6d096a038483491790aea52b24052432e19bc 100644 (file)
@@ -50,6 +50,8 @@
  *             Alan Cox        :       Loosened bind a little.
  *             Mike McLagan    :       ADD/DEL DLCI Ioctls
  *     Willy Konynenberg       :       Transparent proxying support.
+ *             David S. Miller :       New socket lookup architecture.
+ *                                     Some other random speedups.
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
@@ -119,10 +121,6 @@ extern int tcp_get_info(char *, char **, off_t, int, int);
 extern int udp_get_info(char *, char **, off_t, int, int);
 
 
-struct sock * tcp_sock_array[SOCK_ARRAY_SIZE];
-struct sock * udp_sock_array[SOCK_ARRAY_SIZE];
-struct sock * raw_sock_array[SOCK_ARRAY_SIZE];
-
 #ifdef CONFIG_DLCI
 extern int dlci_ioctl(unsigned int, void*);
 #endif
@@ -134,293 +132,94 @@ int (*dlci_ioctl_hook)(unsigned int, void *) = NULL;
 int (*rarp_ioctl_hook)(unsigned int,void*) = NULL;
 
 /*
- *     See if a socket number is in use.
+ *     Destroy an AF_INET socket
  */
  
-static int sk_inuse(struct proto *prot, int num)
+static __inline__ void kill_sk_queues(struct sock *sk)
 {
-       struct sock *sk;
+       struct sk_buff *skb;
 
-       for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
-               sk != NULL;  sk=sk->next) 
-       {
-               if (sk->num == num) 
-                       return(1);
+       /* First the read buffer. */
+       while((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+               /* This will take care of closing sockets that were
+                * listening and didn't accept everything.
+                */
+               if (skb->sk != NULL && skb->sk != sk)
+                       skb->sk->prot->close(skb->sk, 0);
+               kfree_skb(skb, FREE_READ);
        }
-       return(0);
-}
 
+       /* Next, the error queue. */
+       while((skb = skb_dequeue(&sk->error_queue)) != NULL)
+               kfree_skb(skb, FREE_READ);
 
-/*
- *     Pick a new socket number
- */
+       /* Now the backlog. */
+       while((skb=skb_dequeue(&sk->back_log)) != NULL)
+               kfree_skb(skb, FREE_READ);
+}
 
-unsigned short get_new_socknum(struct proto *prot, unsigned short base)
+static __inline__ void kill_sk_now(struct sock *sk)
 {
-       static int start=0;
-
-       /*
-        * Used to cycle through the port numbers so the
-        * chances of a confused connection drop.
-        */
-        
-       int i, j;
-       int best = 0;
-       int size = 32767; /* a big num. */
-       struct sock *sk;
+       /* No longer exists. */
+       del_from_prot_sklist(sk);
 
-       if (base == 0) 
-               base = PROT_SOCK+1+(start & 1023);
-       if (base <= PROT_SOCK) 
-       {
-               base += PROT_SOCK+(start & 1023);
-       }
-
-       /*
-        *      Now look through the entire array and try to find an empty ptr. 
-        */
-        
-       for(i=0; i < SOCK_ARRAY_SIZE; i++) 
-       {
-               j = 0;
-               sk = prot->sock_array[(i+base+1) &(SOCK_ARRAY_SIZE -1)];
-               while(sk != NULL) 
-               {
-                       sk = sk->next;
-                       j++;
-               }
-               if (j == 0) 
-               {
-                       start =(i+1+start )&1023;
-                       return(i+base+1);
-               }
-               if (j < size) 
-               {
-                       best = i;
-                       size = j;
-               }
-       }
+       /* This is gross, but needed for SOCK_PACKET -DaveM */
+       if(sk->prot->unhash)
+               sk->prot->unhash(sk);
 
-       /* Now make sure the one we want is not in use. */
-
-       while(sk_inuse(prot, base +best+1)) 
-       {
-               best += SOCK_ARRAY_SIZE;
-       }
-       return(best+base+1);
+       if(sk->opt)
+               kfree(sk->opt);
+       dst_release(sk->dst_cache);
+       sk_free(sk);
 }
 
-/*
- *     Add a socket into the socket tables by number.
- */
-
-void inet_put_sock(unsigned short num, struct sock *sk)
+static __inline__ void kill_sk_later(struct sock *sk)
 {
-       struct sock **skp, *tmp;
-       int mask;
-       unsigned long flags;
-       
-       if(sk->type==SOCK_PACKET)
-               return;
-
-       sk->num = num;
-       sk->next = NULL;
-       num = num &(SOCK_ARRAY_SIZE -1);
-
+       /* this should never happen. */
+       /* actually it can if an ack has just been sent. */
        /* 
-        *      We can't have an interrupt re-enter here. 
-        */
-        
-       save_flags(flags);
-       cli();
-
-       sk->prot->inuse += 1;
-       if (sk->prot->highestinuse < sk->prot->inuse)
-               sk->prot->highestinuse = sk->prot->inuse;
-
-       if (sk->prot->sock_array[num] == NULL) 
-       {
-               sk->prot->sock_array[num] = sk;
-               restore_flags(flags);
-               return;
-       }
-       
-       restore_flags(flags);
-       for(mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask) 
-       {
-               if ((mask & sk->rcv_saddr) &&
-                   (mask & sk->rcv_saddr) != (mask & 0xffffffff)) 
-               {
-                       mask = mask << 8;
-                       break;
-               }
-       }
-
-       /*
-        * add the socket to the sock_array[]..
+        * It's more normal than that...
+        * It can happen because a skb is still in the device queues
+        * [PR]
         */
-       skp = sk->prot->sock_array + num;
-       cli();
-       while ((tmp = *skp) != NULL) {
-               if (!(tmp->rcv_saddr & mask))
-                       break;
-               skp = &tmp->next;
-       }
-       sk->next = tmp;
-       *skp = sk;
-       sti();
-}
-
-/*
- *     Remove a socket from the socket tables.
- */
-
-void inet_remove_sock(struct sock *sk1)
-{
-       struct sock **p;
-       unsigned long flags;
-
-       if (sk1->type==SOCK_PACKET)
-               return;
-               
-       if (!sk1->prot) 
-       {
-               NETDEBUG(printk("sock.c: remove_sock: sk1->prot == NULL\n"));
-               return;
-       }
+                 
+       printk("Socket destroy delayed (r=%d w=%d)\n",
+              sk->rmem_alloc, sk->wmem_alloc);
 
-       /* We can't have this changing out from under us. */
-       save_flags(flags);
-       cli();
-       
-       p=&(sk1->prot->sock_array[sk1->num & (SOCK_ARRAY_SIZE -1)]);
-       
-       while(*p!=NULL)
-       {
-               if(*p==sk1)
-               {
-                       sk1->prot->inuse--;
-                       *p=sk1->next;
-                       break;
-               }
-               p=&((*p)->next);
-       }
-       restore_flags(flags);
+       sk->destroy = 1;
+       sk->ack_backlog = 0;
+       release_sock(sk);
+       net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
 }
 
-/*
- *     Destroy an AF_INET socket
- */
 void destroy_sock(struct sock *sk)
 {
-       struct sk_buff *skb;
-
        lock_sock(sk);                  /* just to be safe. */
 
-       /*
-        *      Now we can no longer get new packets or once the
-        *      timers are killed, send them.
+       /* Now we can no longer get new packets or once the
+        * timers are killed, send them.
         */
-        
        net_delete_timer(sk);
 
        if (sk->prot->destroy)
                sk->prot->destroy(sk);
 
-       /*
-        *      Clean up the read buffer.
-        */
-
-       while((skb=skb_dequeue(&sk->receive_queue))!=NULL) 
-       {
-               /*
-                * This will take care of closing sockets that were
-                * listening and didn't accept everything.
-                */
-               if (skb->sk != NULL && skb->sk != sk) 
-               {
-                       IS_SKB(skb);
-                       skb->sk->prot->close(skb->sk, 0);
-               }
-               IS_SKB(skb);
-               kfree_skb(skb, FREE_READ);
-       }
-
-       /*
-        *      Clean up the error queue.
-        */
-
-       while((skb=skb_dequeue(&sk->error_queue))!=NULL) 
-       {
-               IS_SKB(skb);
-               kfree_skb(skb, FREE_READ);
-       }
-
-       /*
-        *      Now the backlog. 
-        */
-        
-       while((skb=skb_dequeue(&sk->back_log))!=NULL) 
-       {
-               IS_SKB(skb);
-               kfree_skb(skb, FREE_READ);
-       }
+       kill_sk_queues(sk);
 
-       /*
-        *      Now if it has a half accepted/ closed socket. 
-        */
-        
-       if (sk->pair) 
-       {
+       /* Now if it has a half accepted/ closed socket. */
+       if (sk->pair) {
                sk->pair->prot->close(sk->pair, 0);
                sk->pair = NULL;
        }
 
-       /*
-        * Now if everything is gone we can free the socket
+       /* Now if everything is gone we can free the socket
         * structure, otherwise we need to keep it around until
         * everything is gone.
         */
-
-       if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0) 
-       {
-/*
- *     It is wrong! We MUST unlink socket from socket table
- *     even earlier, than it used to be.
- *     F.e. TCP socket must be unlinked at the moment, when
- *     it goes to TCP_CLOSE. --ANK
- */
-               inet_remove_sock(sk);
-
-               if(sk->opt)
-                       kfree(sk->opt);
-               dst_release(sk->dst_cache);
-               /*
-                *      This one is pure paranoia. I'll take it out
-                *      later once I know the bug is buried.
-                */
-               tcp_cache_zap();
-               sk_free(sk);
-       } 
-       else 
-       {
-               /* this should never happen. */
-               /* actually it can if an ack has just been sent. */
-               /* 
-                * It's more normal than that...
-                * It can happen because a skb is still in the device queues
-                * [PR]
-                */
-                 
-               printk("Socket destroy delayed (r=%d w=%d)\n",
-                       sk->rmem_alloc, sk->wmem_alloc);
-
-               sk->destroy = 1;
-               sk->ack_backlog = 0;
-               release_sock(sk);
-               net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
-       }
+       if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0)
+               kill_sk_now(sk);
+       else
+               kill_sk_later(sk);
 }
 
 /*
@@ -467,15 +266,13 @@ int inet_getsockopt(struct socket *sock, int level, int optname,
 static int inet_autobind(struct sock *sk)
 {
        /* We may need to bind the socket. */
-       if (sk->num == 0) 
-       {
-               sk->num = get_new_socknum(sk->prot, 0);
+       if (sk->num == 0) {
+               sk->num = sk->prot->good_socknum();
                if (sk->num == 0) 
                        return(-EAGAIN);
-               udp_cache_zap();
-               tcp_cache_zap();
-               inet_put_sock(sk->num, sk);
-               sk->dummy_th.source = ntohs(sk->num);
+               sk->dummy_th.source = htons(sk->num);
+               sk->prot->hash(sk);
+               add_to_prot_sklist(sk);
        }
        return 0;
 }
@@ -491,7 +288,7 @@ int inet_listen(struct socket *sock, int backlog)
        if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
                return(-EINVAL);
 
-       if (inet_autobind(sk)!=0)
+       if (inet_autobind(sk) != 0)
                return -EAGAIN;
 
        /* We might as well re use these. */ 
@@ -506,10 +303,11 @@ int inet_listen(struct socket *sock, int backlog)
        if ((unsigned) backlog > SOMAXCONN)
                backlog = SOMAXCONN;
        sk->max_ack_backlog = backlog;
-       if (sk->state != TCP_LISTEN)
-       {
+       if (sk->state != TCP_LISTEN) {
                sk->ack_backlog = 0;
                sk->state = TCP_LISTEN;
+               sk->prot->rehash(sk);
+               add_to_prot_sklist(sk);
        }
        sk->socket->flags |= SO_ACCEPTCON;
        return(0);
@@ -526,87 +324,44 @@ static int inet_create(struct socket *sock, int protocol)
 {
        struct sock *sk;
        struct proto *prot;
-       int err;
 
        sock->state = SS_UNCONNECTED;
        sk = sk_alloc(GFP_KERNEL);
        if (sk == NULL) 
-               return(-ENOBUFS);
-
-       /*
-        *      Note for tcp that also wiped the dummy_th block for us.
-        */
-       switch (sock->type) 
-       {
-               case SOCK_STREAM:
-               case SOCK_SEQPACKET:
-                       if (protocol && protocol != IPPROTO_TCP) 
-                       {
-                               sk_free(sk);
-                               return(-EPROTONOSUPPORT);
-                       }
-                       protocol = IPPROTO_TCP;
-                       sk->no_check = TCP_NO_CHECK;
-                       if (ipv4_config.no_pmtu_disc)
-                               sk->ip_pmtudisc = IP_PMTUDISC_DONT;
-                       else
-                               sk->ip_pmtudisc = IP_PMTUDISC_WANT;
-                       prot = &tcp_prot;
-                       sock->ops = &inet_stream_ops;
-                       break;
-
-               case SOCK_DGRAM:
-                       if (protocol && protocol != IPPROTO_UDP) 
-                       {
-                               sk_free(sk);
-                               return(-EPROTONOSUPPORT);
-                       }
-                       protocol = IPPROTO_UDP;
-                       sk->no_check = UDP_NO_CHECK;
-                       sk->ip_pmtudisc = IP_PMTUDISC_DONT;
-                       prot=&udp_prot;
-                       sock->ops = &inet_dgram_ops;
-                       break;
-      
-               case SOCK_RAW:
-                       if (!suser()) 
-                       {
-                               sk_free(sk);
-                               return(-EPERM);
-                       }
-                       if (!protocol) 
-                       {
-                               sk_free(sk);
-                               return(-EPROTONOSUPPORT);
-                       }
-                       prot = &raw_prot;
-                       sk->reuse = 1;
+               goto do_oom;
+
+       /* Note for tcp that also wiped the dummy_th block for us. */
+       if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) {
+               if (protocol && protocol != IPPROTO_TCP)
+                       goto free_and_noproto;
+               protocol = IPPROTO_TCP;
+               sk->no_check = TCP_NO_CHECK;
+               if (ipv4_config.no_pmtu_disc)
                        sk->ip_pmtudisc = IP_PMTUDISC_DONT;
-                       sk->num = protocol;
-                       sock->ops = &inet_dgram_ops;
-                       break;
-
-               case SOCK_PACKET:
-                       if (!suser()) 
-                       {
-                               sk_free(sk);
-                               return(-EPERM);
-                       }
-                       if (!protocol) 
-                       {
-                               sk_free(sk);
-                               return(-EPROTONOSUPPORT);
-                       }
-                       prot = &packet_prot;
-                       sk->reuse = 1;
-                       sk->ip_pmtudisc = IP_PMTUDISC_DONT;
-                       sk->num = protocol;
-                       sock->ops = &inet_dgram_ops;
-                       break;
-
-               default:
-                       sk_free(sk);
-                       return(-ESOCKTNOSUPPORT);
+               else
+                       sk->ip_pmtudisc = IP_PMTUDISC_WANT;
+               prot = &tcp_prot;
+               sock->ops = &inet_stream_ops;
+       } else if(sock->type == SOCK_DGRAM) {
+               if (protocol && protocol != IPPROTO_UDP)
+                       goto free_and_noproto;
+               protocol = IPPROTO_UDP;
+               sk->no_check = UDP_NO_CHECK;
+               sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+               prot=&udp_prot;
+               sock->ops = &inet_dgram_ops;
+       } else if(sock->type == SOCK_RAW || sock->type == SOCK_PACKET) {
+               if (!suser())
+                       goto free_and_badperm;
+               if (!protocol)
+                       goto free_and_noproto;
+               prot = (sock->type == SOCK_RAW) ? &raw_prot : &packet_prot;
+               sk->reuse = 1;
+               sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+               sk->num = protocol;
+               sock->ops = &inet_dgram_ops;
+       } else {
+               goto free_and_badtype;
        }
 
        sock_init_data(sock,sk);
@@ -636,33 +391,47 @@ static int inet_create(struct socket *sock, int protocol)
        sk->ip_mc_index=0;
        sk->ip_mc_list=NULL;
        
-       /*
-        *      Speed up by setting some standard state for the dummy_th
+       /*      Speed up by setting some standard state for the dummy_th
         *      if TCP uses it (maybe move to tcp_init later)
         */
        
-       if (sk->num) 
-       {
-               /*
-                * It assumes that any protocol which allows
+       if (sk->num) {
+               /* It assumes that any protocol which allows
                 * the user to assign a number at socket
                 * creation time automatically
                 * shares.
                 */
-               inet_put_sock(sk->num, sk);
                sk->dummy_th.source = ntohs(sk->num);
+
+               /* This is gross, but needed for SOCK_PACKET -DaveM */
+               if(sk->prot->hash)
+                       sk->prot->hash(sk);
+               add_to_prot_sklist(sk);
        }
 
-       if (sk->prot->init) 
-       {
-               err = sk->prot->init(sk);
-               if (err != 0) 
-               {
+       if (sk->prot->init) {
+               int err = sk->prot->init(sk);
+               if (err != 0) {
                        destroy_sock(sk);
                        return(err);
                }
        }
        return(0);
+
+free_and_badtype:
+       sk_free(sk);
+       return -ESOCKTNOSUPPORT;
+
+free_and_badperm:
+       sk_free(sk);
+       return -EPERM;
+
+free_and_noproto:
+       sk_free(sk);
+       return -EPROTONOSUPPORT;
+
+do_oom:
+       return -ENOBUFS;
 }
 
 
@@ -684,193 +453,99 @@ static int inet_dup(struct socket *newsock, struct socket *oldsock)
 int inet_release(struct socket *sock, struct socket *peersock)
 {
        struct sock *sk = sock->sk;
-       unsigned long timeout;
-
-       if (sk==NULL)
-               return 0;
 
-       if (sock->state != SS_UNCONNECTED)
-               sock->state = SS_DISCONNECTING;
+       if (sk) {
+               unsigned long timeout;
 
-       sk->state_change(sk);
+               /* Begin closedown and wake up sleepers. */
+               if (sock->state != SS_UNCONNECTED)
+                       sock->state = SS_DISCONNECTING;
+               sk->state_change(sk);
 
-       /* Start closing the connection.  This may take a while. */
+               /* Applications forget to leave groups before exiting */
+               ip_mc_drop_socket(sk);
 
-       /* Applications forget to leave groups before exiting */
-       ip_mc_drop_socket(sk);
-
-       /*
-        * If linger is set, we don't return until the close
-        * is complete.  Otherwise we return immediately. The
-        * actually closing is done the same either way.
-        *
-        * If the close is due to the process exiting, we never
-        * linger..
-        */
-       timeout = 0;
-       if (sk->linger)
-       {
-               timeout = ~0UL;
-               if (!sk->lingertime)
-                       timeout = jiffies + HZ*sk->lingertime;
-       }
-       if (current->flags & PF_EXITING)
+               /* If linger is set, we don't return until the close
+                * is complete.  Otherwise we return immediately. The
+                * actually closing is done the same either way.
+                *
+                * If the close is due to the process exiting, we never
+                * linger..
+                */
                timeout = 0;
+               if (sk->linger && !(current->flags & PF_EXITING)) {
+                       timeout = ~0UL;
 
-       sock->sk = NULL;
-       sk->socket = NULL;
-
-       sk->prot->close(sk, timeout);
+                       /* XXX This makes no sense whatsoever... -DaveM */
+                       if (!sk->lingertime)
+                               timeout = jiffies + HZ*sk->lingertime;
+               }
+               sock->sk = NULL;
+               sk->socket = NULL;
+               sk->prot->close(sk, timeout);
+       }
        return(0);
 }
 
-
-static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
-                    int addr_len)
+static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
        struct sockaddr_in *addr=(struct sockaddr_in *)uaddr;
-       struct sock *sk=sock->sk, *sk2;
-       unsigned short snum = 0 /* Stoopid compiler.. this IS ok */;
+       struct sock *sk=sock->sk;
+       unsigned short snum;
        int chk_addr_ret;
 
-       /*
-        *      If the socket has its own bind function then use it.
-        */
-        
+       /* If the socket has its own bind function then use it. (RAW and PACKET) */
        if(sk->prot->bind)
-               return sk->prot->bind(sk,uaddr, addr_len);
+               return sk->prot->bind(sk, uaddr, addr_len);
                
-       /* check this error. */
-       if (sk->state != TCP_CLOSE)
-               return(-EINVAL);
-       if(addr_len<sizeof(struct sockaddr_in))
+       /* Check these errors (active socket, bad address length, double bind). */
+       if ((sk->state != TCP_CLOSE)                    ||
+           (addr_len < sizeof(struct sockaddr_in))     ||
+           (sk->num != 0))
                return -EINVAL;
                
-       if (sock->type != SOCK_RAW)
-       {
-               if (sk->num != 0) 
-                       return(-EINVAL);
-
-               snum = ntohs(addr->sin_port);
-               
+       snum = ntohs(addr->sin_port);
 #ifdef CONFIG_IP_MASQUERADE
-               /*
-                *      The kernel masquerader needs some ports
-                */             
-               if(snum>=PORT_MASQ_BEGIN && snum<=PORT_MASQ_END)
-                       return -EADDRINUSE;
+       /* The kernel masquerader needs some ports. */
+       if((snum >= PORT_MASQ_BEGIN) && (snum <= PORT_MASQ_END))
+               return -EADDRINUSE;
 #endif          
-
-               if (snum == 0) 
-                       snum = get_new_socknum(sk->prot, 0);
-               if (snum < PROT_SOCK && !suser()) 
-                       return(-EACCES);
-       }
+       if (snum == 0) 
+               snum = sk->prot->good_socknum();
+       if (snum < PROT_SOCK && !suser())
+               return(-EACCES);
        
        chk_addr_ret = __ip_chk_addr(addr->sin_addr.s_addr);
+       if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR &&
+           chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST) {
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
-       /*
-        * Superuser may bind to any address to allow transparent proxying.
-        */
-       if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST && !suser())
-#else
-       if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST)
+               /* Superuser may bind to any address to allow transparent proxying. */
+               if(!suser())
 #endif
-               return(-EADDRNOTAVAIL); /* Source address MUST be ours! */
+                       return -EADDRNOTAVAIL;  /* Source address MUST be ours! */
+       }
 
-#ifndef CONFIG_IP_TRANSPARENT_PROXY
-       /*
-        * Am I just thick or is this test really always true after the one
-        * above?  Just taking the test out appears to be the easiest way to
-        * make binds to remote addresses for transparent proxying work.
+       /*      We keep a pair of addresses. rcv_saddr is the one
+        *      used by hash lookups, and saddr is used for transmit.
+        *
+        *      In the BSD API these are the same except where it
+        *      would be illegal to use them (multicast/broadcast) in
+        *      which case the sending device address is used.
         */
-       if (chk_addr_ret || addr->sin_addr.s_addr == 0)
-       {
-#endif
-               /*
-                *      We keep a pair of addresses. rcv_saddr is the one
-                *      used by get_sock_*(), and saddr is used for transmit.
-                *
-                *      In the BSD API these are the same except where it
-                *      would be illegal to use them (multicast/broadcast) in
-                *      which case the sending device address is used.
-                */
-               sk->rcv_saddr = addr->sin_addr.s_addr;
-               if(chk_addr_ret==IS_MULTICAST||chk_addr_ret==IS_BROADCAST)
-                       sk->saddr = 0;  /* Use device */
-               else
-                       sk->saddr = addr->sin_addr.s_addr;
-#ifndef CONFIG_IP_TRANSPARENT_PROXY
-       }
-#endif
-       if (sock->type != SOCK_RAW)
-       {
-               /* Make sure we are allowed to bind here. */
-               cli();
-               for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
-                                       sk2 != NULL; sk2 = sk2->next) 
-               {
-                       /*
-                        *      Hash collision or real match ?
-                        */
-                        
-                       if (sk2->num != snum) 
-                               continue;
-                               
-                       /*
-                        *      Either bind on the port is wildcard means
-                        *      they will overlap and thus be in error
-                        */                     
-                        
-                       if (!sk2->rcv_saddr || !sk->rcv_saddr)
-                       {
-                               /*
-                                *      Allow only if both are setting reuse.
-                                */
-                               if(sk2->reuse && sk->reuse && sk2->state!=TCP_LISTEN)
-                                       continue;
-                               sti();
-                               return(-EADDRINUSE);
-                       }
-
-                       /*
-                        *      Two binds match ?
-                        */
-
-                       if (sk2->rcv_saddr != sk->rcv_saddr) 
-                               continue;
-                       /*
-                        *      Reusable port ?
-                        */
-
-                       if (!sk->reuse)
-                       {
-                               sti();
-                               return(-EADDRINUSE);
-                       }
-                       
-                       /*
-                        *      Reuse ?
-                        */
-                        
-                       if (!sk2->reuse || sk2->state==TCP_LISTEN)
-                       {
-                               sti();
-                               return(-EADDRINUSE);
-                       }
-               }
-               sti();
-
-               inet_remove_sock(sk);
-               if (sock->type==SOCK_DGRAM)
-                       udp_cache_zap();
-               if (sock->type==SOCK_STREAM)
-                       tcp_cache_zap();
-               inet_put_sock(snum, sk);
-               sk->dummy_th.source = ntohs(sk->num);
-               sk->daddr = 0;
-               sk->dummy_th.dest = 0;
-       }
+       sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
+       if(chk_addr_ret == IS_MULTICAST || chk_addr_ret == IS_BROADCAST)
+               sk->saddr = 0;  /* Use device */
+
+       /* Make sure we are allowed to bind here. */
+       if(sk->prot->verify_bind(sk, snum))
+               return -EADDRINUSE;
+
+       sk->num = snum;
+       sk->dummy_th.source = ntohs(snum);
+       sk->daddr = 0;
+       sk->dummy_th.dest = 0;
+       sk->prot->rehash(sk);
+       add_to_prot_sklist(sk);
        dst_release(sk->dst_cache);
        sk->dst_cache=NULL;
        return(0);
@@ -882,7 +557,7 @@ int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr,
        struct sock *sk=sock->sk;
        int err;
 
-       if (inet_autobind(sk)!=0)
+       if (inet_autobind(sk) != 0)
                return(-EAGAIN);
        if (sk->prot->connect == NULL) 
                return(-EOPNOTSUPP);
@@ -903,85 +578,61 @@ int inet_stream_connect(struct socket *sock, struct sockaddr * uaddr,
        struct sock *sk=sock->sk;
        int err;
 
-       switch (sock->state)
-       {
-               case SS_UNCONNECTED:
-                       /* This is ok... continue with connect */
-                       break;
-               case SS_CONNECTED:
-                       /* Socket is already connected */
+       if(sock->state != SS_UNCONNECTED && sock->state != SS_CONNECTING) {
+               if(sock->state == SS_CONNECTED)
                        return -EISCONN;
-               case SS_CONNECTING:
-                       /* Not yet connected... we will check this. */
-               
-                       /*
-                        *      FIXME:  for all protocols what happens if you start
-                        *      an async connect fork and both children connect. Clean
-                        *      this up in the protocols!
-                        */
-                       break;
-               default:
-                       return(-EINVAL);
-       }
-
-       if (sock->state == SS_CONNECTING && tcp_connected(sk->state))
-       {
-               sock->state = SS_CONNECTED;
-               /* Connection completing after a connect/EINPROGRESS/select/connect */
-               return 0;       /* Rock and roll */
+               return -EINVAL;
        }
 
-       if (sock->state == SS_CONNECTING && sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK))
-       {
-               if(sk->err!=0)
-                       return sock_error(sk);
-               return -EALREADY;       /* Connecting is currently in progress */
-       }
-       if (sock->state != SS_CONNECTING) 
-       {
+       if(sock->state == SS_CONNECTING) {
+               if(tcp_connected(sk->state)) {
+                       sock->state = SS_CONNECTED;
+                       return 0;
+               }
+               if(sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK)) {
+                       if(sk->err)
+                               return sock_error(sk);
+                       return -EALREADY;
+               }
+       } else {
                /* We may need to bind the socket. */
-               if (inet_autobind(sk)!=0)
+               if (inet_autobind(sk) != 0)
                        return(-EAGAIN);
                if (sk->prot->connect == NULL) 
                        return(-EOPNOTSUPP);
                err = sk->prot->connect(sk, uaddr, addr_len);
-               if (err < 0) 
+               if (err < 0)
                        return(err);
                sock->state = SS_CONNECTING;
        }
        
-       if (sk->state > TCP_FIN_WAIT2 && sock->state==SS_CONNECTING)
-       {
-               sock->state=SS_UNCONNECTED;
+       if (sk->state > TCP_FIN_WAIT2 && sock->state == SS_CONNECTING) {
+               sock->state = SS_UNCONNECTED;
                return sock_error(sk);
        }
 
        if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) 
                return (-EINPROGRESS);
 
-       cli(); /* avoid the race condition */
-       while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) 
-       {
+       cli();
+       while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
                interruptible_sleep_on(sk->sleep);
-               if (current->signal & ~current->blocked) 
-               {
+               if (current->signal & ~current->blocked) {
                        sti();
                        return(-ERESTARTSYS);
                }
                /* This fixes a nasty in the tcp/ip code. There is a hideous hassle with
                   icmp error packets wanting to close a tcp or udp socket. */
-               if (sk->err && sk->protocol == IPPROTO_TCP)
-               {
+               if (sk->err && sk->protocol == IPPROTO_TCP) {
                        sock->state = SS_UNCONNECTED;
                        sti();
                        return sock_error(sk); /* set by tcp_err() */
                }
        }
        sti();
-       sock->state = SS_CONNECTED;
 
-       if (sk->state != TCP_ESTABLISHED && sk->err) 
-       {
+       sock->state = SS_CONNECTED;
+       if ((sk->state != TCP_ESTABLISHED) && sk->err) {
                sock->state = SS_UNCONNECTED;
                return sock_error(sk);
        }
@@ -994,32 +645,24 @@ int inet_stream_connect(struct socket *sock, struct sockaddr * uaddr,
 
 int inet_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-       struct sock *sk1 = sock->sk;
+       struct sock *sk1 = sock->sk, *sk2;
        struct sock *newsk = newsock->sk;
-       struct sock *sk2;
-       int err;
+       int err = -EINVAL;
 
-       if (sock->state != SS_UNCONNECTED)
-               return -EINVAL;
-       if (!(sock->flags & SO_ACCEPTCON)) 
-               return -EINVAL;
-       if (sk1->prot->accept == NULL) 
-               return -EOPNOTSUPP;
+       if (sock->state != SS_UNCONNECTED || !(sock->flags & SO_ACCEPTCON))
+               goto do_err;
 
-       /*
-        *      Restore the state if we have been interrupted, and then returned. 
-        */
-        
-       if (sk1->pair != NULL ) 
-       {
+       err = -EOPNOTSUPP;
+       if (sk1->prot->accept == NULL)
+               goto do_err;
+
+       /* Restore the state if we have been interrupted, and then returned. */
+       if (sk1->pair != NULL) {
                sk2 = sk1->pair;
                sk1->pair = NULL;
-       } 
-       else
-       {
-               sk2 = sk1->prot->accept(sk1,flags);
-               if (sk2 == NULL) 
-                       return sock_error(sk1);
+       } else {
+               if((sk2 = sk1->prot->accept(sk1,flags)) == NULL)
+                       goto do_sk1_err;
        }
 
        /*
@@ -1027,7 +670,6 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags)
         *      We need to free it up because the tcp module creates
         *      its own when it accepts one.
         */
-
        sk2->sleep = newsk->sleep;
 
        newsock->sk = sk2;
@@ -1035,56 +677,54 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags)
        newsk->socket = NULL;
 
        if (flags & O_NONBLOCK)
-       {
-               destroy_sock(newsk);
-               return(0);
-       }
+               goto do_half_success;
 
-       cli(); /* avoid the race. */
-       while (sk2->state == TCP_SYN_RECV) 
-       {
+       cli();
+       while (sk2->state == TCP_SYN_RECV) {
                interruptible_sleep_on(sk2->sleep);
-               if (current->signal & ~current->blocked) 
-               {
-                       sti();
-                       sk1->pair = sk2;
-                       sk2->sleep = NULL;
-                       sk2->socket = NULL;
-
-                       newsock->sk = newsk;
-                       newsk->socket = newsock;
-                       return -ERESTARTSYS;
-               }
+               if (current->signal & ~current->blocked)
+                       goto do_interrupted;
        }
        sti();
-
-       if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) 
-       {
-               err = sock_error(sk2);
-               sk2->sleep = NULL;
-               sk2->socket = NULL;
-               destroy_sock(sk2);
-
-               newsock->sk = newsk;
-               newsk->socket = newsock;
-
-               return err;
-       }
+       if(sk2->state == TCP_ESTABLISHED)
+               goto do_full_success;
+       if(sk2->err > 0)
+               goto do_connect_err;
+       err = -ECONNABORTED;
        if (sk2->state == TCP_CLOSE)
-       {
-               sk2->sleep = NULL;
-               sk2->socket = NULL;
-               destroy_sock(sk2);
-
-               newsock->sk = newsk;
-               newsk->socket = newsock;
-
-               return -ECONNABORTED;
-       }
-
+               goto do_bad_connection;
+do_full_success:
        destroy_sock(newsk);
        newsock->state = SS_CONNECTED;
+       return 0;
+
+do_half_success:
+       destroy_sock(newsk);
        return(0);
+
+do_connect_err:
+       err = sock_error(sk2);
+do_bad_connection:
+       sk2->sleep = NULL;
+       sk2->socket = NULL;
+       destroy_sock(sk2);
+       newsock->sk = newsk;
+       newsk->socket = newsock;
+       return err;
+
+do_interrupted:
+       sti();
+       sk1->pair = sk2;
+       sk2->sleep = NULL;
+       sk2->socket = NULL;
+       newsock->sk = newsk;
+       newsk->socket = newsock;
+       err = -ERESTARTSYS;
+do_err:
+       return err;
+do_sk1_err:
+       err = sock_error(sk1);
+       return err;
 }
 
 
@@ -1095,19 +735,16 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags)
 static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
                 int *uaddr_len, int peer)
 {
-       struct sock *sk=sock->sk;
-       struct sockaddr_in *sin=(struct sockaddr_in *)uaddr;
+       struct sock *sk         = sock->sk;
+       struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
   
        sin->sin_family = AF_INET;
-       if (peer) 
-       {
+       if (peer) {
                if (!tcp_connected(sk->state)) 
                        return(-ENOTCONN);
                sin->sin_port = sk->dummy_th.dest;
                sin->sin_addr.s_addr = sk->daddr;
-       } 
-       else 
-       {
+       } else {
                __u32 addr = sk->rcv_saddr;
                if (!addr)
                        addr = sk->saddr;
@@ -1134,7 +771,7 @@ int inet_recvmsg(struct socket *sock, struct msghdr *msg, int size,
        if (sk->err)
                return sock_error(sk);
        /* We may need to bind the socket. */
-       if (inet_autobind(sk)!=0)
+       if (inet_autobind(sk) != 0)
                return(-EAGAIN);
        err = sk->prot->recvmsg(sk, msg, size, flags&MSG_DONTWAIT,
                                flags&~MSG_DONTWAIT, &addr_len);
@@ -1157,9 +794,11 @@ int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size,
                return(-EOPNOTSUPP);
        if(sk->err)
                return sock_error(sk);
+
        /* We may need to bind the socket. */
-       if(inet_autobind(sk)!=0)
+       if(inet_autobind(sk) != 0)
                return -EAGAIN;
+
        return sk->prot->sendmsg(sk, msg, size);
 }
 
@@ -1168,8 +807,7 @@ int inet_shutdown(struct socket *sock, int how)
 {
        struct sock *sk = sock->sk;
 
-       /*
-        * This should really check to make sure
+       /* This should really check to make sure
         * the socket is a TCP socket. (WHY AC...)
         */
        how++; /* maps 0->1 has the advantage of making bit 1 rcvs and
@@ -1329,256 +967,6 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
        return(0);
 }
 
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-/*
- * Some routines for the for loop in get_sock which sometimes needs to walk
- * two linked lists in sequence.  Could use macros as well.
- * Does anyone know a nicer way to code this?
- */
-static __inline__ struct sock *secondlist(unsigned short hpnum, struct sock *s,
-                               int *pfirstpass, struct proto *prot)
-{
-       if (s == NULL && (*pfirstpass)-- )
-               return prot->sock_array[hpnum & (SOCK_ARRAY_SIZE - 1)];
-       else
-               return s;
-}
-static __inline__ struct sock *get_sock_loop_init(unsigned short hnum,
-                       unsigned short hpnum, struct sock *s,
-                       int *pfirstpass, struct proto *prot)
-{
-       s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
-       return secondlist(hpnum, s, pfirstpass, prot);
-}
-static __inline__ struct sock *get_sock_loop_next(unsigned short hnum,
-                       unsigned short hpnum, struct sock *s,
-                       int *pfirstpass, struct proto *prot)
-{
-       s = s->next;
-       return secondlist(hpnum, s, pfirstpass, prot);
-}
-
-struct sock *get_sock_proxy(struct proto *prot, unsigned short num,
-                               unsigned long raddr,
-                               unsigned short rnum, unsigned long laddr,
-                               unsigned long paddr, unsigned short pnum)
-{
-       struct sock *s = 0;
-       struct sock *result = NULL;
-       int badness = -1;
-       unsigned short hnum;
-       unsigned short hpnum;
-       int firstpass = 1;
-
-       hnum = ntohs(num);
-       hpnum = ntohs(pnum);
-
-       /*
-        * SOCK_ARRAY_SIZE must be a power of two.  This will work better
-        * than a prime unless 3 or more sockets end up using the same
-        * array entry.  This should not be a problem because most
-        * well known sockets don't overlap that much, and for
-        * the other ones, we can just be careful about picking our
-        * socket number when we choose an arbitrary one.
-        */
-
-       for(s = get_sock_loop_init(hnum, hpnum, s, &firstpass, prot);
-               s != NULL;
-               s = get_sock_loop_next(hnum, hpnum, s, &firstpass, prot))
-       {
-               int score = 0;
-
-               /* accept the addressed port or the redirect (proxy) port */
-               if (s->num != hnum && s->num != hpnum)
-                       continue;
-
-               if(s->dead && (s->state == TCP_CLOSE))
-                       continue;
-               /* local address matches? */
-               if (s->rcv_saddr) {
-                       /*
-                        * If this is redirected traffic, it must either
-                        * match on the redirected port/ip-address or on
-                        * the actual destination, not on a mixture.
-                        * There must be a simpler way to express this...
-                        */
-                       if ((s->num != hpnum || s->rcv_saddr != paddr)
-                           && (s->num != hnum || s->rcv_saddr != laddr))
-                               continue;
-                       score++;
-               }
-               /* remote address matches? */
-               if (s->daddr) {
-                       if (s->daddr != raddr)
-                               continue;
-                       score++;
-               }
-               /* remote port matches? */
-               if (s->dummy_th.dest) {
-                       if (s->dummy_th.dest != rnum)
-                               continue;
-                       score++;
-               }
-               /* perfect match? */
-               if (score == 3 && s->num == hnum)
-                       return s;
-               /* no, check if this is the best so far.. */
-               if (score <= badness)
-                       continue;
-               /* don't accept near matches on the actual destination
-                * port with IN_ADDR_ANY for redirected traffic, but do
-                * allow explicit remote address listens.  (disputable)
-                */
-               if (s->num != hpnum && !s->rcv_saddr)
-                       continue;
-               result = s;
-               badness = score;
-       }
-       return result;
-}
-#endif
-
-/*
- * This routine must find a socket given a TCP or UDP header.
- * Everything is assumed to be in net order.
- *
- * We give priority to more closely bound ports: if some socket
- * is bound to a particular foreign address, it will get the packet
- * rather than somebody listening to any address..
- */
-
-struct sock *get_sock(struct proto *prot, unsigned short num,
-                               unsigned long raddr,
-                               unsigned short rnum, unsigned long laddr)
-{
-       struct sock *s = 0;
-       struct sock *result = NULL;
-       int badness = -1;
-       unsigned short hnum;
-
-       hnum = ntohs(num);
-
-       /*
-        * SOCK_ARRAY_SIZE must be a power of two.  This will work better
-        * than a prime unless 3 or more sockets end up using the same
-        * array entry.  This should not be a problem because most
-        * well known sockets don't overlap that much, and for
-        * the other ones, we can just be careful about picking our
-        * socket number when we choose an arbitrary one.
-        */
-
-       for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
-                       s != NULL; s = s->next) 
-       {
-               int score = 0;
-
-               if (s->num != hnum) 
-                       continue;
-
-               if(s->dead && (s->state == TCP_CLOSE))
-                       continue;
-               /* local address matches? */
-               if (s->rcv_saddr) {
-                       if (s->rcv_saddr != laddr)
-                               continue;
-                       score++;
-               }
-               /* remote address matches? */
-               if (s->daddr) {
-                       if (s->daddr != raddr)
-                               continue;
-                       score++;
-               }
-               /* remote port matches? */
-               if (s->dummy_th.dest) {
-                       if (s->dummy_th.dest != rnum)
-                               continue;
-                       score++;
-               }
-               /* perfect match? */
-               if (score == 3)
-                       return s;
-               /* no, check if this is the best so far.. */
-               if (score <= badness)
-                       continue;
-               result = s;
-               badness = score;
-       }
-       return result;
-}
-
-
-/*
- *     Deliver a datagram to raw sockets.
- */
-struct sock *get_sock_raw(struct sock *sk, 
-                               unsigned short num,
-                               unsigned long raddr,
-                               unsigned long laddr)
-{
-       struct sock *s;
-
-       s=sk;
-
-       for(; s != NULL; s = s->next) 
-       {
-               if (s->num != num) 
-                       continue;
-               if(s->dead && (s->state == TCP_CLOSE))
-                       continue;
-               if(s->daddr && s->daddr!=raddr)
-                       continue;
-               if(s->rcv_saddr && s->rcv_saddr != laddr)
-                       continue;
-               return(s);
-       }
-       return(NULL);
-}
-
-/*
- *     Deliver a datagram to broadcast/multicast sockets.
- */
-struct sock *get_sock_mcast(struct sock *sk, 
-                               unsigned short num,
-                               unsigned long raddr,
-                               unsigned short rnum, unsigned long laddr)
-{
-       struct sock *s;
-       unsigned short hnum;
-
-       hnum = ntohs(num);
-
-       /*
-        * SOCK_ARRAY_SIZE must be a power of two.  This will work better
-        * than a prime unless 3 or more sockets end up using the same
-        * array entry.  This should not be a problem because most
-        * well known sockets don't overlap that much, and for
-        * the other ones, we can just be careful about picking our
-        * socket number when we choose an arbitrary one.
-        */
-       
-       s=sk;
-
-       for(; s != NULL; s = s->next) 
-       {
-               if (s->num != hnum) 
-                       continue;
-               if(s->dead && (s->state == TCP_CLOSE))
-                       continue;
-               if(s->daddr && s->daddr!=raddr)
-                       continue;
-               if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) 
-                       continue;
-               if(s->rcv_saddr  && s->rcv_saddr != laddr)
-                       continue;
-               return(s);
-       }
-       return(NULL);
-}
-
-
 struct proto_ops inet_stream_ops = {
        AF_INET,
 
@@ -1621,9 +1009,6 @@ struct proto_ops inet_dgram_ops = {
        inet_recvmsg
 };
 
-
-
-
 struct net_proto_family inet_family_ops = {
        AF_INET,
        inet_create
@@ -1681,8 +1066,6 @@ void inet_proto_init(struct net_proto *pro)
 {
        struct sk_buff *dummy_skb;
        struct inet_protocol *p;
-       int i;
-
 
        printk("Swansea University Computer Society TCP/IP for NET3.037\n");
 
@@ -1703,22 +1086,6 @@ void inet_proto_init(struct net_proto *pro)
        /*
         *      Add all the protocols. 
         */
-        
-       for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
-       {
-               tcp_sock_array[i] = NULL;
-               udp_sock_array[i] = NULL;
-               raw_sock_array[i] = NULL;
-       }
-       tcp_prot.inuse = 0;
-       tcp_prot.highestinuse = 0;
-       tcp_prot.sock_array = tcp_sock_array;
-       udp_prot.inuse = 0;
-       udp_prot.highestinuse = 0;
-       udp_prot.sock_array = udp_sock_array;
-       raw_prot.inuse = 0;
-       raw_prot.highestinuse = 0;
-       raw_prot.sock_array = raw_sock_array;
 
        printk("IP Protocols: ");
        for(p = inet_protocol_base; p != NULL;) 
index 9012e1e105d85f0594f765cf0b8ea4be467b0261..666302fc9376428940378f1a9a0eb0ebcc160cf3 100644 (file)
@@ -737,21 +737,17 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len)
        /* Deliver ICMP message to raw sockets. Pretty useless feature?
         */
 
-       hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
-       if ((raw_sk=raw_prot.sock_array[hash]) != NULL) {
-               raw_sk = get_sock_raw(raw_sk, iph->protocol, iph->saddr, iph->daddr);
+       /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
+       hash = iph->protocol & (MAX_INET_PROTOS - 1);
+       if ((raw_sk = raw_v4_htable[hash]) != NULL) {
+               raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr);
                while (raw_sk) {
                        raw_err(raw_sk, skb);
-                       raw_sk=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr);
+                       raw_sk = raw_v4_lookup(raw_sk->next, iph->protocol,
+                                              iph->saddr, iph->daddr);
                }
        }
 
-       /*
-        *      Get the protocol(s).
-        */
-        
-       hash = iph->protocol & (MAX_INET_PROTOS -1);
-
        /*
         *      This can't change while we are doing it. 
         *
@@ -981,6 +977,10 @@ static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, int len)
  *     in udp.c or tcp.c...
  */
 
+/* This should work with the new hashes now. -DaveM */
+extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
+extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
+
 int icmp_chkaddr(struct sk_buff *skb)
 {
        struct icmphdr *icmph=(struct icmphdr *)(skb->nh.raw + skb->nh.iph->ihl*4);
@@ -995,8 +995,7 @@ int icmp_chkaddr(struct sk_buff *skb)
                        {
                        struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-                       sk = get_sock(&tcp_prot, th->source, iph->daddr,
-                                               th->dest, iph->saddr);
+                       sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
                        if (!sk) return 0;
                        if (sk->saddr != iph->saddr) return 0;
                        if (sk->daddr != iph->daddr) return 0;
@@ -1010,8 +1009,7 @@ int icmp_chkaddr(struct sk_buff *skb)
                        {
                        struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-                       sk = get_sock(&udp_prot, uh->source, iph->daddr,
-                                               uh->dest, iph->saddr);
+                       sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
                        if (!sk) return 0;
                        if (sk->saddr != iph->saddr && __ip_chk_addr(iph->saddr) != IS_MYADDR)
                                return 0;
index cb70a973477596277ea2c5934959d4e220c6c941..d0e691ac1d51e49a9561fdef197783a74dd38f5e 100644 (file)
@@ -245,39 +245,34 @@ int ip_local_deliver(struct sk_buff *skb)
         *      RFC 1122: SHOULD pass TOS value up to the transport layer.
         */
  
-       hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
+       /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
+       hash = iph->protocol & (MAX_INET_PROTOS - 1);
 
        /* 
         *      If there maybe a raw socket we must check - if not we don't care less 
         */
                 
-       if((raw_sk=raw_prot.sock_array[hash])!=NULL)
-       {
-               struct sock *sknext=NULL;
+       if((raw_sk = raw_v4_htable[hash]) != NULL) {
+               struct sock *sknext = NULL;
                struct sk_buff *skb1;
-               raw_sk=get_sock_raw(raw_sk, iph->protocol,  iph->saddr, iph->daddr);
-               if(raw_sk)      /* Any raw sockets */
-               {
-                       do
-                       {
+               raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr);
+               if(raw_sk) {    /* Any raw sockets */
+                       do {
                                /* Find the next */
-                               sknext=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr);
+                               sknext = raw_v4_lookup(raw_sk->next, iph->protocol,
+                                                      iph->saddr, iph->daddr);
                                if(sknext)
-                                       skb1=skb_clone(skb, GFP_ATOMIC);
+                                       skb1 = skb_clone(skb, GFP_ATOMIC);
                                else
                                        break;  /* One pending raw socket left */
                                if(skb1)
                                        raw_rcv(raw_sk, skb1);
-                               raw_sk=sknext;
-                       }
-                       while(raw_sk!=NULL);
+                               raw_sk = sknext;
+                       } while(raw_sk!=NULL);
                                
-                       /*
-                        *      Here either raw_sk is the last raw socket, or NULL if none 
-                        */
-                       
-                       /*
-                        *      We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy 
+                       /*      Here either raw_sk is the last raw socket, or NULL if
+                        *      none.  We deliver to the last raw socket AFTER the
+                        *      protocol checks as it avoids a surplus copy.
                         */
                }
        }
@@ -286,7 +281,6 @@ int ip_local_deliver(struct sk_buff *skb)
         *      skb->h.raw now points at the protocol beyond the IP header.
         */
        
-       hash = iph->protocol & (MAX_INET_PROTOS -1);
        for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
        {
                struct sk_buff *skb2;
index c4a492a082806a673e9c91f0efbf455caa8d43a6..b05b10be7b7cd62c3c9823146f62258ec0891eff 100644 (file)
@@ -171,17 +171,17 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
 {
        int val=0,err;
        unsigned char ucval = 0;
-       int len;
 #if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
        struct ip_fw tmp_fw;
 #endif 
 
-       if(get_user(len, optval))
-               return -EFAULT;
-       if(len>=sizeof(int) && get_user(val, (int *) optval))
-               return -EFAULT;
-       if(len>=sizeof(char) && get_user(ucval, (unsigned char *) optval))
-               return -EFAULT;
+       if(optlen>=sizeof(int)) {
+               if(get_user(val, (int *) optval))
+                       return -EFAULT;
+       } else if(optlen>=sizeof(char)) {
+               if(get_user(ucval, (unsigned char *) optval))
+                       return -EFAULT;
+       }
        
        if(level!=SOL_IP)
                return -ENOPROTOOPT;
index 365a294664cf69bb21b24564aa4e4860b72644f0..b98b58f35bb70e70367303c4ec17104dd9039021 100644 (file)
@@ -491,27 +491,33 @@ int packet_recvmsg(struct sock *sk, struct msghdr *msg, int len,
  
 struct proto packet_prot = 
 {
-       packet_close,
-       NULL,
-       NULL,                   /* accept */
-       NULL,
-       NULL,
-       NULL,
-       datagram_poll,
-       NULL,                   /* No ioctl */
-       packet_init,
-       NULL,
-       NULL,
-       NULL,                   /* No set/get socket options */
-       NULL,
-       packet_sendmsg,         /* Sendmsg */
-       packet_recvmsg,         /* Recvmsg */
-       packet_bind,            /* Bind */
-       NULL,                   /* Backlog_rcv */
-       128,
-       0,
-       "PACKET",
-       0, 0
+       (struct sock *)&packet_prot,    /* sklist_next */
+       (struct sock *)&packet_prot,    /* sklist_prev */
+       packet_close,                   /* close */
+       NULL,                           /* connect */
+       NULL,                           /* accept */
+       NULL,                           /* retransmit */
+       NULL,                           /* write_wakeup */
+       NULL,                           /* read_wakeup */
+       datagram_poll,                  /* poll */
+       NULL,                           /* ioctl */
+       packet_init,                    /* init */
+       NULL,                           /* destroy */
+       NULL,                           /* shutdown */
+       NULL,                           /* setsockopt */
+       NULL,                           /* getsockopt */
+       packet_sendmsg,                 /* Sendmsg */
+       packet_recvmsg,                 /* Recvmsg */
+       packet_bind,                    /* bind */
+       NULL,                           /* backlog_rcv */
+       NULL,                           /* hash */
+       NULL,                           /* unhash */
+       NULL,                           /* rehash */
+       NULL,                           /* good_socknum */
+       NULL,                           /* verify_bind */
+       128,                            /* max_header */
+       0,                              /* retransmits */
+       "PACKET",                       /* name */
+       0,                              /* inuse */
+       0                               /* highestinuse */
 };
-
-       
index c5721e5655b18d5d08f50799ee19eb45d79cbfba..9bf813ee00d16d329b67b1bd63801077a4b1be1c 100644 (file)
 static int
 get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t offset, int length)
 {
-       struct sock **s_array;
        struct sock *sp;
        struct tcp_opt *tp;
-       int i;
-       int timer_active;
-       int timer_active1;
-       int timer_active2;
+       int timer_active, timer_active1, timer_active2;
        unsigned long timer_expires;
        unsigned long  dest, src;
        unsigned short destp, srcp;
-       int len=0;
+       int len=0, i = 0;
        off_t pos=0;
        off_t begin;
        char tmpbuf[129];
   
-       s_array = pro->sock_array;
        if (offset < 128) 
                len += sprintf(buffer, "%-127s\n",
                               "  sl  local_address rem_address   st tx_queue "
@@ -85,72 +80,56 @@ get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t of
  *     concede defeat and cli().
  */
        start_bh_atomic();
+       sp = pro->sklist_next;
+       while(sp != (struct sock *)pro) {
+               pos += 128;
+               if (pos < offset)
+                       goto next;
 
-       for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
-       {
-               sp = s_array[i];
-
-               while(sp != NULL) 
-               {
-                       pos += 128;
-                       if (pos < offset)
-                       {
-                               sp = sp->next;
-                               continue;
-                       }
-
-                       tp = &(sp->tp_pinfo.af_tcp);
-
-                       dest  = sp->daddr;
-                       src   = sp->saddr;
-                       destp = sp->dummy_th.dest;
-                       srcp  = sp->dummy_th.source;
+               tp = &(sp->tp_pinfo.af_tcp);
+               dest  = sp->daddr;
+               src   = sp->saddr;
+               destp = sp->dummy_th.dest;
+               srcp  = sp->dummy_th.source;
 
-                       /* Since we are Little Endian we need to swap the bytes :-( */
-                       destp = ntohs(destp);
-                       srcp  = ntohs(srcp);
-                       timer_active1 = del_timer(&sp->retransmit_timer);
-                       timer_active2 = del_timer(&sp->timer);
-                       if (!timer_active1) sp->retransmit_timer.expires=0;
-                       if (!timer_active2) sp->timer.expires=0;
-                       timer_active=0;
-                       timer_expires=(unsigned)-1;
-                       if (timer_active1 &&
-                         sp->retransmit_timer.expires < timer_expires) {
-                           timer_active=timer_active1;
-                           timer_expires=sp->retransmit_timer.expires;
-                       }
-                       if (timer_active2 &&
-                         sp->timer.expires < timer_expires) {
-                           timer_active=timer_active2;
-                           timer_expires=sp->timer.expires;
-                       }
-                       sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
-                               " %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
-                               i, src, srcp, dest, destp, sp->state, 
-                               format==0?sp->write_seq-tp->snd_una:sp->wmem_alloc, 
-                               format==0?tp->rcv_nxt-sp->copied_seq:sp->rmem_alloc,
-                               timer_active, timer_expires-jiffies, (unsigned) sp->retransmits,
-                               sp->socket ? sp->socket->inode->i_uid:0,
-                               timer_active?sp->timeout:0,
-                               sp->socket ? sp->socket->inode->i_ino:0);
-
-                       if (timer_active1) add_timer(&sp->retransmit_timer);
-                       if (timer_active2) add_timer(&sp->timer);
-                       len += sprintf(buffer+len, "%-127s\n", tmpbuf);
-                       /*
-                        * All sockets with (port mod SOCK_ARRAY_SIZE) = i
-                        * are kept in sock_array[i], so we must follow the
-                        * 'next' link to get them all.
-                        */
-                       if(len >= length)
-                               break;
-                       sp = sp->next;
+               /* Since we are Little Endian we need to swap the bytes :-( */
+               destp = ntohs(destp);
+               srcp  = ntohs(srcp);
+               timer_active1 = del_timer(&sp->retransmit_timer);
+               timer_active2 = del_timer(&sp->timer);
+               if (!timer_active1) sp->retransmit_timer.expires=0;
+               if (!timer_active2) sp->timer.expires=0;
+               timer_active=0;
+               timer_expires=(unsigned)-1;
+               if (timer_active1 && sp->retransmit_timer.expires < timer_expires) {
+                       timer_active=timer_active1;
+                       timer_expires=sp->retransmit_timer.expires;
+               }
+               if (timer_active2 && sp->timer.expires < timer_expires) {
+                       timer_active=timer_active2;
+                       timer_expires=sp->timer.expires;
                }
-               if(len>= length)
+               sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
+                       " %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
+                       i, src, srcp, dest, destp, sp->state, 
+                       format==0?sp->write_seq-tp->snd_una:sp->wmem_alloc, 
+                       format==0?tp->rcv_nxt-sp->copied_seq:sp->rmem_alloc,
+                       timer_active, timer_expires-jiffies, (unsigned) sp->retransmits,
+                       sp->socket ? sp->socket->inode->i_uid:0,
+                       timer_active?sp->timeout:0,
+                       sp->socket ? sp->socket->inode->i_ino:0);
+
+               if (timer_active1) add_timer(&sp->retransmit_timer);
+               if (timer_active2) add_timer(&sp->timer);
+               len += sprintf(buffer+len, "%-127s\n", tmpbuf);
+               if(len >= length)
                        break;
+       next:
+               sp = sp->sklist_next;
+               i++;
        }
        end_bh_atomic();
+
        begin = len - (pos - offset);
        *start = buffer + begin;
        len -= begin;
@@ -189,7 +168,6 @@ int afinet_get_info(char *buffer, char **start, off_t offset, int length, int du
 
        int len  = socket_get_info(buffer,start,offset,length);
 
-       len += sprintf(buffer+len,"SOCK_ARRAY_SIZE=%d\n",SOCK_ARRAY_SIZE);
        len += sprintf(buffer+len,"TCP: inuse %d highest %d\n",
                       tcp_prot.inuse, tcp_prot.highestinuse);
        len += sprintf(buffer+len,"UDP: inuse %d highest %d\n",
index 279778f15f2e3fa1fc575e61c540debc2853f17e..ca781d6b9ce0efca70fff9b3c99467c6606ff031 100644 (file)
@@ -30,6 +30,7 @@
  *             Alan Cox        :       Beginnings of mrouted support.
  *             Alan Cox        :       Added IP_HDRINCL option.
  *             Alan Cox        :       Skip broadcast check if BSDism set.
+ *             David S. Miller :       New socket lookup architecture.
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
 #include <net/sock.h>
 #include <net/icmp.h>
 #include <net/udp.h>
+#include <net/raw.h>
 #include <net/checksum.h>
 
 #ifdef CONFIG_IP_MROUTE
 struct sock *mroute_socket=NULL;
 #endif
 
+struct sock *raw_v4_htable[RAWV4_HTABLE_SIZE];
+
+static void raw_v4_hash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+
+       num &= (RAWV4_HTABLE_SIZE - 1);
+       skp = &raw_v4_htable[num];
+       SOCKHASH_LOCK();
+       sk->next = *skp;
+       *skp = sk;
+       sk->hashent = num;
+       SOCKHASH_UNLOCK();
+}
+
+static void raw_v4_unhash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+
+       num &= (RAWV4_HTABLE_SIZE - 1);
+       skp = &raw_v4_htable[num];
+
+       SOCKHASH_LOCK();
+       while(*skp != NULL) {
+               if(*skp == sk) {
+                       *skp = sk->next;
+                       break;
+               }
+               skp = &((*skp)->next);
+       }
+       SOCKHASH_UNLOCK();
+}
+
+static void raw_v4_rehash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+       int oldnum = sk->hashent;
+
+       num &= (RAWV4_HTABLE_SIZE - 1);
+       skp = &raw_v4_htable[oldnum];
+
+       SOCKHASH_LOCK();
+       while(*skp != NULL) {
+               if(*skp == sk) {
+                       *skp = sk->next;
+                       break;
+               }
+               skp = &((*skp)->next);
+       }
+       sk->next = raw_v4_htable[num];
+       raw_v4_htable[num] = sk;
+       sk->hashent = num;
+       SOCKHASH_UNLOCK();
+}
+
+/* Grumble... icmp and ip_input want to get at this... */
+struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
+                          unsigned long raddr, unsigned long laddr)
+{
+       struct sock *s = sk;
+
+       SOCKHASH_LOCK();
+       for(s = sk; s; s = s->next) {
+               if((s->num == num)                              &&
+                  !(s->dead && (s->state == TCP_CLOSE))        &&
+                  !(s->daddr && s->daddr != raddr)             &&
+                  !(s->rcv_saddr && s->rcv_saddr != laddr))
+                       break; /* gotcha */
+       }
+       SOCKHASH_UNLOCK();
+       return s;
+}
+
 /*
  *     Raw_err does not currently get called by the icmp module - FIXME:
  */
@@ -339,13 +417,31 @@ static void raw_close(struct sock *sk, unsigned long timeout)
        destroy_sock(sk);
 }
 
-
-static int raw_init(struct sock *sk)
+/* This gets rid of all the nasties in af_inet. -DaveM */
+static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
-       return(0);
+       struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
+       int chk_addr_ret;
+
+       if((sk->state != TCP_CLOSE) || (addr_len < sizeof(struct sockaddr_in)))
+               return -EINVAL;
+       chk_addr_ret = __ip_chk_addr(addr->sin_addr.s_addr);
+       if(addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR &&
+          chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST) {
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+               /* Superuser may bind to any address to allow transparent proxying. */
+               if(!suser())
+#endif
+                       return -EADDRNOTAVAIL;
+       }
+       sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
+       if(chk_addr_ret == IS_MULTICAST || chk_addr_ret == IS_BROADCAST)
+               sk->saddr = 0;  /* Use device */
+       dst_release(sk->dst_cache);
+       sk->dst_cache = NULL;
+       return 0;
 }
 
-
 /*
  *     This should be easy, if there is something there
  *     we return it, otherwise we block.
@@ -406,30 +502,37 @@ int raw_recvmsg(struct sock *sk, struct msghdr *msg, int len,
 
 
 struct proto raw_prot = {
-       raw_close,
-       udp_connect,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       datagram_poll,
-#ifdef CONFIG_IP_MROUTE        
-       ipmr_ioctl,
+       (struct sock *)&raw_prot,       /* sklist_next */
+       (struct sock *)&raw_prot,       /* sklist_prev */
+       raw_close,                      /* close */
+       udp_connect,                    /* connect */
+       NULL,                           /* accept */
+       NULL,                           /* retransmit */
+       NULL,                           /* write_wakeup */
+       NULL,                           /* read_wakeup */
+       datagram_poll,                  /* poll */
+#ifdef CONFIG_IP_MROUTE
+       ipmr_ioctl,                     /* ioctl */
 #else
-       NULL,
-#endif         
-       raw_init,
-       NULL,
-       NULL,
-       ip_setsockopt,
-       ip_getsockopt,
-       raw_sendmsg,
-       raw_recvmsg,
-       NULL,           /* No special bind */
-       raw_rcv_skb,
-       128,
-       0,
-       "RAW",
-       0, 0,
-       NULL
+       NULL,                           /* ioctl */
+#endif
+       NULL,                           /* init */
+       NULL,                           /* destroy */
+       NULL,                           /* shutdown */
+       ip_setsockopt,                  /* setsockopt */
+       ip_getsockopt,                  /* getsockopt */
+       raw_sendmsg,                    /* sendmsg */
+       raw_recvmsg,                    /* recvmsg */
+       raw_bind,                       /* bind */
+       raw_rcv_skb,                    /* backlog_rcv */
+       raw_v4_hash,                    /* hash */
+       raw_v4_unhash,                  /* unhash */
+       raw_v4_rehash,                  /* rehash */
+       NULL,                           /* good_socknum */
+       NULL,                           /* verify_bind */
+       128,                            /* max_header */
+       0,                              /* retransmits */
+       "RAW",                          /* name */
+       0,                              /* inuse */
+       0                               /* highestinuse */
 };
index bc44c99f9c6bb1674fd3fffb0093027b56ff5b33..5b263ab67f05420377c0d758cc846be7cdb532f7 100644 (file)
@@ -817,6 +817,7 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov,
                
                if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV)
                {
+                       printk("tcp_do_sendmsg1: EPIPE dude...\n");
                        if (sk->keepopen)
                                send_sig(SIGPIPE, current, 0);
                        return -EPIPE;
@@ -869,6 +870,7 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov,
                        {
                                if (copied)
                                        return copied;
+                               printk("tcp_do_sendmsg2: SEND_SHUTDOWN, EPIPE...\n");
                                send_sig(SIGPIPE,current,0);
                                return -EPIPE;
                        }
@@ -1613,7 +1615,6 @@ void tcp_close(struct sock *sk, unsigned long timeout)
 
        lock_sock(sk);
 
-       tcp_cache_zap();
        if(sk->state == TCP_LISTEN)
        {
                /* Special case */
@@ -1621,6 +1622,7 @@ void tcp_close(struct sock *sk, unsigned long timeout)
                tcp_close_pending(sk);
                release_sock(sk);
                sk->dead = 1;
+               sk->prot->unhash(sk);
                return;
        }
 
@@ -1667,12 +1669,6 @@ void tcp_close(struct sock *sk, unsigned long timeout)
                sti();
        }
 
-       /*
-        * This will destroy it. The timers will take care of actually
-        * free'ing up the memory.
-        */
-       tcp_cache_zap();        /* Kill the cache again. */
-
        /* Now that the socket is dead, if we are in the FIN_WAIT2 state
         * we may need to set up a timer.
          */
@@ -1687,6 +1683,9 @@ void tcp_close(struct sock *sk, unsigned long timeout)
 
        release_sock(sk);
        sk->dead = 1;
+
+       if(sk->state == TCP_CLOSE)
+               sk->prot->unhash(sk);
 }
 
 
index 093522d631e7bd604d21b2d472feac262bfb2578..b868605cd33fca1528378bcb9b91bf1dc34bdf04 100644 (file)
  *             Jorge Cwik, <jorge@laser.satlink.net>
  */
 
-/*
- *     TODO
- *             - A better sock cache
- *
- */
-
 /*
  * Changes:
  *             Pedro Roque     :       Fast Retransmit/Recovery.
@@ -1333,7 +1327,6 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
 
        if(th->syn && skb->seq != sk->syn_seq)
        {
-               printk(KERN_DEBUG "syn in established state\n");
                tcp_reset(sk, skb);
                kfree_skb(skb, FREE_READ);
                return 1;
@@ -1409,7 +1402,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
 
        /*
         *      state == CLOSED
-        *      tested in tcp_v{4,6}_rcv
+        *      Hash lookup always fails, so no worries. -DaveM 
         */
 
        switch (sk->state) {
index ad298ad0d641623ddb341d759ce11bcfa04a6123..29b7e7ae6458e176ebf5a715668d62887f0ac31b 100644 (file)
  *      2 of the License, or (at your option) any later version.
  */
 
+/*
+ * Changes:
+ *             David S. Miller :       New socket lookup architecture.
+ *                                     This code is dedicated to John Dyson.
+ */
+
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
@@ -38,44 +44,360 @@ static void tcp_v4_send_reset(struct sk_buff *skb);
 void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, 
                       struct sk_buff *skb);
 
-/*
- *     Cached last hit socket
+/* This is for sockets with full identity only.  Sockets here will always
+ * be without wildcards and will have the following invariant:
+ *          TCP_ESTABLISHED <= sk->state < TCP_CLOSE
  */
-static volatile unsigned long  th_cache_saddr, th_cache_daddr;
-static volatile unsigned short  th_cache_dport, th_cache_sport;
-static volatile struct sock    *th_cache_sk;
+struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
+
+/* All sockets in TCP_LISTEN state will be in here.  This is the only table
+ * where wildcard'd TCP sockets can exist.  Hash function here is just local
+ * port number.  XXX Fix or we'll lose with thousands of IP aliases...
+ */
+struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
 
-void tcp_cache_zap(void)
+/* Ok, let's try this, I give up, we do need a local binding
+ * TCP hash as well as the others for fast bind/connect.
+ */
+struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE];
+
+static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport,
+                                __u32 faddr, __u16 fport)
 {
-       th_cache_sk=NULL;
+       return ((laddr ^ lport) ^ (faddr ^ fport)) & (TCP_HTABLE_SIZE - 1);
 }
 
-/*
- *     Find the socket, using the last hit cache if applicable.
- *     The cache is not quite right...
+static __inline__ int tcp_sk_hashfn(struct sock *sk)
+{
+       __u32 laddr = sk->rcv_saddr;
+       __u16 lport = sk->num;
+       __u32 faddr = sk->daddr;
+       __u16 fport = sk->dummy_th.dest;
+
+       return tcp_hashfn(laddr, lport, faddr, fport);
+}
+
+static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum)
+{
+       struct sock *sk2;
+       int retval = 0, sk_reuse = sk->reuse;
+
+       SOCKHASH_LOCK();
+       sk2 = tcp_bound_hash[tcp_bhashfn(snum)];
+       for(; sk2 != NULL; sk2 = sk2->prev) {
+               if((sk2->num == snum) && (sk2 != sk)) {
+                       unsigned char state = sk2->state;
+                       int sk2_reuse = sk2->reuse;
+
+                       if(!sk2->rcv_saddr || !sk->rcv_saddr) {
+                               if((!sk2_reuse)                 ||
+                                  (!sk_reuse)                  ||
+                                  (state != TCP_LISTEN)) {
+                                       retval = 1;
+                                       break;
+                               }
+                       } else if(sk2->rcv_saddr == sk->rcv_saddr) {
+                               if((!sk_reuse)                  ||
+                                  (!sk2_reuse)                 ||
+                                  (state == TCP_LISTEN)) {
+                                       retval = 1;
+                                       break;
+                               }
+                       }
+               }
+       }
+       SOCKHASH_UNLOCK();
+
+       return retval;
+}
+
+static __inline__ int tcp_lport_inuse(int num)
+{
+       struct sock *sk = tcp_bound_hash[tcp_bhashfn(num)];
+
+       for(; sk != NULL; sk = sk->prev) {
+               if(sk->num == num)
+                       return 1;
+       }
+       return 0;
+}
+
+/* Find a "good" local port, this is family independant.
+ * There are several strategies working in unison here to
+ * get the best possible performance.  The current socket
+ * load is kept track of, if it is zero there is a strong
+ * likely hood that there is a zero length chain we will
+ * find with a small amount of searching, else the load is
+ * what we shoot for for when the chains all have at least
+ * one entry.  The base helps us walk the chains in an
+ * order such that a good chain is found as quickly as possible.  -DaveM
  */
+unsigned short tcp_good_socknum(void)
+{
+       static int start = PROT_SOCK;
+       static int binding_contour = 0;
+       int best = 0;
+       int size = 32767; /* a big num. */
+       int retval = 0, i, end, bc;
+
+       SOCKHASH_LOCK();
+       i = tcp_bhashfn(start);
+       end = i + TCP_BHTABLE_SIZE;
+       bc = binding_contour;
+       do {
+               struct sock *sk = tcp_bound_hash[tcp_bhashfn(i)];
+               if(!sk) {
+                       retval = (start + i);
+                       start  = (retval + 1);
+
+                       /* Check for decreasing load. */
+                       if(bc != 0)
+                               binding_contour = 0;
+                       goto done;
+               } else {
+                       int j = 0;
+                       do { sk = sk->prev; } while(++j < size && sk);
+                       if(j < size) {
+                               best = (start + i);
+                               size = j;
+                               if(bc && size <= bc) {
+                                       start = best + 1;
+                                       goto verify;
+                               }
+                       }
+               }
+       } while(++i != end);
+
+       /* Socket load is increasing, adjust our load average. */
+       binding_contour = size;
+verify:
+       if(size < binding_contour)
+               binding_contour = size;
+
+       if(best > 32767)
+               best -= (32768 - PROT_SOCK);
+
+       while(tcp_lport_inuse(best))
+               best += TCP_BHTABLE_SIZE;
+       retval = best;
+done:
+       if(start > 32767)
+               start -= (32768 - PROT_SOCK);
+
+       SOCKHASH_UNLOCK();
 
-static inline struct sock * get_tcp_sock(u32 saddr, u16 sport,
-                                        u32 daddr, u16 dport)
+       return retval;
+}
+
+static void tcp_v4_hash(struct sock *sk)
 {
-       struct sock * sk;
+       unsigned char state;
+
+       SOCKHASH_LOCK();
+       state = sk->state;
+       if(state != TCP_CLOSE || !sk->dead) {
+               struct sock **htable;
+
+               if(state == TCP_LISTEN) {
+                       sk->hashent = tcp_sk_listen_hashfn(sk);
+                       htable = &tcp_listening_hash[0];
+               } else {
+                       sk->hashent = tcp_sk_hashfn(sk);
+                       htable = &tcp_established_hash[0];
+               }
+               sk->next = htable[sk->hashent];
+               htable[sk->hashent] = sk;
+               sk->hashtable = htable;
+               tcp_sk_bindify(sk);
+       }
+       SOCKHASH_UNLOCK();
+}
 
-       sk = (struct sock *) th_cache_sk;
-       if (!sk || saddr != th_cache_saddr || daddr != th_cache_daddr ||
-           sport != th_cache_sport || dport != th_cache_dport) {
-               sk = get_sock(&tcp_prot, dport, saddr, sport, daddr);
-               if (sk) {
-                       th_cache_saddr=saddr;
-                       th_cache_daddr=daddr;
-                       th_cache_dport=dport;
-                       th_cache_sport=sport;
-                       th_cache_sk=sk;
+static void tcp_v4_unhash(struct sock *sk)
+{
+       struct sock **htable;
+
+       SOCKHASH_LOCK();
+       htable = sk->hashtable;
+       if(htable) {
+               struct sock **skp = &(htable[sk->hashent]);
+               while(*skp != NULL) {
+                       if(*skp == sk) {
+                               *skp = sk->next;
+                               break;
+                       }
+                       skp = &((*skp)->next);
+               }
+               sk->hashtable = NULL;
+       }
+       if(sk->state == TCP_CLOSE && sk->dead)
+               tcp_sk_unbindify(sk);
+       SOCKHASH_UNLOCK();
+}
+
+static void tcp_v4_rehash(struct sock *sk)
+{
+       struct sock **htable;
+       unsigned char state;
+
+       SOCKHASH_LOCK();
+       htable = &(sk->hashtable[sk->hashent]);
+       state = sk->state;
+       if(htable) {
+               while(*htable != NULL) {
+                       if(*htable == sk) {
+                               *htable = sk->next;
+                               break;
+                       }
+                       htable = &((*htable)->next);
                }
        }
+       tcp_sk_unbindify(sk);
+       htable = NULL;
+       if(state != TCP_CLOSE || !sk->dead) {
+               if(state == TCP_LISTEN) {
+                       sk->hashent = tcp_sk_listen_hashfn(sk);
+                       htable = &tcp_listening_hash[0];
+               } else {
+                       sk->hashent = tcp_sk_hashfn(sk);
+                       htable = &tcp_established_hash[0];
+               }
+               sk->next = htable[sk->hashent];
+               htable[sk->hashent] = sk;
+               tcp_sk_bindify(sk);
+       }
+       sk->hashtable = htable;
+       SOCKHASH_UNLOCK();
+}
+
+/* Don't inline this cruft.  Here are some nice properties to
+ * exploit here.  The BSD API does not allow a listening TCP
+ * to specify the remote port nor the remote address for the
+ * connection.  So always assume those are both wildcarded
+ * during the search since they can never be otherwise.
+ *
+ * XXX Later on, hash on both local port _and_ local address,
+ * XXX to handle a huge IP alias'd box.  Keep in mind that
+ * XXX such a scheme will require us to run through the listener
+ * XXX hash twice, once for local addresses bound, and once for
+ * XXX the local address wildcarded (because the hash is different).
+ */
+static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum)
+{
+       struct sock *sk = tcp_listening_hash[tcp_lhashfn(hnum)];
+       struct sock *result = NULL;
+
+       for(; sk; sk = sk->next) {
+               if(sk->num == hnum) {
+                       __u32 rcv_saddr = sk->rcv_saddr;
+
+                       if(rcv_saddr) {
+                               if(rcv_saddr == daddr)
+                                       return sk; /* Best possible match. */
+                       } else if(!result)
+                               result = sk;
+               }
+       }
+       return result;
+}
+
+/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
+ * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ */
+static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
+                                          u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+       unsigned short hnum = ntohs(dport);
+       struct sock *sk;
+
+       /* Optimize here for direct hit, only listening connections can
+        * have wildcards anyways.  It is assumed that this code only
+        * gets called from within NET_BH.
+        */
+       sk = tcp_established_hash[tcp_hashfn(daddr, hnum, saddr, sport)];
+       for(; sk; sk = sk->next)
+               if(sk->daddr            == saddr                && /* remote address */
+                  sk->dummy_th.dest    == sport                && /* remote port    */
+                  sk->num              == hnum                 && /* local port     */
+                  sk->rcv_saddr        == daddr)                  /* local address  */
+                       goto hit; /* You sunk my battleship! */
+       sk = tcp_v4_lookup_longway(daddr, hnum);
+hit:
        return sk;
 }
 
+__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+       return __tcp_v4_lookup(0, saddr, sport, daddr, dport);
+}
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+#define secondlist(hpnum, sk, fpass) \
+({ struct sock *s1; if(!(sk) && (fpass)--) \
+       s1 = tcp_bound_hash[tcp_bhashfn(hpnum)]; \
+   else \
+       s1 = (sk); \
+   s1; \
+})
+
+#define tcp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \
+       secondlist((hpnum), tcp_bound_hash[tcp_bhashfn(hnum)],(fpass))
+
+#define tcp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
+       secondlist((hpnum),(sk)->next,(fpass))
+
+struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+                                unsigned short rnum, unsigned long laddr,
+                                unsigned long paddr, unsigned short pnum)
+{
+       struct sock *s, *result = NULL;
+       int badness = -1;
+       unsigned short hnum = ntohs(num);
+       unsigned short hpnum = ntohs(pnum);
+       int firstpass = 1;
+
+       /* This code must run only from NET_BH. */
+       for(s = tcp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
+           s != NULL;
+           s = tcp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
+               if(s->num == hnum || s->num == hpnum) {
+                       int score = 0;
+                       if(s->dead && (s->state == TCP_CLOSE))
+                               continue;
+                       if(s->rcv_saddr) {
+                               if((s->num != hpnum || s->rcv_saddr != paddr) &&
+                                  (s->num != hnum || s->rcv_saddr != laddr))
+                                       continue;
+                               score++;
+                       }
+                       if(s->daddr) {
+                               if(s->daddr != raddr)
+                                       continue;
+                               score++;
+                       }
+                       if(s->dummy_th.dest) {
+                               if(s->dummy_th.dest != rnum)
+                                       continue;
+                               score++;
+                       }
+                       if(score == 3 && s->num == hnum) {
+                               result = s;
+                               break;
+                       } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+                                       result = s;
+                                       badness = score;
+                       }
+               }
+       }
+       return result;
+}
+
+#undef secondlist
+#undef tcp_v4_proxy_loop_init
+#undef tcp_v4_proxy_loop_next
+
+#endif
+
 static __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
 {
        return secure_tcp_sequence_number(sk->saddr, sk->daddr,
@@ -94,27 +416,24 @@ static __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
 
 static int tcp_unique_address(u32 saddr, u16 snum, u32 daddr, u16 dnum)
 {
-       int retval = 1;
+       int retval = 1, hashent = tcp_hashfn(saddr, snum, daddr, dnum);
        struct sock * sk;
 
-       /* Make sure we are allowed to connect here. */
-       cli();
-       for (sk = tcp_prot.sock_array[snum & (SOCK_ARRAY_SIZE -1)];
-                       sk != NULL; sk = sk->next)
-       {
-               /* hash collision? */
-               if (sk->num != snum)
-                       continue;
-               if (sk->saddr != saddr)
-                       continue;
-               if (sk->daddr != daddr)
-                       continue;
-               if (sk->dummy_th.dest != dnum)
-                       continue;
-               retval = 0;
-               break;
+       /* Make sure we are allowed to connect here.
+        * But freeze the hash while we snoop around.
+        */
+       SOCKHASH_LOCK();
+       sk = tcp_established_hash[hashent];
+       for (; sk != NULL; sk = sk->next) {
+               if(sk->daddr            == daddr                && /* remote address */
+                  sk->dummy_th.dest    == dnum                 && /* remote port */
+                  sk->num              == snum                 && /* local port */
+                  sk->saddr            == saddr) {                /* local address */
+                       retval = 0;
+                       break;
+               }
        }
-       sti();
+       SOCKHASH_UNLOCK();
        return retval;
 }
 
@@ -184,6 +503,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
                sk->priority = rt->u.dst.priority;
 
        sk->dummy_th.dest = usin->sin_port;
+
        sk->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
                                                   sk->dummy_th.source,
                                                   usin->sin_port);
@@ -262,14 +582,13 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        buff->csum = csum_partial(ptr, 4, 0);
        tcp_v4_send_check(sk, t1, sizeof(struct tcphdr) + 4, buff);
 
-       /*
-        *      This must go first otherwise a really quick response 
-        *      will get reset.
-        */
-
-       tcp_cache_zap();
        tcp_set_state(sk,TCP_SYN_SENT);
 
+       /* Socket identity change complete, no longer
+        * in TCP_CLOSE, so rehash.
+        */
+       tcp_v4_rehash(sk);
+
        tp->rto = rt->u.dst.rtt;
 
        tcp_init_xmit_timers(sk);
@@ -348,7 +667,7 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp)
        int code = skb->h.icmph->code;
        struct sock *sk;
 
-       sk = get_sock(&tcp_prot, th->source, iph->daddr, th->dest, iph->saddr);
+       sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
 
        if (sk == NULL)
                return;
@@ -402,39 +721,13 @@ void tcp_v4_err(struct sk_buff *skb, unsigned char *dp)
        }
 }
 
-/*
- *     This routine computes a TCP checksum.
- *
- *     Modified January 1995 from a go-faster DOS routine by
- *     Jorge Cwik <jorge@laser.satlink.net>
- */
+/* This routine computes an IPv4 TCP checksum. */
 void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, 
                       struct sk_buff *skb)
 {
-       __u32 saddr = sk->saddr;
-       __u32 daddr = sk->daddr;
-#ifdef DEBUG_TCP_CHECK
-       u16 check;
-#endif
-       th->check = 0;
-       th->check = tcp_v4_check(th, len, saddr, daddr,
-                                csum_partial((char *)th, sizeof(*th), 
-                                             skb->csum));
-
-#ifdef DEBUG_TCP_CHECK
-       check = th->check;
        th->check = 0;
-       th->check = tcp_v4_check(th, len, saddr, daddr,
-               csum_partial((char *)th,len,0));
-       if (check != th->check) {
-               static int count = 0;
-               if (++count < 10) {
-                       printk("Checksum %x (%x) from %p\n", th->check, check,
-                              __builtin_return_address(0));
-                       printk("TCP=<off:%d a:%d s:%d f:%d> len=%d\n", th->doff*4, th->ack, th->syn, th->fin, len);
-               }
-       }
-#endif
+       th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
+                                csum_partial((char *)th, sizeof(*th), skb->csum));
 }
 
 /*
@@ -504,7 +797,7 @@ int tcp_chkaddr(struct sk_buff *skb)
        struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4);
        struct sock *sk;
 
-       sk = get_sock(&tcp_prot, th->dest, iph->saddr, th->source, iph->daddr);
+       sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
 
        if (!sk)
                return 0;
@@ -717,6 +1010,10 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        }
 
        memcpy(newsk, sk, sizeof(*newsk));
+
+       /* Or else we die! -DaveM */
+       newsk->sklist_next = NULL;
+
        newsk->opt = NULL;
        newsk->dst_cache  = NULL;
        skb_queue_head_init(&newsk->write_queue);
@@ -833,10 +1130,8 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        
        newsk->mss = min(req->mss, snd_mss);
        
-       inet_put_sock(newsk->num, newsk);
-
-       tcp_cache_zap();
-
+       tcp_v4_hash(newsk);
+       add_to_prot_sklist(newsk);
        return newsk;
 }
 
@@ -1009,11 +1304,11 @@ int tcp_v4_rcv(struct sk_buff *skb, unsigned short len)
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
        if (IPCB(skb)->redirport)
-               sk = get_sock_proxy(&tcp_prot, th->dest, saddr, th->source, daddr, skb->dev->pa_addr, IPCB(skb)->redirport);
+               sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr,
+                                        skb->dev->pa_addr, IPCB(skb)->redirport);
        else
 #endif
-       sk = get_tcp_sock(saddr, th->source, daddr, th->dest);
-
+       sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest);
        if (!sk)
                goto no_tcp_socket;
 
@@ -1084,9 +1379,8 @@ int tcp_v4_rebuild_header(struct sock *sk, struct sk_buff *skb)
 
 static struct sock * tcp_v4_get_sock(struct sk_buff *skb, struct tcphdr *th)
 {
-       struct sock *sk;
-       sk = get_tcp_sock(skb->nh.iph->saddr, th->source, skb->nh.iph->daddr, th->dest);
-       return sk;
+       return tcp_v4_lookup(skb->nh.iph->saddr, th->source,
+                            skb->nh.iph->daddr, th->dest);
 }
 
 static void v4_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
@@ -1197,26 +1491,33 @@ static int tcp_v4_destroy_sock(struct sock *sk)
 }
 
 struct proto tcp_prot = {
-       tcp_close,
-       tcp_v4_connect,
-       tcp_accept,
-       NULL,
-       tcp_write_wakeup,
-       tcp_read_wakeup,
-       tcp_poll,
-       tcp_ioctl,
-       tcp_v4_init_sock,
-       tcp_v4_destroy_sock,
-       tcp_shutdown,
-       tcp_setsockopt,
-       tcp_getsockopt,
-       tcp_v4_sendmsg,
-       tcp_recvmsg,
-       NULL,           /* No special bind()    */
-       tcp_v4_backlog_rcv,
-       128,
-       0,
-       "TCP",
-       0, 0,
-       NULL
+       (struct sock *)&tcp_prot,       /* sklist_next */
+       (struct sock *)&tcp_prot,       /* sklist_prev */
+       tcp_close,                      /* close */
+       tcp_v4_connect,                 /* connect */
+       tcp_accept,                     /* accept */
+       NULL,                           /* retransmit */
+       tcp_write_wakeup,               /* write_wakeup */
+       tcp_read_wakeup,                /* read_wakeup */
+       tcp_poll,                       /* poll */
+       tcp_ioctl,                      /* ioctl */
+       tcp_v4_init_sock,               /* init */
+       tcp_v4_destroy_sock,            /* destroy */
+       tcp_shutdown,                   /* shutdown */
+       tcp_setsockopt,                 /* setsockopt */
+       tcp_getsockopt,                 /* getsockopt */
+       tcp_v4_sendmsg,                 /* sendmsg */
+       tcp_recvmsg,                    /* recvmsg */
+       NULL,                           /* bind */
+       tcp_v4_backlog_rcv,             /* backlog_rcv */
+       tcp_v4_hash,                    /* hash */
+       tcp_v4_unhash,                  /* unhash */
+       tcp_v4_rehash,                  /* rehash */
+       tcp_good_socknum,               /* good_socknum */
+       tcp_v4_verify_bind,             /* verify_bind */
+       128,                            /* max_header */
+       0,                              /* retransmits */
+       "TCP",                          /* name */
+       0,                              /* inuse */
+       0                               /* highestinuse */
 };
index 05cca9f49c7940a12836ded507291a05b8973215..b0c2a314670139c6c18a110c1899b0fc3621ec3c 100644 (file)
@@ -27,6 +27,8 @@
  *                             :       AF independence
  *
  *             Linus Torvalds  :       send_delayed_ack
+ *             David S. Miller :       Charge memory using the right skb
+ *                                     during syn/ack processing.
  *
  */
 
@@ -905,7 +907,7 @@ int tcp_send_synack(struct sock *sk)
        skb->when = jiffies;
        buff = skb_clone(skb, GFP_ATOMIC);
 
-       skb_set_owner_w(skb, sk);
+       skb_set_owner_w(buff, sk);
 
        tp->af_specific->queue_xmit(buff);
 
index f3503e4611eb8f0bb78fe9e5ef06c5a28696b0f1..337ff5413c22c7ee1b4bf1fd319e2f7e28e6cc85 100644 (file)
@@ -348,28 +348,29 @@ static __inline__ int tcp_keepopen_proc(struct sock *sk)
  */
 #define MAX_KA_PROBES  5
 
+/* Keepopen's are only valid for "established" TCP's, nicely our listener
+ * hash gets rid of most of the useless testing, so we run through a couple
+ * of the established hash chains each clock tick.  -DaveM
+ */
 static void tcp_keepalive(unsigned long data)
 {
-       struct sock *sk;
+       static int chain_start = 0;
        int count = 0;
        int i;
        
-       for(i=0; i < SOCK_ARRAY_SIZE; i++)
-       {
-               sk = tcp_prot.sock_array[i];
-               while (sk)
-               {
-                       if (sk->keepopen)
-                       {
+       for(i = chain_start; i < (chain_start + (TCP_HTABLE_SIZE >> 2)); i++) {
+               struct sock *sk = tcp_established_hash[i];
+               while(sk) {
+                       if(sk->keepopen) {
                                count += tcp_keepopen_proc(sk);
+                               if(count == MAX_KA_PROBES)
+                                       goto out;
                        }
-
-                       if (count == MAX_KA_PROBES)
-                               return;
-                       
-                       sk = sk->next;      
+                       sk = sk->next;
                }
        }
+out:
+       chain_start = ((chain_start + (TCP_HTABLE_SIZE>>2)) & (TCP_HTABLE_SIZE - 1));
 }
 
 /*
@@ -457,26 +458,24 @@ void tcp_retransmit_timer(unsigned long data)
  *     Slow timer for SYN-RECV sockets
  */
 
+/* This now scales very nicely. -DaveM */
 static void tcp_syn_recv_timer(unsigned long data)
 {
        struct sock *sk;
        unsigned long now = jiffies;
        int i;
 
-       for(i=0; i < SOCK_ARRAY_SIZE; i++)
-       {
-               sk = tcp_prot.sock_array[i];
-               while (sk)
-               {
+       for(i = 0; i < TCP_LHTABLE_SIZE; i++) {
+               sk = tcp_listening_hash[i];
+
+               while(sk) {
                        struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
                        
-                       if (sk->state == TCP_LISTEN && !sk->users &&
-                           tp->syn_wait_queue)
-                       {
+                       /* TCP_LISTEN is implied. */
+                       if (!sk->users && tp->syn_wait_queue) {
                                struct open_request *req;
 
                                req = tp->syn_wait_queue;
-
                                do {
                                        struct open_request *conn;
                                  
@@ -484,17 +483,13 @@ static void tcp_syn_recv_timer(unsigned long data)
                                        req = req->dl_next;
 
                                        if (conn->sk)
-                                       {
                                                continue;
-                                       }
-                                       
+
                                        if ((long)(now - conn->expires) <= 0)
                                                break;
 
                                        tcp_synq_unlink(tp, conn);
-                                       
-                                       if (conn->retrans >= TCP_RETR1)
-                                       {
+                                       if (conn->retrans >= TCP_RETR1) {
 #ifdef TCP_DEBUG
                                                printk(KERN_DEBUG "syn_recv: "
                                                       "too many retransmits\n");
@@ -506,16 +501,15 @@ static void tcp_syn_recv_timer(unsigned long data)
 
                                                if (!tp->syn_wait_queue)
                                                        break;
-                                       }
-                                       else
-                                       {
+                                       } else {
                                                __u32 timeo;
-                                               
+
                                                (*conn->class->rtx_syn_ack)(sk, conn);
 
                                                conn->retrans++;
 #ifdef TCP_DEBUG
-                                               printk(KERN_DEBUG "syn_ack rtx %d\n", conn->retrans);
+                                               printk(KERN_DEBUG "syn_ack rtx %d\n",
+                                                      conn->retrans);
 #endif
                                                timeo = min((TCP_TIMEOUT_INIT 
                                                             << conn->retrans),
@@ -525,7 +519,6 @@ static void tcp_syn_recv_timer(unsigned long data)
                                        }
                                } while (req != tp->syn_wait_queue);
                        }
-                       
                        sk = sk->next;
                }
        }
index d5ec97acd3abdf0cdf8848172f5d2f9102ebc355..18766b030fa009162fe5278c1ef3aa95bcba5568 100644 (file)
@@ -49,6 +49,9 @@
  *             Mike Shaver     :       RFC1122 checks.
  *             Alan Cox        :       Nonblocking error fix.
  *     Willy Konynenberg       :       Transparent proxying support.
+ *             David S. Miller :       New socket lookup architecture.
+ *                                     Last socket cache retained as it
+ *                                     does have a high hit rate.
  *
  *
  *             This program is free software; you can redistribute it and/or
 
 struct udp_mib         udp_statistics;
 
-/*
- *     Cached last hit socket
+struct sock *udp_hash[UDP_HTABLE_SIZE];
+
+static int udp_v4_verify_bind(struct sock *sk, unsigned short snum)
+{
+       struct sock *sk2;
+       int retval = 0;
+
+       SOCKHASH_LOCK();
+       for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
+               if((sk2->num == snum) && (sk2 != sk)) {
+                       if(!sk2->rcv_saddr || !sk->rcv_saddr) {
+                               if(sk->reuse && sk->reuse && (sk2->state != TCP_LISTEN))
+                                       continue;
+                               retval = 1;
+                               break;
+                       }
+                       if((sk2->rcv_saddr == sk->rcv_saddr) &&
+                          (!sk->reuse || !sk2->reuse || (sk2->state == TCP_LISTEN))) {
+                               retval = 1;
+                               break;
+                       }
+               }
+       }
+       SOCKHASH_UNLOCK();
+       return retval;
+}
+
+static inline int udp_lport_inuse(int num)
+{
+       struct sock *sk = udp_hash[num & (UDP_HTABLE_SIZE - 1)];
+
+       for(; sk != NULL; sk = sk->next) {
+               if(sk->num == num)
+                       return 1;
+       }
+       return 0;
+}
+
+/* Shared by v4/v6 tcp. */
+unsigned short udp_good_socknum(void)
+{
+       static int start = 0;
+       unsigned short base;
+       int i, best = 0, size = 32767; /* a big num. */
+       int result;
+
+       base = PROT_SOCK + (start & 1023) + 1;
+
+       SOCKHASH_LOCK();
+       for(i = 0; i < UDP_HTABLE_SIZE; i++) {
+               struct sock *sk = udp_hash[i];
+               if(!sk) {
+                       start = (i + 1 + start) & 1023;
+                       result = i + base + 1;
+                       goto out;
+               } else {
+                       int j = 0;
+                       do {
+                               if(++j >= size)
+                                       goto next;
+                       } while((sk = sk->next));
+                       best = i;
+                       size = j;
+               }
+       next:
+       }
+
+       while(udp_lport_inuse(base + best + 1))
+               best += UDP_HTABLE_SIZE;
+       result = (best + base + 1);
+out:
+       SOCKHASH_UNLOCK();
+       return result;
+}
+
+/* Last hit UDP socket cache, this is ipv4 specific so make it static. */
+static u32 uh_cache_saddr, uh_cache_daddr;
+static u16 uh_cache_dport, uh_cache_sport;
+static struct sock *uh_cache_sk = NULL;
+
+static void udp_v4_hash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+
+       num &= (UDP_HTABLE_SIZE - 1);
+       skp = &udp_hash[num];
+
+       SOCKHASH_LOCK();
+       sk->next = *skp;
+       *skp = sk;
+       sk->hashent = num;
+       SOCKHASH_UNLOCK();
+}
+
+static void udp_v4_unhash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+
+       num &= (UDP_HTABLE_SIZE - 1);
+       skp = &udp_hash[num];
+
+       SOCKHASH_LOCK();
+       while(*skp != NULL) {
+               if(*skp == sk) {
+                       *skp = sk->next;
+                       break;
+               }
+               skp = &((*skp)->next);
+       }
+       if(uh_cache_sk == sk)
+               uh_cache_sk = NULL;
+       SOCKHASH_UNLOCK();
+}
+
+static void udp_v4_rehash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+       int oldnum = sk->hashent;
+
+       num &= (UDP_HTABLE_SIZE - 1);
+       skp = &udp_hash[oldnum];
+
+       SOCKHASH_LOCK();
+       while(*skp != NULL) {
+               if(*skp == sk) {
+                       *skp = sk->next;
+                       break;
+               }
+               skp = &((*skp)->next);
+       }
+       sk->next = udp_hash[num];
+       udp_hash[num] = sk;
+       sk->hashent = num;
+       if(uh_cache_sk == sk)
+               uh_cache_sk = NULL;
+       SOCKHASH_UNLOCK();
+}
+
+/* UDP is nearly always wildcards out the wazoo, it makes no sense to try
+ * harder than this here plus the last hit cache. -DaveM
  */
-volatile unsigned long         uh_cache_saddr,uh_cache_daddr;
-volatile unsigned short  uh_cache_dport, uh_cache_sport;
-volatile struct sock *uh_cache_sk;
+struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+       struct sock *sk, *result = NULL;
+       unsigned short hnum = ntohs(dport);
+       int badness = -1;
+
+       for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
+               if((sk->num == hnum) && !(sk->dead && (sk->state == TCP_CLOSE))) {
+                       int score = 0;
+                       if(sk->rcv_saddr) {
+                               if(sk->rcv_saddr != daddr)
+                                       continue;
+                               score++;
+                       }
+                       if(sk->daddr) {
+                               if(sk->daddr != saddr)
+                                       continue;
+                               score++;
+                       }
+                       if(sk->dummy_th.dest) {
+                               if(sk->dummy_th.dest != sport)
+                                       continue;
+                               score++;
+                       }
+                       if(score == 3) {
+                               result = sk;
+                               break;
+                       } else if(score > badness) {
+                               result = sk;
+                               badness = score;
+                       }
+               }
+       }
+       return result;
+}
 
-void udp_cache_zap(void)
+__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
 {
-       unsigned long flags;
-       save_flags(flags);
-       cli();
-       uh_cache_saddr=0;
-       uh_cache_daddr=0;
-       uh_cache_dport=0;
-       uh_cache_sport=0;
-       uh_cache_sk=NULL;
-       restore_flags(flags);
+       struct sock *sk;
+
+       if(uh_cache_sk                  &&
+          uh_cache_saddr == saddr      &&
+          uh_cache_sport == sport      &&
+          uh_cache_dport == dport      &&
+          uh_cache_daddr == daddr)
+               return uh_cache_sk;
+
+       sk = udp_v4_lookup_longway(saddr, sport, daddr, dport);
+       uh_cache_sk     = sk;
+       uh_cache_saddr  = saddr;
+       uh_cache_daddr  = daddr;
+       uh_cache_sport  = sport;
+       uh_cache_dport  = dport;
+       return sk;
 }
 
-#define min(a,b)       ((a)<(b)?(a):(b))
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+#define secondlist(hpnum, sk, fpass) \
+({ struct sock *s1; if(!(sk) && (fpass)--) \
+       s1 = udp_hash[(hpnum) & (TCP_HTABLE_SIZE - 1)]; \
+   else \
+       s1 = (sk); \
+   s1; \
+})
+
+#define udp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \
+       secondlist((hpnum), udp_hash[(hnum)&(TCP_HTABLE_SIZE-1)],(fpass))
+
+#define udp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
+       secondlist((hpnum),(sk)->next,(fpass))
+
+struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+                                unsigned short rnum, unsigned long laddr,
+                                unsigned long paddr, unsigned short pnum)
+{
+       struct sock *s, *result = NULL;
+       int badness = -1;
+       unsigned short hnum = ntohs(num);
+       unsigned short hpnum = ntohs(pnum);
+       int firstpass = 1;
+
+       SOCKHASH_LOCK();
+       for(s = udp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
+           s != NULL;
+           s = udp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
+               if(s->num == hnum || s->num == hpnum) {
+                       int score = 0;
+                       if(s->dead && (s->state == TCP_CLOSE))
+                               continue;
+                       if(s->rcv_saddr) {
+                               if((s->num != hpnum || s->rcv_saddr != paddr) &&
+                                  (s->num != hnum || s->rcv_saddr != laddr))
+                                       continue;
+                               score++;
+                       }
+                       if(s->daddr) {
+                               if(s->daddr != raddr)
+                                       continue;
+                               score++;
+                       }
+                       if(s->dummy_th.dest) {
+                               if(s->dummy_th.dest != rnum)
+                                       continue;
+                               score++;
+                       }
+                       if(score == 3 && s->num == hnum) {
+                               result = s;
+                               break;
+                       } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+                                       result = s;
+                                       badness = score;
+                       }
+               }
+       }
+       SOCKHASH_UNLOCK();
+       return result;
+}
+
+#undef secondlist
+#undef udp_v4_proxy_loop_init
+#undef udp_v4_proxy_loop_next
+
+#endif
+
+static inline struct sock *udp_v4_mcast_next(struct sock *sk,
+                                            unsigned short num,
+                                            unsigned long raddr,
+                                            unsigned short rnum,
+                                            unsigned long laddr)
+{
+       struct sock *s = sk;
+       unsigned short hnum = ntohs(num);
+       for(; s; s = s->next) {
+               if ((s->num != hnum)                                    ||
+                   (s->dead && (s->state == TCP_CLOSE))                ||
+                   (s->daddr && s->daddr!=raddr)                       ||
+                   (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) ||
+                   (s->rcv_saddr  && s->rcv_saddr != laddr))
+                       continue;
+               break;
+       }
+       return s;
+}
 
+#define min(a,b)       ((a)<(b)?(a):(b))
 
 /*
  * This routine is called by the ICMP module when it gets some
@@ -158,8 +427,7 @@ void udp_err(struct sk_buff *skb, unsigned char *dp)
        int code = skb->h.icmph->code;
        struct sock *sk;
 
-       sk = get_sock(&udp_prot, uh->source, iph->daddr, uh->dest, iph->saddr);
-
+       sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
        if (sk == NULL)
                return; /* No socket for error */
 
@@ -234,9 +502,10 @@ struct udpfakehdr
 };
 
 /*
- *     Copy and checksum a UDP packet from user space into a buffer. We still have to do the planning to
- *     get ip_build_xmit to spot direct transfer to network card and provide an additional callback mode
- *     for direct user->board I/O transfers. That one will be fun.
+ *     Copy and checksum a UDP packet from user space into a buffer. We still have
+ *     to do the planning to get ip_build_xmit to spot direct transfer to network
+ *     card and provide an additional callback mode for direct user->board I/O
+ *     transfers. That one will be fun.
  */
  
 static int udp_getfrag(const void *p, char * to, unsigned int offset, unsigned int fraglen) 
@@ -271,9 +540,9 @@ static int udp_getfrag(const void *p, char * to, unsigned int offset, unsigned i
 
 /*
  *     Unchecksummed UDP is sufficiently critical to stuff like ATM video conferencing
- *     that we use two routines for this for speed. Probably we ought to have a CONFIG_FAST_NET
- *     set for >10Mb/second boards to activate this sort of coding. Timing needed to verify if
- *     this is a valid decision.
+ *     that we use two routines for this for speed. Probably we ought to have a
+ *     CONFIG_FAST_NET set for >10Mb/second boards to activate this sort of coding.
+ *     Timing needed to verify if this is a valid decision.
  */
  
 static int udp_getfrag_nosum(const void *p, char * to, unsigned int offset, unsigned int fraglen) 
@@ -616,7 +885,8 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
                sk->rcv_saddr=INADDR_ANY;
                sk->daddr=INADDR_ANY;
                sk->state = TCP_CLOSE;
-               udp_cache_zap();
+               if(uh_cache_sk == sk)
+                       uh_cache_sk = NULL;
                return 0;
        }
 
@@ -638,7 +908,8 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
        sk->daddr = rt->rt_dst;
        sk->dummy_th.dest = usin->sin_port;
        sk->state = TCP_ESTABLISHED;
-       udp_cache_zap();
+       if(uh_cache_sk == sk)
+               uh_cache_sk = NULL;
        ip_rt_put(rt);
        return(0);
 }
@@ -648,8 +919,8 @@ static void udp_close(struct sock *sk, unsigned long timeout)
 {
        lock_sock(sk);
        sk->state = TCP_CLOSE;
-       if(uh_cache_sk==sk)
-               udp_cache_zap();
+       if(uh_cache_sk == sk)
+               uh_cache_sk = NULL;
        release_sock(sk);
        sk->dead = 1;
        destroy_sock(sk);
@@ -682,6 +953,41 @@ static inline void udp_deliver(struct sock *sk, struct sk_buff *skb)
        udp_queue_rcv_skb(sk, skb);
 }
 
+/*
+ *     Multicasts and broadcasts go to each listener.
+ */
+static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh,
+                                u32 saddr, u32 daddr)
+{
+       struct sock *sk;
+       int given = 0;
+
+       SOCKHASH_LOCK();
+       sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
+       sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr);
+       if(sk) {
+               struct sock *sknext = NULL;
+
+               do {
+                       struct sk_buff *skb1 = skb;
+
+                       sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr,
+                                                  uh->source, daddr);
+                       if(sknext)
+                               skb1 = skb_clone(skb, GFP_ATOMIC);
+
+                       if(skb1)
+                               udp_deliver(sk, skb1);
+                       sk = sknext;
+               } while(sknext);
+               given = 1;
+       }
+       SOCKHASH_UNLOCK();
+       if(!given)
+               kfree_skb(skb, FREE_READ);
+       return 0;
+}
+
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 /*
  *     Check whether a received UDP packet might be for one of our
@@ -694,35 +1000,18 @@ int udp_chkaddr(struct sk_buff *skb)
        struct udphdr *uh = (struct udphdr *)(skb->nh.raw + iph->ihl*4);
        struct sock *sk;
 
-       sk = get_sock(&udp_prot, uh->dest, iph->saddr, uh->source, iph->daddr);
+       sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+       if (!sk)
+               return 0;
 
-       if (!sk) return 0;
        /* 0 means accept all LOCAL addresses here, not all the world... */
-       if (sk->rcv_saddr == 0) return 0;
+       if (sk->rcv_saddr == 0)
+               return 0;
+
        return 1;
 }
 #endif
 
-
-static __inline__ struct sock *
-get_udp_sock(unsigned short dport, unsigned long saddr, unsigned short sport,
-            unsigned long daddr)
-{
-       struct sock *sk;
-
-       if (saddr==uh_cache_saddr && daddr==uh_cache_daddr &&
-           dport==uh_cache_dport && sport==uh_cache_sport)
-                return (struct sock *)uh_cache_sk;
-       sk = get_sock(&udp_prot, dport, saddr, sport, daddr);
-       uh_cache_saddr=saddr;
-       uh_cache_daddr=daddr;
-       uh_cache_dport=dport;
-       uh_cache_sport=sport;
-       uh_cache_sk=sk;
-       return sk;
-}
-
-
 /*
  *     All we need to do is get the socket, and then do a checksum. 
  */
@@ -769,12 +1058,10 @@ int udp_rcv(struct sk_buff *skb, unsigned short len)
        /* FIXME list for IP, though, so I wouldn't worry about it. */
        /* (That's the Right Place to do it, IMHO.) -- MS */
 
-       if (uh->check && (
-               ( (skb->ip_summed == CHECKSUM_HW) && udp_check(uh, len, saddr, daddr, skb->csum ) ) ||
-               ( (skb->ip_summed == CHECKSUM_NONE) && udp_check(uh, len, saddr, daddr,csum_partial((char*)uh, len, 0)))
-                         /* skip if CHECKSUM_UNNECESSARY */
-                        )
-          ) {
+       if (uh->check &&
+           (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,len,saddr,daddr,skb->csum)) ||
+            ((skb->ip_summed==CHECKSUM_NONE) &&
+             (udp_check(uh,len,saddr,daddr, csum_partial((char*)uh, len, 0)))))) {
                /* <mea@utu.fi> wants to know, who sent it, to
                   go and stomp on the garbage sender... */
 
@@ -802,38 +1089,17 @@ int udp_rcv(struct sk_buff *skb, unsigned short len)
        skb_trim(skb,len);
 
 
-       if (rt->rt_flags&(RTF_BROADCAST|RTF_MULTICAST))
-       {
-               /*
-                *      Multicasts and broadcasts go to each listener.
-                */
-               struct sock *sknext=NULL;
-               sk=get_sock_mcast(udp_prot.sock_array[ntohs(uh->dest)&(SOCK_ARRAY_SIZE-1)], uh->dest,
-                               saddr, uh->source, daddr);
-               if(sk) {                
-                       do {
-                               struct sk_buff *skb1;
-
-                               sknext=get_sock_mcast(sk->next, uh->dest, saddr, uh->source, daddr);
-                               if(sknext)
-                                       skb1=skb_clone(skb,GFP_ATOMIC);
-                               else
-                                       skb1=skb;
-                               if(skb1)
-                                       udp_deliver(sk, skb1);
-                               sk=sknext;
-                       } while(sknext!=NULL);
-               } else
-                       kfree_skb(skb, FREE_READ);
-               return 0;
-       }
+       if(rt->rt_flags & (RTF_BROADCAST|RTF_MULTICAST))
+               return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
        if (IPCB(skb)->redirport)
-               sk = get_sock_proxy(&udp_prot, uh->dest, saddr, uh->source, daddr, skb->dev->pa_addr, IPCB(skb)->redirport);
+               sk = udp_v4_proxy_lookup(uh->dest, saddr, uh->source,
+                                        daddr, skb->dev->pa_addr,
+                                        IPCB(skb)->redirport);
        else
 #endif
-       sk = get_udp_sock(uh->dest, saddr, uh->source, daddr);
+       sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest);
        
        if (sk == NULL) {
                udp_statistics.UdpNoPorts++;
@@ -851,26 +1117,33 @@ int udp_rcv(struct sk_buff *skb, unsigned short len)
 }
 
 struct proto udp_prot = {
-       udp_close,
-       udp_connect,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       datagram_poll,
-       udp_ioctl,
-       NULL,
-       NULL,
-       NULL,
-       ip_setsockopt,
-       ip_getsockopt,
-       udp_sendmsg,
-       udp_recvmsg,
-       NULL,           /* No special bind function */
-       udp_queue_rcv_skb,
-       128,
-       0,
-       "UDP",
-       0, 0,
-       NULL,
+       (struct sock *)&udp_prot,       /* sklist_next */
+       (struct sock *)&udp_prot,       /* sklist_prev */
+       udp_close,                      /* close */
+       udp_connect,                    /* connect */
+       NULL,                           /* accept */
+       NULL,                           /* retransmit */
+       NULL,                           /* write_wakeup */
+       NULL,                           /* read_wakeup */
+       datagram_poll,                  /* poll */
+       udp_ioctl,                      /* ioctl */
+       NULL,                           /* init */
+       NULL,                           /* destroy */
+       NULL,                           /* shutdown */
+       ip_setsockopt,                  /* setsockopt */
+       ip_getsockopt,                  /* getsockopt */
+       udp_sendmsg,                    /* sendmsg */
+       udp_recvmsg,                    /* recvmsg */
+       NULL,                           /* bind */
+       udp_queue_rcv_skb,              /* backlog_rcv */
+       udp_v4_hash,                    /* hash */
+       udp_v4_unhash,                  /* unhash */
+       udp_v4_rehash,                  /* rehash */
+       udp_good_socknum,               /* good_socknum */
+       udp_v4_verify_bind,             /* verify_bind */
+       128,                            /* max_header */
+       0,                              /* retransmits */
+       "UDP",                          /* name */
+       0,                              /* inuse */
+       0                               /* highestinuse */
 };
index 1b04029881df0626da08e5efe77c1c968129ce53..1ec58d856304ba37443fb6a94cb82c024d38f017 100644 (file)
@@ -7,7 +7,7 @@
  *
  *     Adapted from linux/net/ipv4/af_inet.c
  *
- *     $Id: af_inet6.c,v 1.8 1997/01/26 07:14:56 davem Exp $
+ *     $Id: af_inet6.c,v 1.12 1997/03/02 06:14:44 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -61,8 +61,6 @@
 #include <linux/ip_fw.h>
 #include <net/addrconf.h>
 
-struct sock * rawv6_sock_array[SOCK_ARRAY_SIZE];
-
 extern struct proto_ops inet6_stream_ops;
 extern struct proto_ops inet6_dgram_ops;
 
@@ -70,62 +68,37 @@ static int inet6_create(struct socket *sock, int protocol)
 {
        struct sock *sk;
        struct proto *prot;
-       int err;
 
        sk = sk_alloc(GFP_KERNEL);
        if (sk == NULL) 
-               return(-ENOBUFS);
-
-       /*
-        *      Note for tcp that also wiped the dummy_th block for us.
-        */
-
-       switch(sock->type) 
-       {
-               case SOCK_STREAM:
-               case SOCK_SEQPACKET:
-                       if (protocol && protocol != IPPROTO_TCP) 
-                       {
-                               sk_free(sk);
-                               return(-EPROTONOSUPPORT);
-                       }
-                       protocol = IPPROTO_TCP;
-                       sk->no_check = TCP_NO_CHECK;
-                       prot = &tcpv6_prot;
-                       sock->ops = &inet6_stream_ops;
-                       break;
-
-               case SOCK_DGRAM:
-                       if (protocol && protocol != IPPROTO_UDP) 
-                       {
-                               sk_free(sk);
-                               return(-EPROTONOSUPPORT);
-                       }
-                       protocol = IPPROTO_UDP;
-                       sk->no_check = UDP_NO_CHECK;
-                       prot=&udpv6_prot;
-                       sock->ops = &inet6_dgram_ops;
-                       break;
-      
-               case SOCK_RAW:
-                       if (!suser()) 
-                       {
-                               sk_free(sk);
-                               return(-EPERM);
-                       }
-                       if (!protocol) 
-                       {
-                               sk_free(sk);
-                               return(-EPROTONOSUPPORT);
-                       }
-                       prot = &rawv6_prot;
-                       sock->ops = &inet6_dgram_ops;
-                       sk->reuse = 1;
-                       sk->num = protocol;
-                       break;
-               default:
-                       sk_free(sk);
-                       return(-ESOCKTNOSUPPORT);
+               goto do_oom;
+
+       /* Note for tcp that also wiped the dummy_th block for us. */
+       if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) {
+               if (protocol && protocol != IPPROTO_TCP) 
+                       goto free_and_noproto;
+               protocol = IPPROTO_TCP;
+               sk->no_check = TCP_NO_CHECK;
+               prot = &tcpv6_prot;
+               sock->ops = &inet6_stream_ops;
+       } else if(sock->type == SOCK_DGRAM) {
+               if (protocol && protocol != IPPROTO_UDP) 
+                       goto free_and_noproto;
+               protocol = IPPROTO_UDP;
+               sk->no_check = UDP_NO_CHECK;
+               prot=&udpv6_prot;
+               sock->ops = &inet6_dgram_ops;
+       } else if(sock->type == SOCK_RAW) {
+               if (!suser()) 
+                       goto free_and_badperm;
+               if (!protocol) 
+                       goto free_and_noproto;
+               prot = &rawv6_prot;
+               sock->ops = &inet6_dgram_ops;
+               sk->reuse = 1;
+               sk->num = protocol;
+       } else {
+               goto free_and_badtype;
        }
        
        sock_init_data(sock,sk);
@@ -145,11 +118,9 @@ static int inet6_create(struct socket *sock, int protocol)
        sk->net_pinfo.af_inet6.mcast_hops = IPV6_DEFAULT_MCASTHOPS;
        sk->net_pinfo.af_inet6.mc_loop    = 1;
 
-       /*
-        *      init the ipv4 part of the socket since
-        *      we can have sockets using v6 API for ipv4
+       /* Init the ipv4 part of the socket since we can have sockets
+        * using v6 API for ipv4.
         */
-
        sk->ip_ttl=64;
 
        sk->ip_mc_loop=1;
@@ -157,34 +128,41 @@ static int inet6_create(struct socket *sock, int protocol)
        sk->ip_mc_index=0;
        sk->ip_mc_list=NULL;
 
-
        if (sk->type==SOCK_RAW && protocol==IPPROTO_RAW)
                sk->ip_hdrincl=1;
 
-       if (sk->num) 
-       {
-               /*
-                * It assumes that any protocol which allows
+       if (sk->num) {
+               /* It assumes that any protocol which allows
                 * the user to assign a number at socket
-                * creation time automatically
-                * shares.
+                * creation time automatically shares.
                 */
-
-               inet_put_sock(sk->num, sk);
                sk->dummy_th.source = ntohs(sk->num);
+               if(sk->prot->hash)
+                       sk->prot->hash(sk);
+               add_to_prot_sklist(sk);
        }
 
-       if (sk->prot->init) 
-       {
-               err = sk->prot->init(sk);
-               if (err != 0) 
-               {
+       if (sk->prot->init) {
+               int err = sk->prot->init(sk);
+               if (err != 0) {
                        destroy_sock(sk);
                        return(err);
                }
        }
        MOD_INC_USE_COUNT;
        return(0);
+
+free_and_badtype:
+       sk_free(sk);
+       return -ESOCKTNOSUPPORT;
+free_and_badperm:
+       sk_free(sk);
+       return -EPERM;
+free_and_noproto:
+       sk_free(sk);
+       return -EPROTONOSUPPORT;
+do_oom:
+       return -ENOBUFS;
 }
 
 static int inet6_dup(struct socket *newsock, struct socket *oldsock)
@@ -192,80 +170,47 @@ static int inet6_dup(struct socket *newsock, struct socket *oldsock)
        return(inet6_create(newsock, oldsock->sk->protocol));
 }
 
-
-/*
- *     bind for INET6 API      
- */
-
-static int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
-                     int addr_len)
+/* bind for INET6 API */
+static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
        struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr;
-       struct sock *sk = sock->sk, *sk2;
+       struct sock *sk = sock->sk;
        __u32 v4addr = 0;
        unsigned short snum = 0;
        int addr_type = 0;
 
-       /*
-        *      If the socket has its own bind function then use it.
-        */
-        
+       /* If the socket has its own bind function then use it. */
        if(sk->prot->bind)
                return sk->prot->bind(sk, uaddr, addr_len);
                
-       /* check this error. */
-       if (sk->state != TCP_CLOSE)
-               return(-EINVAL);
-
-       if(addr_len < sizeof(struct sockaddr_in6))
+       /* Check these errors (active socket, bad address length, double bind). */
+       if ((sk->state != TCP_CLOSE)                    ||
+           (addr_len < sizeof(struct sockaddr_in6))    ||
+           (sk->num != 0))
                return -EINVAL;
                
-       if(sock->type != SOCK_RAW)
-       {
-               if (sk->num != 0) 
-                       return(-EINVAL);
-
-               snum = ntohs(addr->sin6_port);
-               
-               if (snum == 0) 
-                       snum = get_new_socknum(sk->prot, 0);
-
-               if (snum < PROT_SOCK && !suser()) 
-                       return(-EACCES);
-       }
+       snum = ntohs(addr->sin6_port);
+       if (snum == 0) 
+               snum = sk->prot->good_socknum();
+       if (snum < PROT_SOCK && !suser()) 
+               return(-EACCES);
        
        addr_type = ipv6_addr_type(&addr->sin6_addr);
-
        if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM)
-       {
                return(-EINVAL);
-       }
-
-       /*
-        *      check if the address belongs to the host
-        */
 
-       if (addr_type == IPV6_ADDR_MAPPED)
-       {
+       /* Check if the address belongs to the host. */
+       if (addr_type == IPV6_ADDR_MAPPED) {
                v4addr = addr->sin6_addr.s6_addr32[3];
-
                if (__ip_chk_addr(v4addr) != IS_MYADDR)
                        return(-EADDRNOTAVAIL);
-       }
-       else
-       {
-               if (addr_type != IPV6_ADDR_ANY)
-               {
-                       /* 
-                        *      ipv4 addr of the socket is invalid.
-                        *      only the unpecified and mapped address  
-                        *      have a v4 equivalent.
+       } else {
+               if (addr_type != IPV6_ADDR_ANY) {
+                       /* ipv4 addr of the socket is invalid.  Only the
+                        * unpecified and mapped address have a v4 equivalent.
                         */
-
                        v4addr = LOOPBACK4_IPV6;
-
-                       if (!(addr_type & IPV6_ADDR_MULTICAST))
-                       {
+                       if (!(addr_type & IPV6_ADDR_MULTICAST)) {
                                if (ipv6_chk_addr(&addr->sin6_addr) == NULL)
                                        return(-EADDRNOTAVAIL);
                        }
@@ -282,82 +227,16 @@ static int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
                memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr, 
                       sizeof(struct in6_addr));
 
-       if(sock->type != SOCK_RAW)
-       {
-               /* Make sure we are allowed to bind here. */
-               cli();
-               for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
-                                       sk2 != NULL; sk2 = sk2->next) 
-               {
-                       /*
-                        *      Hash collision or real match ?
-                        */
-                        
-                       if (sk2->num != snum) 
-                               continue;
-                               
-                       /*
-                        *      Either bind on the port is wildcard means
-                        *      they will overlap and thus be in error.
-                        *      We use the sk2 v4 address to test the 
-                        *      other socket since addr_any in av4 implies
-                        *      addr_any in v6
-                        */                     
-                        
-                       if (addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr))
-                       {
-                               /*
-                                *      Allow only if both are setting reuse.
-                                */
-                               if(sk2->reuse && sk->reuse && sk2->state!=TCP_LISTEN)
-                                       continue;
-                               sti();
-                               return(-EADDRINUSE);
-                       }
-
-                       /*
-                        *      Two binds match ?
-                        */
+       /* Make sure we are allowed to bind here. */
+       if(sk->prot->verify_bind(sk, snum))
+               return -EADDRINUSE;
 
-                       if (ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
-                                         &sk2->net_pinfo.af_inet6.rcv_saddr))
-
-                               continue;
-                       /*
-                        *      Reusable port ?
-                        */
-
-                       if (!sk->reuse)
-                       {
-                               sti();
-                               return(-EADDRINUSE);
-                       }
-                       
-                       /*
-                        *      Reuse ?
-                        */
-                        
-                       if (!sk2->reuse || sk2->state==TCP_LISTEN) 
-                       {
-                               sti();
-                               return(-EADDRINUSE);
-                       }
-               }
-               sti();
-
-               inet_remove_sock(sk);
-               
-               /*
-               if(sock->type==SOCK_DGRAM)
-                       udp_cache_zap();
-               if(sock->type==SOCK_STREAM)
-                       tcp_cache_zap();
-                       */
-               inet_put_sock(snum, sk);
-               sk->dummy_th.source = ntohs(sk->num);
-               sk->dummy_th.dest = 0;
-               sk->daddr = 0;
-       }
+       sk->num = snum;
+       sk->dummy_th.source = ntohs(sk->num);
+       sk->dummy_th.dest = 0;
+       sk->daddr = 0;
+       sk->prot->rehash(sk);
+       add_to_prot_sklist(sk);
 
        return(0);
 }
@@ -385,31 +264,24 @@ static int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
   
        sin->sin6_family = AF_INET6;
        sk = sock->sk;
-       if (peer) 
-       {
+       if (peer) {
                if (!tcp_connected(sk->state))
                        return(-ENOTCONN);
                sin->sin6_port = sk->dummy_th.dest;
                memcpy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.daddr,
                       sizeof(struct in6_addr));
-       } 
-       else 
-       {
-               if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) ==
-                   IPV6_ADDR_ANY)
+       } else {
+               if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_ANY)
                        memcpy(&sin->sin6_addr, 
                               &sk->net_pinfo.af_inet6.saddr,
                               sizeof(struct in6_addr));
-
                else
                        memcpy(&sin->sin6_addr, 
                               &sk->net_pinfo.af_inet6.rcv_saddr,
                               sizeof(struct in6_addr));
 
                sin->sin6_port = sk->dummy_th.source;
-
        }
-       
        *uaddr_len = sizeof(*sin);      
        return(0);
 }
@@ -510,214 +382,6 @@ static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
        return(0);
 }
 
-/*
- * This routine must find a socket given a TCP or UDP header.
- * Everything is assumed to be in net order.
- *
- * We give priority to more closely bound ports: if some socket
- * is bound to a particular foreign address, it will get the packet
- * rather than somebody listening to any address..
- */
-
-struct sock *inet6_get_sock(struct proto *prot, 
-                           struct in6_addr *loc_addr, 
-                           struct in6_addr *rmt_addr,                     
-                           unsigned short loc_port,
-                           unsigned short rmt_port)
-{
-       struct sock *s;
-       struct sock *result = NULL;
-       int badness = -1;
-       unsigned short hnum;
-       struct ipv6_pinfo *np;
-       hnum = ntohs(loc_port);
-
-       /*
-        * SOCK_ARRAY_SIZE must be a power of two.  This will work better
-        * than a prime unless 3 or more sockets end up using the same
-        * array entry.  This should not be a problem because most
-        * well known sockets don't overlap that much, and for
-        * the other ones, we can just be careful about picking our
-        * socket number when we choose an arbitrary one.
-        */
-
-       for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
-                       s != NULL; s = s->next) 
-       {
-               int score = 0;
-               
-               if ((s->num != hnum) || s->family != AF_INET6)
-                       continue;
-
-               if(s->dead && (s->state == TCP_CLOSE))
-               {
-                       printk(KERN_DEBUG "dead or closed socket\n");
-                       continue;
-               }
-
-               np = &s->net_pinfo.af_inet6;
-
-               /* remote port matches? */
-
-               if (s->dummy_th.dest) {
-                       if (s->dummy_th.dest != rmt_port)
-                       {
-                               continue;
-                       }
-                       score++;
-               }
-
-               /* local address matches? */
-
-               if (!ipv6_addr_any(&np->rcv_saddr))
-               {
-                       if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr))
-                       {
-                               continue;
-                       }
-                       score++;
-               }
-
-               /* remote address matches? */
-               if (!ipv6_addr_any(&np->daddr))
-               {
-                       if (ipv6_addr_cmp(&np->daddr, rmt_addr))
-                       {
-                               continue;
-                       }
-                       score++;
-               }
-
-               /* perfect match? */
-               if (score == 3)
-                       return s;
-               /* no, check if this is the best so far.. */
-               if (score <= badness)
-                       continue;
-               result = s;
-               badness = score;
-       }
-       return result;
-}
-
-static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
-{
-       struct ipv6_mc_socklist *mc;
-               
-       for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next)
-       {
-               if (ipv6_addr_cmp(&mc->addr, addr) == 0)
-                       return 1;
-       }
-
-       return 0;
-}
-
-/*
- *     Deliver a datagram to raw sockets.
- */
-struct sock *inet6_get_sock_raw(struct sock *sk, unsigned short num,
-                               struct in6_addr *loc_addr, 
-                               struct in6_addr *rmt_addr)
-                         
-{
-       struct sock *s;
-       struct ipv6_pinfo *np;
-       int addr_type = 0;
-
-       s=sk;
-
-       addr_type = ipv6_addr_type(loc_addr);
-
-       for(; s != NULL; s = s->next) 
-       {
-               if (s->num != num) 
-                       continue;
-
-               if(s->dead && (s->state == TCP_CLOSE))
-                       continue;
-
-               np = &s->net_pinfo.af_inet6;
-
-               if (!ipv6_addr_any(&np->daddr) &&
-                   ipv6_addr_cmp(&np->daddr, rmt_addr))
-               {
-                       continue;
-               }
-
-               if (!ipv6_addr_any(&np->rcv_saddr))
-               {
-                       if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
-                               return(s);
-               
-                       if ((addr_type & IPV6_ADDR_MULTICAST) &&
-                           inet6_mc_check(s, loc_addr))
-                               return (s);
-                       
-                       continue;
-               }
-
-               return(s);
-       }
-       return(NULL);
-}
-
-/*
- *     inet6_get_sock_mcast for UDP sockets.
- */
-
-struct sock *inet6_get_sock_mcast(struct sock *sk, 
-                                 unsigned short num, unsigned short rmt_port,
-                                 struct in6_addr *loc_addr, 
-                                 struct in6_addr *rmt_addr)
-{      
-       struct sock *s;
-       struct ipv6_pinfo *np;
-
-       s=sk;
-
-       for(; s != NULL; s = s->next) 
-       {
-               if (s->num != num) 
-                       continue;
-
-               if(s->dead && (s->state == TCP_CLOSE))
-                       continue;
-
-               np = &s->net_pinfo.af_inet6;
-
-               if (s->dummy_th.dest) {
-                       if (s->dummy_th.dest != rmt_port)
-                       {
-                               continue;
-                       }
-               }
-
-               if (!ipv6_addr_any(&np->daddr) &&
-                   ipv6_addr_cmp(&np->daddr, rmt_addr))
-               {
-                       continue;
-               }
-
-
-               if (!ipv6_addr_any(&np->rcv_saddr))
-               {
-                       if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
-                               return(s);
-               }
-               
-               if (!inet6_mc_check(s, loc_addr))
-               {
-                       continue;
-               }
-
-               return(s);
-       }
-       return(NULL);
-}
-       
-
 struct proto_ops inet6_stream_ops = {
        AF_INET6,
 
@@ -773,7 +437,6 @@ int init_module(void)
 void inet6_proto_init(struct net_proto *pro)
 #endif
 {
-       int i;
        struct sk_buff *dummy_skb;
 
        printk(KERN_INFO "IPv6 v0.1 for NET3.037\n");
@@ -786,30 +449,13 @@ void inet6_proto_init(struct net_proto *pro)
 
        (void) sock_register(&inet6_family_ops);
        
-       for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
-       {
-               rawv6_sock_array[i] = NULL;
-       }
-
        /*
         *      ipngwg API draft makes clear that the correct semantics
         *      for TCP and UDP is to consider one TCP and UDP instance
         *      in a host availiable by both INET and INET6 APIs and
-        *      hable to communicate via both network protocols.
+        *      able to communicate via both network protocols.
         */
-       
-       tcpv6_prot.inuse = 0;
-       tcpv6_prot.highestinuse = 0;       
-       tcpv6_prot.sock_array = tcp_sock_array;
 
-       udpv6_prot.inuse = 0;
-       udpv6_prot.highestinuse = 0;
-       udpv6_prot.sock_array = udp_sock_array;
-
-       rawv6_prot.inuse = 0;
-       rawv6_prot.highestinuse = 0;
-       rawv6_prot.sock_array = rawv6_sock_array;
-       
        ipv6_init();
 
        icmpv6_init(&inet6_family_ops);
index 346b3673fbcbbcfd9c0eef1ff8cbac9a9132bbb0..c1aeaa2e9585ed7517f59b96a46f57908d957bc0 100644 (file)
@@ -356,7 +356,7 @@ static void icmpv6_notify(int type, int code, unsigned char *buff, int len,
                len -= hdrlen;
        }
 
-       hash = nexthdr & (MAX_INET_PROTOS -1);
+       hash = nexthdr & (MAX_INET_PROTOS - 1);
 
        for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; 
             ipprot != NULL; 
@@ -375,15 +375,14 @@ static void icmpv6_notify(int type, int code, unsigned char *buff, int len,
 
        /* delivery to upper layer protocols failed. try raw sockets */
 
-       sk = rawv6_prot.sock_array[hash];
+       sk = raw_v6_htable[hash];
 
        if (sk == NULL)
        {
                return;
        }
 
-       while ((sk = inet6_get_sock_raw(sk, nexthdr, daddr, saddr)))
-       {
+       while((sk = raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
                rawv6_err(sk, type, code, pbuff, saddr, daddr);
                sk = sk->next;
        }
index e02802c2554a82535f012cda6eeed6ff8ab5d07e..c5e99924e5f7fa84ab1bf0dfee8ee96fe308b75a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *     Based in linux/net/ipv4/ip_input.c
  *
- *     $Id: ipv6_input.c,v 1.13 1996/10/11 16:03:06 roque Exp $
+ *     $Id: ipv6_input.c,v 1.4 1997/02/28 09:56:33 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -212,10 +212,9 @@ static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
        struct sock *sk, *sk2;
        __u8 hash;
 
-       hash = nexthdr & (SOCK_ARRAY_SIZE-1);
+       hash = nexthdr & (MAX_INET_PROTOS - 1);
 
-       sk = rawv6_prot.sock_array[hash];
-       
+       sk = raw_v6_htable[hash];
 
        /*
         *      The first socket found will be delivered after
@@ -225,14 +224,13 @@ static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
        if (sk == NULL)
                return NULL;
        
-       sk = inet6_get_sock_raw(sk, nexthdr, daddr, saddr);
+       sk = raw_v6_lookup(sk, nexthdr, daddr, saddr);
 
        if (sk)
        {
                sk2 = sk;
 
-               while ((sk2 = inet6_get_sock_raw(sk2->next, nexthdr, 
-                                                daddr, saddr)))
+               while ((sk2 = raw_v6_lookup(sk2->next, nexthdr, daddr, saddr)))
                {
                        struct sk_buff *buff;
 
index 14fad5b4517dcec8afaba2dc7df06f937636c19f..006b6cdc6913a1d9ef6e403f2eb51dbc40d47846 100644 (file)
@@ -7,7 +7,7 @@
  *
  *     Adapted from linux/net/ipv4/raw.c
  *
- *     $Id: raw.c,v 1.7 1997/01/26 07:14:56 davem Exp $
+ *     $Id: raw.c,v 1.8 1997/02/28 09:56:34 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
 #include <net/addrconf.h>
 #include <net/transp_v6.h>
 
+#include <net/rawv6.h>
+
 #include <asm/uaccess.h>
 
+struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
+
+static void raw_v6_hash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+
+       num &= (RAWV6_HTABLE_SIZE - 1);
+       skp = &raw_v6_htable[num];
+       SOCKHASH_LOCK();
+       sk->next = *skp;
+       *skp = sk;
+       sk->hashent = num;
+       SOCKHASH_UNLOCK();
+}
+
+static void raw_v6_unhash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+
+       num &= (RAWV6_HTABLE_SIZE - 1);
+       skp = &raw_v6_htable[num];
+
+       SOCKHASH_LOCK();
+       while(*skp != NULL) {
+               if(*skp == sk) {
+                       *skp = sk->next;
+                       break;
+               }
+               skp = &((*skp)->next);
+       }
+       SOCKHASH_UNLOCK();
+}
+
+static void raw_v6_rehash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+       int oldnum = sk->hashent;
+
+       num &= (RAWV6_HTABLE_SIZE - 1);
+       skp = &raw_v6_htable[oldnum];
+
+       SOCKHASH_LOCK();
+       while(*skp != NULL) {
+               if(*skp == sk) {
+                       *skp = sk->next;
+                       break;
+               }
+               skp = &((*skp)->next);
+       }
+       sk->next = raw_v6_htable[num];
+       raw_v6_htable[num] = sk;
+       sk->hashent = num;
+       SOCKHASH_UNLOCK();
+}
+
+static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+       struct ipv6_mc_socklist *mc;
+               
+       for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+               if (ipv6_addr_cmp(&mc->addr, addr) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* Grumble... icmp and ip_input want to get at this... */
+struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
+                          struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
+{
+       struct sock *s = sk;
+       int addr_type = ipv6_addr_type(loc_addr);
+
+       for(s = sk; s; s = s->next) {
+               if((s->num == num)              &&
+                  !(s->dead && (s->state == TCP_CLOSE))) {
+                       struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
+
+                       if (!ipv6_addr_any(&np->daddr) &&
+                           ipv6_addr_cmp(&np->daddr, rmt_addr))
+                               continue;
+
+                       if (!ipv6_addr_any(&np->rcv_saddr)) {
+                               if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
+                                       return(s);
+                               if ((addr_type & IPV6_ADDR_MULTICAST) &&
+                                   inet6_mc_check(s, loc_addr))
+                                       return (s);
+                               continue;
+                       }
+                       return(s);
+               }
+       }
+       return NULL;
+}
+
+/* This cleans up af_inet6 a bit. -DaveM */
+static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+       struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
+       __u32 v4addr = 0;
+       int addr_type;
+
+       /* Check these errors. */
+       if (sk->state != TCP_CLOSE || (addr_len < sizeof(struct sockaddr_in6)))
+               return -EINVAL;
+
+       addr_type = ipv6_addr_type(&addr->sin6_addr);
+
+       /* Check if the address belongs to the host. */
+       if (addr_type == IPV6_ADDR_MAPPED) {
+               v4addr = addr->sin6_addr.s6_addr32[3];
+               if (__ip_chk_addr(v4addr) != IS_MYADDR)
+                       return(-EADDRNOTAVAIL);
+       } else {
+               if (addr_type != IPV6_ADDR_ANY) {
+                       /* ipv4 addr of the socket is invalid.  Only the
+                        * unpecified and mapped address have a v4 equivalent.
+                        */
+                       v4addr = LOOPBACK4_IPV6;
+                       if (!(addr_type & IPV6_ADDR_MULTICAST)) {
+                               if (ipv6_chk_addr(&addr->sin6_addr) == NULL)
+                                       return(-EADDRNOTAVAIL);
+                       }
+               }
+       }
+
+       sk->rcv_saddr = v4addr;
+       sk->saddr = v4addr;
+       memcpy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr, 
+              sizeof(struct in6_addr));
+       if (!(addr_type & IPV6_ADDR_MULTICAST))
+               memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr, 
+                      sizeof(struct in6_addr));
+       return 0;
+}
+
 void rawv6_err(struct sock *sk, int type, int code, unsigned char *buff,
               struct in6_addr *saddr, struct in6_addr *daddr)
 {
@@ -443,28 +586,35 @@ static int rawv6_init_sk(struct sock *sk)
 }
 
 struct proto rawv6_prot = {
-       rawv6_close,
-       udpv6_connect,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       datagram_poll,
-       NULL,
-       rawv6_init_sk,
-       NULL,
-       NULL,
-       rawv6_setsockopt,
-       ipv6_getsockopt,                /* FIXME */
-       rawv6_sendmsg,
-       rawv6_recvmsg,
-       NULL,           /* No special bind */
-       rawv6_rcv_skb,
-       128,
-       0,
-       "RAW",
-       0, 0,
-       NULL
+       (struct sock *)&rawv6_prot,     /* sklist_next */
+       (struct sock *)&rawv6_prot,     /* sklist_prev */
+       rawv6_close,                    /* close */
+       udpv6_connect,                  /* connect */
+       NULL,                           /* accept */
+       NULL,                           /* retransmit */
+       NULL,                           /* write_wakeup */
+       NULL,                           /* read_wakeup */
+       datagram_poll,                  /* poll */
+       NULL,                           /* ioctl */
+       rawv6_init_sk,                  /* init */
+       NULL,                           /* destroy */
+       NULL,                           /* shutdown */
+       rawv6_setsockopt,               /* setsockopt */
+       ipv6_getsockopt,                /* getsockopt - FIXME */
+       rawv6_sendmsg,                  /* sendmsg */
+       rawv6_recvmsg,                  /* recvmsg */
+       rawv6_bind,                     /* bind */
+       rawv6_rcv_skb,                  /* backlog_rcv */
+       raw_v6_hash,                    /* hash */
+       raw_v6_unhash,                  /* unhash */
+       raw_v6_rehash,                  /* rehash */
+       NULL,                           /* good_socknum */
+       NULL,                           /* verify_bind */
+       128,                            /* max_header */
+       0,                              /* retransmits */
+       "RAW",                          /* name */
+       0,                              /* inuse */
+       0                               /* highestinuse */
 };
 
 /*
index 9e4eeae531b7a34ee365b610e440eef3cc188240..3d7d85e01a597c544e540a3551ddd4121d847410 100644 (file)
@@ -5,7 +5,7 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>     
  *
- *     $Id: tcp_ipv6.c,v 1.7 1997/01/26 07:14:57 davem Exp $
+ *     $Id: tcp_ipv6.c,v 1.11 1997/03/03 18:27:31 davem Exp $
  *
  *     Based on: 
  *     linux/net/ipv4/tcp.c
@@ -55,6 +55,192 @@ static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb);
 static struct tcp_func ipv6_mapped;
 static struct tcp_func ipv6_specific;
 
+/* I have no idea if this is a good hash for v6 or not. -DaveM */
+static __inline__ int tcp_v6_hashfn(struct in6_addr *laddr, u16 lport,
+                                   struct in6_addr *faddr, u16 fport)
+{
+       int hashent = (lport ^ fport);
+
+       hashent ^= (laddr->s6_addr32[0] ^ laddr->s6_addr32[1]);
+       hashent ^= (faddr->s6_addr32[0] ^ faddr->s6_addr32[1]);
+       return (hashent & (TCP_HTABLE_SIZE - 1));
+}
+
+static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
+{
+       struct in6_addr *laddr = &sk->net_pinfo.af_inet6.rcv_saddr;
+       struct in6_addr *faddr = &sk->net_pinfo.af_inet6.daddr;
+       __u16 lport = sk->num;
+       __u16 fport = sk->dummy_th.dest;
+       return tcp_v6_hashfn(laddr, lport, faddr, fport);
+}
+
+/* Grrr, addr_type already calculated by caller, but I don't want
+ * to add some silly "cookie" argument to this method just for that.
+ */
+static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum)
+{
+       struct sock *sk2;
+       int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+       int retval = 0, sk_reuse = sk->reuse;
+
+       SOCKHASH_LOCK();
+       sk2 = tcp_bound_hash[tcp_sk_bhashfn(sk)];
+       for(; sk2 != NULL; sk2 = sk2->prev) {
+               if((sk2->num == snum) && (sk2 != sk)) {
+                       unsigned char state = sk2->state;
+                       int sk2_reuse = sk2->reuse;
+                       if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
+                               if((!sk2_reuse)                 ||
+                                  (!sk_reuse)                  ||
+                                  (state != TCP_LISTEN)) {
+                                       retval = 1;
+                                       break;
+                               }
+                       } else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+                                                &sk2->net_pinfo.af_inet6.rcv_saddr)) {
+                               if((!sk_reuse)                  ||
+                                  (!sk2_reuse)                 ||
+                                  (state == TCP_LISTEN)) {
+                                       retval = 1;
+                                       break;
+                               }
+                       }
+               }
+       }
+       SOCKHASH_UNLOCK();
+
+       return retval;
+}
+
+static void tcp_v6_hash(struct sock *sk)
+{
+       unsigned char state;
+
+       SOCKHASH_LOCK();
+       state = sk->state;
+       if(state != TCP_CLOSE) {
+               struct sock **htable;
+
+               if(state == TCP_LISTEN) {
+                       sk->hashent = tcp_sk_listen_hashfn(sk);
+                       htable = &tcp_listening_hash[0];
+               } else {
+                       sk->hashent = tcp_v6_sk_hashfn(sk);
+                       htable = &tcp_established_hash[0];
+               }
+               sk->next = htable[sk->hashent];
+               htable[sk->hashent] = sk;
+               sk->hashtable = htable;
+               tcp_sk_bindify(sk);
+       }
+       SOCKHASH_UNLOCK();
+}
+
+static void tcp_v6_unhash(struct sock *sk)
+{
+       struct sock **htable;
+
+       SOCKHASH_LOCK();
+       htable = sk->hashtable;
+       if(htable) {
+               struct sock **skp = &(htable[sk->hashent]);
+               while(*skp != NULL) {
+                       if(*skp == sk) {
+                               *skp = sk->next;
+                               break;
+                       }
+                       skp = &((*skp)->next);
+               }
+               sk->hashtable = NULL;
+               tcp_sk_unbindify(sk);
+       }
+       SOCKHASH_UNLOCK();
+}
+
+static void tcp_v6_rehash(struct sock *sk)
+{
+       struct sock **htable;
+       unsigned char state;
+
+       SOCKHASH_LOCK();
+       htable = &(sk->hashtable[sk->hashent]);
+       state = sk->state;
+       if(htable) {
+               while(*htable != NULL) {
+                       if(*htable == sk) {
+                               *htable = sk->next;
+                               break;
+                       }
+                       htable = &((*htable)->next);
+               }
+       }
+       htable = NULL;
+       if(state != TCP_CLOSE) {
+               if(state == TCP_LISTEN) {
+                       sk->hashent = tcp_sk_listen_hashfn(sk);
+                       htable = &tcp_listening_hash[0];
+               } else {
+                       sk->hashent = tcp_v6_sk_hashfn(sk);
+                       htable = &tcp_established_hash[0];
+               }
+               sk->next = htable[sk->hashent];
+               htable[sk->hashent] = sk;
+       } else {
+               tcp_sk_unbindify(sk);
+       }
+       sk->hashtable = htable;
+       SOCKHASH_UNLOCK();
+}
+
+static struct sock *tcp_v6_lookup_longway(struct in6_addr *daddr, unsigned short hnum)
+{
+       struct sock *sk = tcp_listening_hash[tcp_lhashfn(hnum)];
+       struct sock *result = NULL;
+
+       for(; sk; sk = sk->next) {
+               if((sk->num == hnum) && (sk->family == AF_INET6)) {
+                       struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+                       if(!ipv6_addr_any(&np->rcv_saddr)) {
+                               if(!ipv6_addr_cmp(&np->rcv_saddr, daddr))
+                                       return sk; /* Best possible match. */
+                       } else if(!result)
+                               result = sk;
+               }
+       }
+       return result;
+}
+
+/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
+ * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ */
+static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
+                                          struct in6_addr *saddr, u16 sport,
+                                          struct in6_addr *daddr, u16 dport)
+{
+       unsigned short hnum = ntohs(dport);
+       struct sock *sk;
+
+       /* Optimize here for direct hit, only listening connections can
+        * have wildcards anyways.  It is assumed that this code only
+        * gets called from within NET_BH.
+        */
+       sk = tcp_established_hash[tcp_v6_hashfn(daddr, hnum, saddr, sport)];
+       for(; sk; sk = sk->next)
+               /* For IPV6 do the cheaper port and family tests first. */
+               if(sk->num              == hnum                 && /* local port     */
+                  sk->family           == AF_INET6             && /* address family */
+                  sk->dummy_th.dest    == sport                && /* remote port    */
+                  !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) &&
+                  !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+                       goto hit; /* You sunk my battleship! */
+       sk = tcp_v6_lookup_longway(daddr, hnum);
+hit:
+       return sk;
+}
+
+#define tcp_v6_lookup(sa, sp, da, dp) __tcp_v6_lookup((0),(sa),(sp),(da),(dp))
+
 static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
                                   struct in6_addr *saddr, 
                                   struct in6_addr *daddr, 
@@ -264,6 +450,11 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        
        tcp_set_state(sk, TCP_SYN_SENT);
        
+       /* Socket identity change complete, no longer
+        * in TCP_CLOSE, so rehash.
+        */
+       sk->prot->rehash(sk);
+
        /* FIXME: should use dcache->rtt if availiable */
        tp->rto = TCP_TIMEOUT_INIT;
 
@@ -340,7 +531,7 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info,
        int err;
        int opening;
 
-       sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, th->source, th->dest);
+       sk = tcp_v6_lookup(saddr, th->source, daddr, th->dest);
 
        if (sk == NULL)
        {
@@ -602,6 +793,10 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        }
 
        memcpy(newsk, sk, sizeof(*newsk));
+
+       /* Or else we die! -DaveM */
+       newsk->sklist_next = NULL;
+
        newsk->opt = NULL;
        newsk->dst_cache  = NULL;
        skb_queue_head_init(&newsk->write_queue);
@@ -706,7 +901,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
        newsk->saddr    = LOOPBACK4_IPV6;
        newsk->rcv_saddr= LOOPBACK4_IPV6;
        
-       inet_put_sock(newsk->num, newsk);
+       newsk->prot->hash(newsk);
 
        return newsk;
 
@@ -907,8 +1102,7 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
 
                tcp_statistics.TcpInSegs++;
                
-               sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, 
-                                   th->dest, th->source);
+               sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest);
 
                if (!sk) 
                {
@@ -1057,7 +1251,7 @@ static struct sock * tcp_v6_get_sock(struct sk_buff *skb, struct tcphdr *th)
        saddr = &skb->nh.ipv6h->saddr;
        daddr = &skb->nh.ipv6h->daddr;
 
-       sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, th->source, th->dest);
+       sk = tcp_v6_lookup(saddr, th->source, daddr, th->dest);
 
        return sk;
 }
@@ -1229,28 +1423,35 @@ static int tcp_v6_destroy_sock(struct sock *sk)
 
 
 struct proto tcpv6_prot = {
-       tcp_close,
-       tcp_v6_connect,
-       tcp_accept,
-       NULL,
-       tcp_write_wakeup,
-       tcp_read_wakeup,
-       tcp_poll,
-       tcp_ioctl,
-       tcp_v6_init_sock,
-       tcp_v6_destroy_sock,
-       tcp_shutdown,
-       tcp_setsockopt,
-       tcp_getsockopt,
-       tcp_v6_sendmsg,
-       tcp_recvmsg,
-       NULL,                   /* No special bind()    */
-       tcp_v6_backlog_rcv,
-       128,
-       0,
-       "TCPv6",
-       0, 0,
-       NULL
+       (struct sock *)&tcpv6_prot,     /* sklist_next */
+       (struct sock *)&tcpv6_prot,     /* sklist_prev */
+       tcp_close,                      /* close */
+       tcp_v6_connect,                 /* connect */
+       tcp_accept,                     /* accept */
+       NULL,                           /* retransmit */
+       tcp_write_wakeup,               /* write_wakeup */
+       tcp_read_wakeup,                /* read_wakeup */
+       tcp_poll,                       /* poll */
+       tcp_ioctl,                      /* ioctl */
+       tcp_v6_init_sock,               /* init */
+       tcp_v6_destroy_sock,            /* destroy */
+       tcp_shutdown,                   /* shutdown */
+       tcp_setsockopt,                 /* setsockopt */
+       tcp_getsockopt,                 /* getsockopt */
+       tcp_v6_sendmsg,                 /* sendmsg */
+       tcp_recvmsg,                    /* recvmsg */
+       NULL,                           /* bind */
+       tcp_v6_backlog_rcv,             /* backlog_rcv */
+       tcp_v6_hash,                    /* hash */
+       tcp_v6_unhash,                  /* unhash */
+       tcp_v6_rehash,                  /* rehash */
+       tcp_good_socknum,               /* good_socknum */
+       tcp_v6_verify_bind,             /* verify_bind */
+       128,                            /* max_header */
+       0,                              /* retransmits */
+       "TCPv6",                        /* name */
+       0,                              /* inuse */
+       0                               /* highestinuse */
 };
 
 static struct inet6_protocol tcpv6_protocol = 
index d2bc4cb1f8b96c7ed5a20e8852b0e4495c3962e1..f3c7ba88a75509a179bff39ae7af4ecfea647435 100644 (file)
@@ -7,7 +7,7 @@
  *
  *     Based on linux/ipv4/udp.c
  *
- *     $Id: udp.c,v 1.7 1997/01/26 07:14:58 davem Exp $
+ *     $Id: udp.c,v 1.8 1997/02/28 09:56:35 davem Exp $
  *
  *     This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
 
 struct udp_mib udp_stats_in6;
 
+/* Grrr, addr_type already calculated by caller, but I don't want
+ * to add some silly "cookie" argument to this method just for that.
+ */
+static int udp_v6_verify_bind(struct sock *sk, unsigned short snum)
+{
+       struct sock *sk2;
+       int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+       int retval = 0, sk_reuse = sk->reuse;
+
+       SOCKHASH_LOCK();
+       for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
+               if((sk2->num == snum) && (sk2 != sk)) {
+                       unsigned char state = sk2->state;
+                       int sk2_reuse = sk2->reuse;
+                       if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
+                               if((!sk2_reuse)                 ||
+                                  (!sk_reuse)                  ||
+                                  (state != TCP_LISTEN)) {
+                                       retval = 1;
+                                       break;
+                               }
+                       } else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+                                                &sk2->net_pinfo.af_inet6.rcv_saddr)) {
+                               if((!sk_reuse)                  ||
+                                  (!sk2_reuse)                 ||
+                                  (state == TCP_LISTEN)) {
+                                       retval = 1;
+                                       break;
+                               }
+                       }
+               }
+       }
+       SOCKHASH_UNLOCK();
+       return retval;
+}
+
+static void udp_v6_hash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+
+       num &= (UDP_HTABLE_SIZE - 1);
+       skp = &udp_hash[num];
+
+       SOCKHASH_LOCK();
+       sk->next = *skp;
+       *skp = sk;
+       sk->hashent = num;
+       SOCKHASH_UNLOCK();
+}
+
+static void udp_v6_unhash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+
+       num &= (UDP_HTABLE_SIZE - 1);
+       skp = &udp_hash[num];
+
+       SOCKHASH_LOCK();
+       while(*skp != NULL) {
+               if(*skp == sk) {
+                       *skp = sk->next;
+                       break;
+               }
+               skp = &((*skp)->next);
+       }
+       SOCKHASH_UNLOCK();
+}
+
+static void udp_v6_rehash(struct sock *sk)
+{
+       struct sock **skp;
+       int num = sk->num;
+       int oldnum = sk->hashent;
+
+       num &= (UDP_HTABLE_SIZE - 1);
+       skp = &udp_hash[oldnum];
+
+       SOCKHASH_LOCK();
+       while(*skp != NULL) {
+               if(*skp == sk) {
+                       *skp = sk->next;
+                       break;
+               }
+               skp = &((*skp)->next);
+       }
+       sk->next = udp_hash[num];
+       udp_hash[num] = sk;
+       sk->hashent = num;
+       SOCKHASH_UNLOCK();
+}
+
+static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
+                                 struct in6_addr *daddr, u16 dport)
+{
+       struct sock *sk, *result = NULL;
+       unsigned short hnum = ntohs(dport);
+       int badness = -1;
+
+       for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
+               if((sk->num == hnum)            &&
+                  (sk->family == AF_INET6)     &&
+                  !(sk->dead && (sk->state == TCP_CLOSE))) {
+                       struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+                       int score = 0;
+                       if(sk->dummy_th.dest) {
+                               if(sk->dummy_th.dest != sport)
+                                       continue;
+                               score++;
+                       }
+                       if(!ipv6_addr_any(&np->rcv_saddr)) {
+                               if(ipv6_addr_cmp(&np->rcv_saddr, daddr))
+                                       continue;
+                               score++;
+                       }
+                       if(!ipv6_addr_any(&np->daddr)) {
+                               if(ipv6_addr_cmp(&np->daddr, saddr))
+                                       continue;
+                               score++;
+                       }
+                       if(score == 3) {
+                               result = sk;
+                               break;
+                       } else if(score > badness) {
+                               result = sk;
+                               badness = score;
+                       }
+               }
+       }
+       return result;
+}
+
 /*
  *
  */
@@ -250,7 +383,7 @@ void udpv6_err(int type, int code, unsigned char *buff, __u32 info,
        
        uh = (struct udphdr *) buff;
 
-       sk = inet6_get_sock(&udpv6_prot, daddr, saddr, uh->source, uh->dest);
+       sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
    
        if (sk == NULL)
        {
@@ -285,6 +418,74 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
        return 0;
 }
 
+static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+       struct ipv6_mc_socklist *mc;
+               
+       for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+               if (ipv6_addr_cmp(&mc->addr, addr) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+static struct sock *udp_v6_mcast_next(struct sock *sk,
+                                     u16 loc_port, struct in6_addr *loc_addr,
+                                     u16 rmt_port, struct in6_addr *rmt_addr)
+{
+       struct sock *s = sk;
+       unsigned short num = ntohs(loc_port);
+       for(; s; s = s->next) {
+               if((s->num == num)              &&
+                  !(s->dead && (s->state == TCP_CLOSE))) {
+                       struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
+                       if(s->dummy_th.dest) {
+                               if(s->dummy_th.dest != rmt_port)
+                                       continue;
+                       }
+                       if(!ipv6_addr_any(&np->daddr) &&
+                          ipv6_addr_cmp(&np->daddr, rmt_addr))
+                               continue;
+
+                       if(!ipv6_addr_any(&np->rcv_saddr)) {
+                               if(ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
+                                       return s;
+                       }
+                       if(!inet6_mc_check(s, loc_addr))
+                               continue;
+                       return s;
+               }
+       }
+       return NULL;
+}
+
+static void udpv6_mcast_deliver(struct udphdr *uh,
+                               struct in6_addr *saddr, struct in6_addr *daddr,
+                               struct sk_buff *skb)
+{
+       struct sock *sk, *sk2;
+
+       sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
+       sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr);
+       if(sk) {
+               sk2 = sk;
+               while((sk2 = udp_v6_mcast_next(sk2->next,
+                                              uh->dest, saddr,
+                                              uh->source, daddr))) {
+                       struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC);
+                       if(sock_queue_rcv_skb(sk, buff) < 0) {
+                               buff->sk = NULL;
+                               kfree_skb(buff, FREE_READ);
+                       }
+               }
+       }
+       if(!sk || sock_queue_rcv_skb(sk, skb) < 0) {
+               skb->sk = NULL;
+               kfree_skb(skb, FREE_READ);
+       }
+}
+
 int udpv6_rcv(struct sk_buff *skb, struct device *dev,
              struct in6_addr *saddr, struct in6_addr *daddr,
              struct ipv6_options *opt, unsigned short len,
@@ -335,41 +536,8 @@ int udpv6_rcv(struct sk_buff *skb, struct device *dev,
        /* 
         *      Multicast receive code 
         */
-       if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST)
-       {
-               struct sock *sk2;
-               int lport;
-               
-               lport = ntohs(uh->dest);
-               sk = udpv6_prot.sock_array[lport & (SOCK_ARRAY_SIZE-1)];
-
-               sk = inet6_get_sock_mcast(sk, lport, uh->source,
-                                         daddr, saddr);
-
-               if (sk)
-               {
-                       sk2 = sk;
-                       
-                       while ((sk2 = inet6_get_sock_mcast(sk2->next, lport,
-                                                          uh->source,
-                                                          daddr, saddr)))
-                       {
-                               struct sk_buff *buff;
-
-                               buff = skb_clone(skb, GFP_ATOMIC);
-
-                               if (sock_queue_rcv_skb(sk, buff) < 0) 
-                               {
-                                       buff->sk = NULL;
-                                       kfree_skb(buff, FREE_READ);
-                               }
-                       }
-               }
-               if (!sk || sock_queue_rcv_skb(sk, skb) < 0)
-               {
-                       skb->sk = NULL;
-                       kfree_skb(skb, FREE_READ);
-               }
+       if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
+               udpv6_mcast_deliver(uh, saddr, daddr, skb);
                return 0;
        }
 
@@ -380,7 +548,7 @@ int udpv6_rcv(struct sk_buff *skb, struct device *dev,
         * for sock caches... i'll skip this for now.
         */
 
-       sk = inet6_get_sock(&udpv6_prot, daddr, saddr, uh->dest, uh->source);
+       sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
 
        if (sk == NULL)
        {
@@ -596,28 +764,35 @@ static struct inet6_protocol udpv6_protocol =
 
 
 struct proto udpv6_prot = {
-       udpv6_close,
-       udpv6_connect,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       datagram_poll,
-       udp_ioctl,
-       NULL,
-       NULL,
-       NULL,
-       ipv6_setsockopt,
-       ipv6_getsockopt,
-       udpv6_sendmsg,
-       udpv6_recvmsg,
-       NULL,           /* No special bind function */
-       udpv6_queue_rcv_skb,
-       128,
-       0,
-       "UDP",
-       0, 0,
-       NULL
+       (struct sock *)&udpv6_prot,     /* sklist_next */
+       (struct sock *)&udpv6_prot,     /* sklist_prev */
+       udpv6_close,                    /* close */
+       udpv6_connect,                  /* connect */
+       NULL,                           /* accept */
+       NULL,                           /* retransmit */
+       NULL,                           /* write_wakeup */
+       NULL,                           /* read_wakeup */
+       datagram_poll,                  /* poll */
+       udp_ioctl,                      /* ioctl */
+       NULL,                           /* init */
+       NULL,                           /* destroy */
+       NULL,                           /* shutdown */
+       ipv6_setsockopt,                /* setsockopt */
+       ipv6_getsockopt,                /* getsockopt */
+       udpv6_sendmsg,                  /* sendmsg */
+       udpv6_recvmsg,                  /* recvmsg */
+       NULL,                           /* bind */
+       udpv6_queue_rcv_skb,            /* backlog_rcv */
+       udp_v6_hash,                    /* hash */
+       udp_v6_unhash,                  /* unhash */
+       udp_v6_rehash,                  /* rehash */
+       udp_good_socknum,               /* good_socknum */
+       udp_v6_verify_bind,             /* verify_bind */
+       128,                            /* max_header */
+       0,                              /* retransmits */
+       "UDP",                          /* name */
+       0,                              /* inuse */
+       0                               /* highestinuse */
 };
 
 void udpv6_init(void)
index f71ee0e56f4f1deda7d17099376d22a509d20b36..2933166bbfe577c6c701b56507cca8e789d5b753 100644 (file)
@@ -95,6 +95,9 @@ EXPORT_SYMBOL(sock_wake_async);
 EXPORT_SYMBOL(sock_alloc_send_skb);
 EXPORT_SYMBOL(sock_init_data);
 EXPORT_SYMBOL(sock_no_fcntl);
+EXPORT_SYMBOL(sock_no_listen);
+EXPORT_SYMBOL(sock_no_getsockopt);
+EXPORT_SYMBOL(sock_no_setsockopt);
 EXPORT_SYMBOL(sock_rfree);
 EXPORT_SYMBOL(sock_wfree);
 EXPORT_SYMBOL(skb_recv_datagram);
@@ -136,7 +139,6 @@ EXPORT_SYMBOL(sklist_insert_socket);
 
 #ifdef CONFIG_INET
 /* Internet layer registration */
-EXPORT_SYMBOL(get_new_socknum);
 EXPORT_SYMBOL(inet_add_protocol);
 EXPORT_SYMBOL(inet_del_protocol);
 EXPORT_SYMBOL(rarp_ioctl_hook);
@@ -156,7 +158,6 @@ EXPORT_SYMBOL(inet_family_ops);
 /* inet functions common to v4 and v6 */
 EXPORT_SYMBOL(inet_stream_ops);
 EXPORT_SYMBOL(inet_dgram_ops);
-EXPORT_SYMBOL(inet_remove_sock);
 EXPORT_SYMBOL(inet_release);
 EXPORT_SYMBOL(inet_stream_connect);
 EXPORT_SYMBOL(inet_dgram_connect);
@@ -168,24 +169,28 @@ EXPORT_SYMBOL(inet_setsockopt);
 EXPORT_SYMBOL(inet_getsockopt);
 EXPORT_SYMBOL(inet_sendmsg);
 EXPORT_SYMBOL(inet_recvmsg);
-EXPORT_SYMBOL(tcp_sock_array);
-EXPORT_SYMBOL(udp_sock_array);
+
+/* Socket demultiplexing. */
+EXPORT_SYMBOL(tcp_good_socknum);
+EXPORT_SYMBOL(tcp_established_hash);
+EXPORT_SYMBOL(tcp_listening_hash);
+EXPORT_SYMBOL(tcp_bound_hash);
+EXPORT_SYMBOL(udp_good_socknum);
+EXPORT_SYMBOL(udp_hash);
+
 EXPORT_SYMBOL(destroy_sock);
 EXPORT_SYMBOL(ip_queue_xmit);
 EXPORT_SYMBOL(csum_partial);
 EXPORT_SYMBOL(dev_lockct);
 EXPORT_SYMBOL(ndisc_eth_hook);
 EXPORT_SYMBOL(memcpy_fromiovecend);
-EXPORT_SYMBOL(csum_partial_copy);
 EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
 EXPORT_SYMBOL(__release_sock);
 EXPORT_SYMBOL(net_timer);
-EXPORT_SYMBOL(inet_put_sock);
 /* UDP/TCP exported functions for TCPv6 */
 EXPORT_SYMBOL(udp_ioctl);
 EXPORT_SYMBOL(udp_connect);
 EXPORT_SYMBOL(udp_sendmsg);
-EXPORT_SYMBOL(tcp_cache_zap);
 EXPORT_SYMBOL(tcp_close);
 EXPORT_SYMBOL(tcp_accept);
 EXPORT_SYMBOL(tcp_write_wakeup);