patch-2.4.18 linux/drivers/sound/dmasound/dmasound_core.c

Next file: linux/drivers/sound/dmasound/dmasound_paula.c
Previous file: linux/drivers/sound/dmasound/dmasound_awacs.c
Back to the patch index
Back to the overall index

diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_core.c linux/drivers/sound/dmasound/dmasound_core.c
@@ -1,4 +1,3 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_core.c
  *
@@ -103,38 +102,113 @@
  *	2000/3/25	Geert Uytterhoeven:
  *			  - Integration of dmasound_q40
  *			  - Small clean ups
+ *
+ *	2001/01/26 [1.0] Iain Sandoe
+ *			  - make /dev/sndstat show revision & edition info.
+ *			  - since dmasound.mach.sq_setup() can fail on pmac
+ *			    its type has been changed to int and the returns
+ *			    are checked.
+ *		   [1.1]  - stop missing translations from being called.
+ *	2001/02/08 [1.2]  - remove unused translation tables & move machine-
+ *			    specific tables to low-level.
+ *			  - return correct info. for SNDCTL_DSP_GETFMTS.
+ *		   [1.3]  - implement SNDCTL_DSP_GETCAPS fully.
+ *		   [1.4]  - make /dev/sndstat text length usage deterministic.
+ *			  - make /dev/sndstat call to low-level
+ *			    dmasound.mach.state_info() pass max space to ll driver.
+ *			  - tidy startup banners and output info.
+ *		   [1.5]  - tidy up a little (removed some unused #defines in
+ *			    dmasound.h)
+ *			  - fix up HAS_RECORD conditionalisation.
+ *			  - add record code in places it is missing...
+ *			  - change buf-sizes to bytes to allow < 1kb for pmac
+ *			    if user param entry is < 256 the value is taken to
+ *			    be in kb > 256 is taken to be in bytes.
+ *			  - make default buff/frag params conditional on
+ *			    machine to allow smaller values for pmac.
+ *			  - made the ioctls, read & write comply with the OSS
+ *			    rules on setting params.
+ *			  - added parsing of _setup() params for record.
+ *	2001/04/04 [1.6]  - fix bug where sample rates higher than maximum were
+ *			    being reported as OK.
+ *			  - fix open() to return -EBUSY as per OSS doc. when
+ *			    audio is in use - this is independent of O_NOBLOCK.
+ *			  - fix bug where SNDCTL_DSP_POST was blocking.
  */
 
+ /* Record capability notes 30/01/2001:
+  * At present these observations apply only to pmac LL driver (the only one
+  * that can do record, at present).  However, if other LL drivers for machines
+  * with record are added they may apply.
+  *
+  * The fragment parameters for the record and play channels are separate.
+  * However, if the driver is opened O_RDWR there is no way (in the current OSS
+  * API) to specify their values independently for the record and playback
+  * channels.  Since the only common factor between the input & output is the
+  * sample rate (on pmac) it should be possible to open /dev/dspX O_WRONLY and
+  * /dev/dspY O_RDONLY.  The input & output channels could then have different
+  * characteristics (other than the first that sets sample rate claiming the
+  * right to set it for ever).  As it stands, the format, channels, number of
+  * bits & sample rate are assumed to be common.  In the future perhaps these
+  * should be the responsibility of the LL driver - and then if a card really
+  * does not share items between record & playback they can be specified
+  * separately.
+*/
+
+/* Thread-safeness of shared_resources notes: 31/01/2001
+ * If the user opens O_RDWR and then splits record & play between two threads
+ * both of which inherit the fd - and then starts changing things from both
+ * - we will have difficulty telling.
+ *
+ * It's bad application coding - but ...
+ * TODO: think about how to sort this out... without bogging everything down in
+ * semaphores.
+ *
+ * Similarly, the OSS spec says "all changes to parameters must be between
+ * open() and the first read() or write(). - and a bit later on (by
+ * implication) "between SNDCTL_DSP_RESET and the first read() or write() after
+ * it".  If the app is multi-threaded and this rule is broken between threads
+ * we will have trouble spotting it - and the fault will be rather obscure :-(
+ *
+ * We will try and put out at least a kmsg if we see it happen... but I think
+ * it will be quite hard to trap it with an -EXXX return... because we can't
+ * see the fault until after the damage is done.
+*/
 
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/sound.h>
 #include <linux/init.h>
 #include <linux/soundcard.h>
+#include <linux/poll.h>
 #include <linux/smp_lock.h>
 
 #include <asm/uaccess.h>
 
 #include "dmasound.h"
 
+#define DMASOUND_CORE_REVISION 1
+#define DMASOUND_CORE_EDITION 6
 
     /*
      *  Declarations
      */
 
 int dmasound_catchRadius = 0;
-static unsigned int numWriteBufs = 4;
-static unsigned int writeBufSize = 32;	/* in KB! */
+static unsigned int numWriteBufs = DEFAULT_N_BUFFERS;
+static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ;	/* in bytes */
 #ifdef HAS_RECORD
-static unsigned int numReadBufs = 4;
-static unsigned int readBufSize = 32;	/* in KB! */
+static unsigned int numReadBufs = DEFAULT_N_BUFFERS;
+static unsigned int readBufSize = DEFAULT_BUFF_SIZE;	/* in bytes */
 #endif
 
 MODULE_PARM(dmasound_catchRadius, "i");
 MODULE_PARM(numWriteBufs, "i");
 MODULE_PARM(writeBufSize, "i");
+#ifdef HAS_RECORD
 MODULE_PARM(numReadBufs, "i");
 MODULE_PARM(readBufSize, "i");
+#endif
 MODULE_LICENSE("GPL");
 
 #ifdef MODULE
@@ -144,246 +218,9 @@
 static int irq_installed = 0;
 #endif /* MODULE */
 
-
-    /*
-     *  Conversion tables
-     */
-
-#ifdef HAS_8BIT_TABLES
-/* 8 bit mu-law */
-
-char dmasound_ulaw2dma8[] = {
-	-126,	-122,	-118,	-114,	-110,	-106,	-102,	-98,
-	-94,	-90,	-86,	-82,	-78,	-74,	-70,	-66,
-	-63,	-61,	-59,	-57,	-55,	-53,	-51,	-49,
-	-47,	-45,	-43,	-41,	-39,	-37,	-35,	-33,
-	-31,	-30,	-29,	-28,	-27,	-26,	-25,	-24,
-	-23,	-22,	-21,	-20,	-19,	-18,	-17,	-16,
-	-16,	-15,	-15,	-14,	-14,	-13,	-13,	-12,
-	-12,	-11,	-11,	-10,	-10,	-9,	-9,	-8,
-	-8,	-8,	-7,	-7,	-7,	-7,	-6,	-6,
-	-6,	-6,	-5,	-5,	-5,	-5,	-4,	-4,
-	-4,	-4,	-4,	-4,	-3,	-3,	-3,	-3,
-	-3,	-3,	-3,	-3,	-2,	-2,	-2,	-2,
-	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	0,
-	125,	121,	117,	113,	109,	105,	101,	97,
-	93,	89,	85,	81,	77,	73,	69,	65,
-	62,	60,	58,	56,	54,	52,	50,	48,
-	46,	44,	42,	40,	38,	36,	34,	32,
-	30,	29,	28,	27,	26,	25,	24,	23,
-	22,	21,	20,	19,	18,	17,	16,	15,
-	15,	14,	14,	13,	13,	12,	12,	11,
-	11,	10,	10,	9,	9,	8,	8,	7,
-	7,	7,	6,	6,	6,	6,	5,	5,
-	5,	5,	4,	4,	4,	4,	3,	3,
-	3,	3,	3,	3,	2,	2,	2,	2,
-	2,	2,	2,	2,	1,	1,	1,	1,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0
-};
-
-/* 8 bit A-law */
-
-char dmasound_alaw2dma8[] = {
-	-22,	-21,	-24,	-23,	-18,	-17,	-20,	-19,
-	-30,	-29,	-32,	-31,	-26,	-25,	-28,	-27,
-	-11,	-11,	-12,	-12,	-9,	-9,	-10,	-10,
-	-15,	-15,	-16,	-16,	-13,	-13,	-14,	-14,
-	-86,	-82,	-94,	-90,	-70,	-66,	-78,	-74,
-	-118,	-114,	-126,	-122,	-102,	-98,	-110,	-106,
-	-43,	-41,	-47,	-45,	-35,	-33,	-39,	-37,
-	-59,	-57,	-63,	-61,	-51,	-49,	-55,	-53,
-	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
-	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-6,	-6,	-6,	-6,	-5,	-5,	-5,	-5,
-	-8,	-8,	-8,	-8,	-7,	-7,	-7,	-7,
-	-3,	-3,	-3,	-3,	-3,	-3,	-3,	-3,
-	-4,	-4,	-4,	-4,	-4,	-4,	-4,	-4,
-	21,	20,	23,	22,	17,	16,	19,	18,
-	29,	28,	31,	30,	25,	24,	27,	26,
-	10,	10,	11,	11,	8,	8,	9,	9,
-	14,	14,	15,	15,	12,	12,	13,	13,
-	86,	82,	94,	90,	70,	66,	78,	74,
-	118,	114,	126,	122,	102,	98,	110,	106,
-	43,	41,	47,	45,	35,	33,	39,	37,
-	59,	57,	63,	61,	51,	49,	55,	53,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	5,	5,	5,	5,	4,	4,	4,	4,
-	7,	7,	7,	7,	6,	6,	6,	6,
-	2,	2,	2,	2,	2,	2,	2,	2,
-	3,	3,	3,	3,	3,	3,	3,	3
-};
-#endif /* HAS_8BIT_TABLES */
-
-#ifdef HAS_16BIT_TABLES
-
-/* 16 bit mu-law */
-
-short dmasound_ulaw2dma16[] = {
-	-32124,	-31100,	-30076,	-29052,	-28028,	-27004,	-25980,	-24956,
-	-23932,	-22908,	-21884,	-20860,	-19836,	-18812,	-17788,	-16764,
-	-15996,	-15484,	-14972,	-14460,	-13948,	-13436,	-12924,	-12412,
-	-11900,	-11388,	-10876,	-10364,	-9852,	-9340,	-8828,	-8316,
-	-7932,	-7676,	-7420,	-7164,	-6908,	-6652,	-6396,	-6140,
-	-5884,	-5628,	-5372,	-5116,	-4860,	-4604,	-4348,	-4092,
-	-3900,	-3772,	-3644,	-3516,	-3388,	-3260,	-3132,	-3004,
-	-2876,	-2748,	-2620,	-2492,	-2364,	-2236,	-2108,	-1980,
-	-1884,	-1820,	-1756,	-1692,	-1628,	-1564,	-1500,	-1436,
-	-1372,	-1308,	-1244,	-1180,	-1116,	-1052,	-988,	-924,
-	-876,	-844,	-812,	-780,	-748,	-716,	-684,	-652,
-	-620,	-588,	-556,	-524,	-492,	-460,	-428,	-396,
-	-372,	-356,	-340,	-324,	-308,	-292,	-276,	-260,
-	-244,	-228,	-212,	-196,	-180,	-164,	-148,	-132,
-	-120,	-112,	-104,	-96,	-88,	-80,	-72,	-64,
-	-56,	-48,	-40,	-32,	-24,	-16,	-8,	0,
-	32124,	31100,	30076,	29052,	28028,	27004,	25980,	24956,
-	23932,	22908,	21884,	20860,	19836,	18812,	17788,	16764,
-	15996,	15484,	14972,	14460,	13948,	13436,	12924,	12412,
-	11900,	11388,	10876,	10364,	9852,	9340,	8828,	8316,
-	7932,	7676,	7420,	7164,	6908,	6652,	6396,	6140,
-	5884,	5628,	5372,	5116,	4860,	4604,	4348,	4092,
-	3900,	3772,	3644,	3516,	3388,	3260,	3132,	3004,
-	2876,	2748,	2620,	2492,	2364,	2236,	2108,	1980,
-	1884,	1820,	1756,	1692,	1628,	1564,	1500,	1436,
-	1372,	1308,	1244,	1180,	1116,	1052,	988,	924,
-	876,	844,	812,	780,	748,	716,	684,	652,
-	620,	588,	556,	524,	492,	460,	428,	396,
-	372,	356,	340,	324,	308,	292,	276,	260,
-	244,	228,	212,	196,	180,	164,	148,	132,
-	120,	112,	104,	96,	88,	80,	72,	64,
-	56,	48,	40,	32,	24,	16,	8,	0,
-};
-
-/* 16 bit A-law */
-
-short dmasound_alaw2dma16[] = {
-	-5504,	-5248,	-6016,	-5760,	-4480,	-4224,	-4992,	-4736,
-	-7552,	-7296,	-8064,	-7808,	-6528,	-6272,	-7040,	-6784,
-	-2752,	-2624,	-3008,	-2880,	-2240,	-2112,	-2496,	-2368,
-	-3776,	-3648,	-4032,	-3904,	-3264,	-3136,	-3520,	-3392,
-	-22016,	-20992,	-24064,	-23040,	-17920,	-16896,	-19968,	-18944,
-	-30208,	-29184,	-32256,	-31232,	-26112,	-25088,	-28160,	-27136,
-	-11008,	-10496,	-12032,	-11520,	-8960,	-8448,	-9984,	-9472,
-	-15104,	-14592,	-16128,	-15616,	-13056,	-12544,	-14080,	-13568,
-	-344,	-328,	-376,	-360,	-280,	-264,	-312,	-296,
-	-472,	-456,	-504,	-488,	-408,	-392,	-440,	-424,
-	-88,	-72,	-120,	-104,	-24,	-8,	-56,	-40,
-	-216,	-200,	-248,	-232,	-152,	-136,	-184,	-168,
-	-1376,	-1312,	-1504,	-1440,	-1120,	-1056,	-1248,	-1184,
-	-1888,	-1824,	-2016,	-1952,	-1632,	-1568,	-1760,	-1696,
-	-688,	-656,	-752,	-720,	-560,	-528,	-624,	-592,
-	-944,	-912,	-1008,	-976,	-816,	-784,	-880,	-848,
-	5504,	5248,	6016,	5760,	4480,	4224,	4992,	4736,
-	7552,	7296,	8064,	7808,	6528,	6272,	7040,	6784,
-	2752,	2624,	3008,	2880,	2240,	2112,	2496,	2368,
-	3776,	3648,	4032,	3904,	3264,	3136,	3520,	3392,
-	22016,	20992,	24064,	23040,	17920,	16896,	19968,	18944,
-	30208,	29184,	32256,	31232,	26112,	25088,	28160,	27136,
-	11008,	10496,	12032,	11520,	8960,	8448,	9984,	9472,
-	15104,	14592,	16128,	15616,	13056,	12544,	14080,	13568,
-	344,	328,	376,	360,	280,	264,	312,	296,
-	472,	456,	504,	488,	408,	392,	440,	424,
-	88,	72,	120,	104,	24,	8,	56,	40,
-	216,	200,	248,	232,	152,	136,	184,	168,
-	1376,	1312,	1504,	1440,	1120,	1056,	1248,	1184,
-	1888,	1824,	2016,	1952,	1632,	1568,	1760,	1696,
-	688,	656,	752,	720,	560,	528,	624,	592,
-	944,	912,	1008,	976,	816,	784,	880,	848,
-};
-#endif /* HAS_16BIT_TABLES */
-
-
-#ifdef HAS_14BIT_TABLES
-
-    /*
-     *  Unused for now. Where are the MSB parts anyway??
-     */
-
-/* 14 bit mu-law (LSB) */
-
-char dmasound_ulaw2dma14l[] = {
-	33,	33,	33,	33,	33,	33,	33,	33,
-	33,	33,	33,	33,	33,	33,	33,	33,
-	33,	33,	33,	33,	33,	33,	33,	33,
-	33,	33,	33,	33,	33,	33,	33,	33,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	49,	17,	49,	17,	49,	17,	49,	17,
-	49,	17,	49,	17,	49,	17,	49,	17,
-	41,	57,	9,	25,	41,	57,	9,	25,
-	41,	57,	9,	25,	41,	57,	9,	25,
-	37,	45,	53,	61,	5,	13,	21,	29,
-	37,	45,	53,	61,	5,	13,	21,	29,
-	35,	39,	43,	47,	51,	55,	59,	63,
-	3,	7,	11,	15,	19,	23,	27,	31,
-	34,	36,	38,	40,	42,	44,	46,	48,
-	50,	52,	54,	56,	58,	60,	62,	0,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	63,	63,	63,	63,	63,	63,	63,	63,
-	63,	63,	63,	63,	63,	63,	63,	63,
-	15,	47,	15,	47,	15,	47,	15,	47,
-	15,	47,	15,	47,	15,	47,	15,	47,
-	23,	7,	55,	39,	23,	7,	55,	39,
-	23,	7,	55,	39,	23,	7,	55,	39,
-	27,	19,	11,	3,	59,	51,	43,	35,
-	27,	19,	11,	3,	59,	51,	43,	35,
-	29,	25,	21,	17,	13,	9,	5,	1,
-	61,	57,	53,	49,	45,	41,	37,	33,
-	30,	28,	26,	24,	22,	20,	18,	16,
-	14,	12,	10,	8,	6,	4,	2,	0
-};
-
-/* 14 bit A-law (LSB) */
-
-char dmasound_alaw2dma14l[] = {
-	32,	32,	32,	32,	32,	32,	32,	32,
-	32,	32,	32,	32,	32,	32,	32,	32,
-	16,	48,	16,	48,	16,	48,	16,	48,
-	16,	48,	16,	48,	16,	48,	16,	48,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	42,	46,	34,	38,	58,	62,	50,	54,
-	10,	14,	2,	6,	26,	30,	18,	22,
-	42,	46,	34,	38,	58,	62,	50,	54,
-	10,	14,	2,	6,	26,	30,	18,	22,
-	40,	56,	8,	24,	40,	56,	8,	24,
-	40,	56,	8,	24,	40,	56,	8,	24,
-	20,	28,	4,	12,	52,	60,	36,	44,
-	20,	28,	4,	12,	52,	60,	36,	44,
-	32,	32,	32,	32,	32,	32,	32,	32,
-	32,	32,	32,	32,	32,	32,	32,	32,
-	48,	16,	48,	16,	48,	16,	48,	16,
-	48,	16,	48,	16,	48,	16,	48,	16,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	22,	18,	30,	26,	6,	2,	14,	10,
-	54,	50,	62,	58,	38,	34,	46,	42,
-	22,	18,	30,	26,	6,	2,	14,	10,
-	54,	50,	62,	58,	38,	34,	46,	42,
-	24,	8,	56,	40,	24,	8,	56,	40,
-	24,	8,	56,	40,	24,	8,	56,	40,
-	44,	36,	60,	52,	12,	4,	28,	20,
-	44,	36,	60,	52,	12,	4,	28,	20
-};
-#endif /* HAS_14BIT_TABLES */
-
+/* control over who can modify resources shared between play/record */
+static mode_t shared_resource_owner = 0 ;
+static int shared_resources_initialised = 0 ;
 
     /*
      *  Mid level stuff
@@ -393,15 +230,7 @@
 
 static inline void sound_silence(void)
 {
-	/* update hardware settings one more */
-	dmasound.mach.init();
-
-	dmasound.mach.silence();
-}
-
-static inline void sound_init(void)
-{
-	dmasound.mach.init();
+	dmasound.mach.silence(); /* _MUST_ stop DMA */
 }
 
 static inline int sound_set_format(int format)
@@ -414,8 +243,17 @@
 	if (speed < 0)
 		return dmasound.soft.speed;
 
+	/* trap out-of-range speed settings.
+	   at present we allow (arbitrarily) low rates - using soft
+	   up-conversion - but we can't allow > max because there is
+	   no soft down-conversion.
+	*/
+	if (dmasound.mach.max_dsp_speed &&
+	   (speed > dmasound.mach.max_dsp_speed))
+		speed = dmasound.mach.max_dsp_speed ;
+
 	dmasound.soft.speed = speed;
-	dmasound.mach.init();
+
 	if (dmasound.minDev == SND_DEV_DSP)
 		dmasound.dsp.speed = dmasound.soft.speed;
 
@@ -432,7 +270,6 @@
 	dmasound.soft.stereo = stereo;
 	if (dmasound.minDev == SND_DEV_DSP)
 		dmasound.dsp.stereo = stereo;
-	dmasound.mach.init();
 
 	return stereo;
 }
@@ -471,10 +308,14 @@
 	    default:
 		return 0;
 	}
-	return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
+	/* if the user has requested a non-existent translation don't try
+	   to call it but just return 0 bytes moved
+	*/
+	if (ct_func)
+		return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
+	return 0;
 }
 
-
     /*
      *  /dev/mixer abstraction
      */
@@ -559,6 +400,11 @@
 struct sound_queue dmasound_read_sq;
 #endif
 
+static void sq_reset_output(void) ;
+#ifdef HAS_RECORD
+static void sq_reset_input(void) ;
+#endif
+
 static int sq_allocate_buffers(struct sound_queue *sq, int num, int size)
 {
 	int i;
@@ -588,8 +434,6 @@
 	int i;
 
 	if (sq->buffers) {
-		if (sq != &write_sq && dmasound.mach.abort_read)
-			dmasound.mach.abort_read();
 		for (i = 0; i < sq->numBufs; i++)
 			dmasound.mach.dma_free(sq->buffers[i], sq->bufSize);
 		kfree(sq->buffers);
@@ -597,15 +441,84 @@
 	}
 }
 
-static void sq_setup(struct sound_queue *sq, int max_count, int max_active,
-		     int block_size)
+
+static int sq_setup(struct sound_queue *sq)
 {
-	void (*setup_func)(void);
+	int (*setup_func)(void);
+	int hard_frame ;
+
+	if (sq->locked) { /* are we already set? - and not changeable */
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: tried to sq_setup a locked queue\n") ;
+#endif
+		return -EINVAL ;
+	}
+	sq->locked = 1 ; /* don't think we have a race prob. here _check_ */
+
+	/* make sure that the parameters are set up
+	   This should have been done already...
+	*/
 
-	sq->max_count = max_count;
-	sq->max_active = max_active;
-	sq->block_size = block_size;
+	dmasound.mach.init();
+
+	/* OK.  If the user has set fragment parameters explicitly, then we
+	   should leave them alone... as long as they are valid.
+	   Invalid user fragment params can occur if we allow the whole buffer
+	   to be used when the user requests the fragments sizes (with no soft
+	   x-lation) and then the user subsequently sets a soft x-lation that
+	   requires increased internal buffering.
+
+	   Othwerwise (if the user did not set them) OSS says that we should
+	   select frag params on the basis of 0.5 s output & 0.1 s input
+	   latency. (TODO.  For now we will copy in the defaults.)
+	*/
 
+	if (sq->user_frags <= 0) {
+		sq->max_count = sq->numBufs ;
+		sq->max_active = sq->numBufs ;
+		sq->block_size = sq->bufSize;
+		/* set up the user info */
+		sq->user_frags = sq->numBufs ;
+		sq->user_frag_size = sq->bufSize ;
+		sq->user_frag_size *=
+			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
+		sq->user_frag_size /=
+			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
+	} else {
+		/* work out requested block size */
+		sq->block_size = sq->user_frag_size ;
+		sq->block_size *=
+			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
+		sq->block_size /=
+			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
+		/* the user wants to write frag-size chunks */
+		sq->block_size *= dmasound.hard.speed ;
+		sq->block_size /= dmasound.soft.speed ;
+		/* this only works for size values which are powers of 2 */
+		hard_frame =
+			(dmasound.hard.size * (dmasound.hard.stereo+1))/8 ;
+		sq->block_size +=  (hard_frame - 1) ;
+		sq->block_size &= ~(hard_frame - 1) ; /* make sure we are aligned */
+		/* let's just check for obvious mistakes */
+		if ( sq->block_size <= 0 || sq->block_size > sq->bufSize) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: invalid frag size (user set %d)\n", sq->user_frag_size) ;
+#endif
+			sq->block_size = sq->bufSize ;
+		}
+		if ( sq->user_frags <= sq->numBufs ) {
+			sq->max_count = sq->user_frags ;
+			/* if user has set max_active - then use it */
+			sq->max_active = (sq->max_active <= sq->max_count) ?
+				sq->max_active : sq->max_count ;
+		} else {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: invalid frag count (user set %d)\n", sq->user_frags) ;
+#endif
+			sq->max_count =
+			sq->max_active = sq->numBufs ;
+		}
+	}
 	sq->front = sq->count = sq->rear_size = 0;
 	sq->syncing = 0;
 	sq->active = 0;
@@ -613,12 +526,16 @@
 	if (sq == &write_sq) {
 	    sq->rear = -1;
 	    setup_func = dmasound.mach.write_sq_setup;
-	} else {
+	}
+#ifdef HAS_RECORD
+	else {
 	    sq->rear = 0;
 	    setup_func = dmasound.mach.read_sq_setup;
 	}
+#endif
 	if (setup_func)
-	    setup_func();
+	    return setup_func();
+	return 0 ;
 }
 
 static inline void sq_play(void)
@@ -632,6 +549,7 @@
 	ssize_t uWritten = 0;
 	u_char *dest;
 	ssize_t uUsed, bUsed, bLeft;
+	unsigned long flags ;
 
 	/* ++TeSche: Is something like this necessary?
 	 * Hey, that's an honest question! Or does any other part of the
@@ -640,11 +558,52 @@
 	if (uLeft == 0)
 		return 0;
 
+	/* implement any changes we have made to the soft/hard params.
+	   this is not satisfactory really, all we have done up to now is to
+	   say what we would like - there hasn't been any real checking of capability
+	*/
+
+	if (shared_resources_initialised == 0) {
+		dmasound.mach.init() ;
+		shared_resources_initialised = 1 ;
+	}
+
+	/* set up the sq if it is not already done. This may seem a dumb place
+	   to do it - but it is what OSS requires.  It means that write() can
+	   return memory allocation errors.  To avoid this possibility use the
+	   GETBLKSIZE or GETOSPACE ioctls (after you've fiddled with all the
+	   params you want to change) - these ioctls also force the setup.
+	*/
+
+	if (write_sq.locked == 0) {
+		if ((uWritten = sq_setup(&write_sq)) < 0) return uWritten ;
+		uWritten = 0 ;
+	}
+
+/* FIXME: I think that this may be the wrong behaviour when we get strapped
+	for time and the cpu is close to being (or actually) behind in sending data.
+	- because we've lost the time that the N samples, already in the buffer,
+	would have given us to get here with the next lot from the user.
+*/
 	/* The interrupt doesn't start to play the last, incomplete frame.
 	 * Thus we can append to it without disabling the interrupts! (Note
 	 * also that write_sq.rear isn't affected by the interrupt.)
 	 */
 
+	/* as of 1.6 this behaviour changes if SNDCTL_DSP_POST has been issued:
+	   this will mimic the behaviour of syncing and allow the sq_play() to
+	   queue a partial fragment.  Since sq_play() may/will be called from
+	   the IRQ handler - at least on Pmac we have to deal with it.
+	   The strategy - possibly not optimum - is to kill _POST status if we
+	   get here.  This seems, at least, reasonable - in the sense that POST
+	   is supposed to indicate that we might not write before the queue
+	   is drained - and if we get here in time then it does not apply.
+	*/
+
+	save_flags(flags) ; cli() ;
+	write_sq.syncing &= ~2 ; /* take out POST status */
+	restore_flags(flags) ;
+
 	if (write_sq.count > 0 &&
 	    (bLeft = write_sq.block_size-write_sq.rear_size) > 0) {
 		dest = write_sq.buffers[write_sq.rear];
@@ -655,12 +614,12 @@
 			return uUsed;
 		src += uUsed;
 		uWritten += uUsed;
-		uLeft -= uUsed;
+		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
 		write_sq.rear_size = bUsed;
 	}
 
-	do {
-		while (write_sq.count == write_sq.max_active) {
+	while (uLeft) {
+		while (write_sq.count >= write_sq.max_active) {
 			sq_play();
 			if (write_sq.open_mode & O_NONBLOCK)
 				return uWritten > 0 ? uWritten : -EAGAIN;
@@ -685,19 +644,45 @@
 			break;
 		src += uUsed;
 		uWritten += uUsed;
-		uLeft -= uUsed;
+		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
 		if (bUsed) {
 			write_sq.rear = (write_sq.rear+1) % write_sq.max_count;
 			write_sq.rear_size = bUsed;
 			write_sq.count++;
 		}
-	} while (bUsed);   /* uUsed may have been 0 */
+	} /* uUsed may have been 0 */
 
 	sq_play();
 
 	return uUsed < 0? uUsed: uWritten;
 }
 
+static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait)
+{
+	unsigned int mask = 0;
+	int retVal;
+	
+	if (write_sq.locked == 0) {
+		if ((retVal = sq_setup(&write_sq)) < 0)
+			return retVal;
+		return 0;
+	}
+	if (file->f_mode & FMODE_WRITE )
+		poll_wait(file, &write_sq.action_queue, wait);
+#ifdef HAS_RECORD
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &read_sq.action_queue, wait);
+	if (file->f_mode & FMODE_READ)
+		if (read_sq.block_size - read_sq.rear_size > 0)
+			mask |= POLLIN | POLLRDNORM;
+#endif
+	if (file->f_mode & FMODE_WRITE)
+		if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0)
+			mask |= POLLOUT | POLLWRNORM;
+	return mask;
+
+}
+
 #ifdef HAS_RECORD
     /*
      *  Here is how the values are used for reading.
@@ -707,9 +692,12 @@
      *  The value 'rear' indicates the buffer the DMA is currently filling.
      *  When 'front' == 'rear' the buffer "ring" is empty (we always have an
      *  empty available).  The 'rear_size' is used to track partial offsets
-     *  into the current buffer.  Right now, I just keep the DMA running.  If
-     *  the reader can't keep up, the interrupt tosses the oldest buffer.  We
-     *  could also shut down the DMA in this case.
+     *  into the buffer we are currently returning to the user.
+
+     *  This level (> [1.5]) doesn't care what strategy the LL driver uses with
+     *  DMA on over-run.  It can leave it running (and keep active == 1) or it
+     *  can kill it and set active == 0 in which case this routine will spot
+     *  it and restart the DMA.
      */
 
 static ssize_t sq_read(struct file *file, char *dst, size_t uLeft,
@@ -721,8 +709,25 @@
 	if (uLeft == 0)
 		return 0;
 
-	if (!read_sq.active && dmasound.mach.record)
-		dmasound.mach.record();	/* Kick off the record process. */
+	/* cater for the compatibility mode - record compiled in but no LL */
+	if (dmasound.mach.record == NULL)
+		return -EINVAL ;
+
+	/* see comment in sq_write()
+	*/
+
+	if( shared_resources_initialised == 0) {
+		dmasound.mach.init() ;
+		shared_resources_initialised = 1 ;
+	}
+
+	/* set up the sq if it is not already done. see comments in sq_write().
+	*/
+
+	if (read_sq.locked == 0) {
+		if ((uRead = sq_setup(&read_sq)) < 0)
+			return uRead ;
+	}
 
 	uRead = 0;
 
@@ -730,6 +735,13 @@
 	*/
 	while (uLeft > 0) {
 
+		/* we happened to get behind and the LL driver killed DMA
+		   then we should set it going again.  This also sets it
+		   going the first time through.
+		*/
+		if ( !read_sq.active )
+			dmasound.mach.record();
+
 		/* When front == rear, the DMA is not done yet.
 		*/
 		while (read_sq.front == read_sq.rear) {
@@ -774,14 +786,16 @@
 	sq->busy = 0;
 }
 
+#if 0 /* blocking open() */
 static inline void sq_wake_up(struct sound_queue *sq, struct file *file,
 			      mode_t mode)
 {
 	if (file->f_mode & mode) {
-		sq->busy = 0;
+		sq->busy = 0; /* CHECK: IS THIS OK??? */
 		WAKE_UP(sq->open_queue);
 	}
 }
+#endif
 
 static int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode,
 		    int numbufs, int bufsize)
@@ -790,6 +804,7 @@
 
 	if (file->f_mode & mode) {
 		if (sq->busy) {
+#if 0 /* blocking open() */
 			rc = -EBUSY;
 			if (file->f_flags & O_NONBLOCK)
 				return rc;
@@ -800,84 +815,188 @@
 					return rc;
 			}
 			rc = 0;
+#else
+			/* OSS manual says we will return EBUSY regardless
+			   of O_NOBLOCK.
+			*/
+			return -EBUSY ;
+#endif
 		}
 		sq->busy = 1; /* Let's play spot-the-race-condition */
 
-		if (sq_allocate_buffers(sq, numbufs, bufsize)) {
+		/* allocate the default number & size of buffers.
+		   (i.e. specified in _setup() or as module params)
+		   can't be changed at the moment - but _could_ be perhaps
+		   in the setfragments ioctl.
+		*/
+		if (( rc = sq_allocate_buffers(sq, numbufs, bufsize))) {
+#if 0 /* blocking open() */
 			sq_wake_up(sq, file, mode);
+#else
+			sq->busy = 0 ;
+#endif
 			return rc;
 		}
 
-		sq_setup(sq, numbufs, numbufs, bufsize);
 		sq->open_mode = file->f_mode;
 	}
 	return rc;
 }
 
 #define write_sq_init_waitqueue()	sq_init_waitqueue(&write_sq)
+#if 0 /* blocking open() */
 #define write_sq_wake_up(file)		sq_wake_up(&write_sq, file, FMODE_WRITE)
+#endif
 #define write_sq_release_buffers()	sq_release_buffers(&write_sq)
 #define write_sq_open(file)	\
-	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize << 10)
+	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize )
 
 #ifdef HAS_RECORD
 #define read_sq_init_waitqueue()	sq_init_waitqueue(&read_sq)
+#if 0 /* blocking open() */
 #define read_sq_wake_up(file)		sq_wake_up(&read_sq, file, FMODE_READ)
+#endif
 #define read_sq_release_buffers()	sq_release_buffers(&read_sq)
 #define read_sq_open(file)	\
-	sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize << 10)
-#else /* !HAS_RECORD */
-#define read_sq_init_waitqueue()	do {} while (0)
-#define read_sq_wake_up(file)		do {} while (0)
-#define read_sq_release_buffers()	do {} while (0)
-#define read_sq_open(file)		(0)
-#endif /* !HAS_RECORD */
+	sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize )
+#endif
 
 static int sq_open(struct inode *inode, struct file *file)
 {
 	int rc;
 
 	dmasound.mach.open();
-	if ((rc = write_sq_open(file)) || (rc = read_sq_open(file))) {
+
+	if ((rc = write_sq_open(file))) { /* checks the f_mode */
 		dmasound.mach.release();
 		return rc;
 	}
+#ifdef HAS_RECORD
+	if (dmasound.mach.record) {
+		if ((rc = read_sq_open(file))) { /* checks the f_mode */
+			dmasound.mach.release();
+			return rc;
+		}
+	} else { /* no record function installed; in compat mode */
+		if (file->f_mode & FMODE_READ) {
+			/* TODO: if O_RDWR, release any resources grabbed by write part */
+			dmasound.mach.release() ;
+			/* I think this is what is required by open(2) */
+			return -ENXIO ;
+		}
+	}
+#else /* !HAS_RECORD */
+	if (file->f_mode & FMODE_READ) {
+		/* TODO: if O_RDWR, release any resources grabbed by write part */
+		dmasound.mach.release() ;
+		return -ENXIO ; /* I think this is what is required by open(2) */
+	}
+#endif /* HAS_RECORD */
 
 	if (dmasound.mach.sq_open)
-	    dmasound.mach.sq_open();
+	    dmasound.mach.sq_open(file->f_mode);
+
+	/* CHECK whether this is sensible - in the case that dsp0 could be opened
+	  O_RDONLY and dsp1 could be opened O_WRONLY
+	*/
+
 	dmasound.minDev = MINOR(inode->i_rdev) & 0x0f;
-	dmasound.soft = dmasound.dsp;
-	dmasound.hard = dmasound.dsp;
-	sound_init();
-	if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) {
+
+	/* OK. - we should make some attempt at consistency. At least the H'ware
+	   options should be set with a valid mode.  We will make it that the LL
+	   driver must supply defaults for hard & soft params.
+	*/
+
+	if (shared_resource_owner == 0) {
+		/* you can make this AFMT_U8/mono/8K if you want to mimic old
+		   OSS behaviour - while we still have soft translations ;-) */
+		dmasound.soft = dmasound.mach.default_soft ;
+		dmasound.dsp = dmasound.mach.default_soft ;
+		dmasound.hard = dmasound.mach.default_hard ;
+	}
+
+#ifndef DMASOUND_STRICT_OSS_COMPLIANCE
+	/* none of the current LL drivers can actually do this "native" at the moment
+	   OSS does not really require us to supply /dev/audio if we can't do it.
+	*/
+	if (dmasound.minDev == SND_DEV_AUDIO) {
 		sound_set_speed(8000);
 		sound_set_stereo(0);
 		sound_set_format(AFMT_MU_LAW);
 	}
-
-#if 0
-	if (file->f_mode == FMODE_READ && dmasound.mach.record) {
-		/* Start dma'ing straight away */
-		dmasound.mach.record();
-	}
 #endif
 
 	return 0;
 }
 
-static void sq_reset(void)
+static void sq_reset_output(void)
 {
-	sound_silence();
+	sound_silence(); /* this _must_ stop DMA, we might be about to lose the buffers */
 	write_sq.active = 0;
 	write_sq.count = 0;
-	write_sq.front = (write_sq.rear+1) % write_sq.max_count;
+	write_sq.rear_size = 0;
+	/* write_sq.front = (write_sq.rear+1) % write_sq.max_count;*/
+	write_sq.front = 0 ;
+	write_sq.rear = -1 ; /* same as for set-up */
+
+	/* OK - we can unlock the parameters and fragment settings */
+	write_sq.locked = 0 ;
+	write_sq.user_frags = 0 ;
+	write_sq.user_frag_size = 0 ;
+}
+
+#ifdef HAS_RECORD
+
+static void sq_reset_input(void)
+{
+	if (dmasound.mach.record && read_sq.active) {
+		if (dmasound.mach.abort_read) { /* this routine must really be present */
+			read_sq.syncing = 1 ;
+			/* this can use the read_sq.sync_queue to sleep if
+			   necessary - it should not return until DMA
+			   is really stopped - because we might deallocate
+			   the buffers as the next action...
+			*/
+			dmasound.mach.abort_read() ;
+		} else {
+			printk(KERN_ERR
+			"dmasound_core: %s has no abort_read()!! all bets are off\n",
+				dmasound.mach.name) ;
+		}
+	}
+	read_sq.syncing =
+	read_sq.active =
+	read_sq.front =
+	read_sq.count =
+	read_sq.rear = 0 ;
+
+	/* OK - we can unlock the parameters and fragment settings */
+	read_sq.locked = 0 ;
+	read_sq.user_frags = 0 ;
+	read_sq.user_frag_size = 0 ;
+}
+
+#endif
+
+static void sq_reset(void)
+{
+	sq_reset_output() ;
+#ifdef HAS_RECORD
+	sq_reset_input() ;
+#endif
+	/* we could consider resetting the shared_resources_owner here... but I
+	   think it is probably still rather non-obvious to application writer
+	*/
+
+	/* we release everything else though */
+	shared_resources_initialised = 0 ;
 }
 
 static int sq_fsync(struct file *filp, struct dentry *dentry)
 {
 	int rc = 0;
 
-	write_sq.syncing = 1;
+	write_sq.syncing |= 1;
 	sq_play();	/* there may be an incomplete frame waiting */
 
 	while (write_sq.active) {
@@ -886,13 +1005,14 @@
 			/* While waiting for audio output to drain, an
 			 * interrupt occurred.  Stop audio output immediately
 			 * and clear the queue. */
-			sq_reset();
+			sq_reset_output();
 			rc = -EINTR;
 			break;
 		}
 	}
 
-	write_sq.syncing = 0;
+	/* flag no sync regardless of whether we had a DSP_POST or not */
+	write_sq.syncing = 0 ;
 	return rc;
 }
 
@@ -901,33 +1021,132 @@
 	int rc = 0;
 
 	lock_kernel();
-	if (write_sq.busy)
-		rc = sq_fsync(file, file->f_dentry);
-	dmasound.soft = dmasound.dsp;
-	dmasound.hard = dmasound.dsp;
-	sound_silence();
 
-	write_sq_release_buffers();
-	read_sq_release_buffers();
-	dmasound.mach.release();
+#ifdef HAS_RECORD
+	/* probably best to do the read side first - so that time taken to do it
+	   overlaps with playing any remaining output samples.
+	*/
+	if (file->f_mode & FMODE_READ) {
+		sq_reset_input() ; /* make sure dma is stopped and all is quiet */
+		read_sq_release_buffers();
+		read_sq.busy = 0;
+	}
+#endif
 
-	/* There is probably a DOS atack here. They change the mode flag. */
-	/* XXX add check here */
-	read_sq_wake_up(file);
-	write_sq_wake_up(file);
+	if (file->f_mode & FMODE_WRITE) {
+		if (write_sq.busy)
+			rc = sq_fsync(file, file->f_dentry);
+
+		sq_reset_output() ; /* make sure dma is stopped and all is quiet */
+		write_sq_release_buffers();
+		write_sq.busy = 0;
+	}
 
+	if (file->f_mode & shared_resource_owner) { /* it's us that has them */
+		shared_resource_owner = 0 ;
+		shared_resources_initialised = 0 ;
+		dmasound.hard = dmasound.mach.default_hard ;
+	}
+
+	dmasound.mach.release();
+
+#if 0 /* blocking open() */
 	/* Wake up a process waiting for the queue being released.
 	 * Note: There may be several processes waiting for a call
 	 * to open() returning. */
+
+	/* Iain: hmm I don't understand this next comment ... */
+	/* There is probably a DOS atack here. They change the mode flag. */
+	/* XXX add check here,*/
+#ifdef HAS_RECORD
+	read_sq_wake_up(file); /* checks f_mode */
+#endif
+	write_sq_wake_up(file); /* checks f_mode */
+#endif /* blocking open() */
+
 	unlock_kernel();
 
 	return rc;
 }
 
+/* here we see if we have a right to modify format, channels, size and so on
+   if no-one else has claimed it already then we do...
+
+   TODO: We might change this to mask O_RDWR such that only one or the other channel
+   is the owner - if we have problems.
+*/
+
+static int shared_resources_are_mine(mode_t md)
+{
+	if (shared_resource_owner)
+		return (shared_resource_owner & md ) ;
+	else {
+		shared_resource_owner = md ;
+		return 1 ;
+	}
+}
+
+/* if either queue is locked we must deny the right to change shared params
+*/
+
+static int queues_are_quiescent(void)
+{
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		if (read_sq.locked)
+			return 0 ;
+#endif
+	if (write_sq.locked)
+		return 0 ;
+	return 1 ;
+}
+
+/* check and set a queue's fragments per user's wishes...
+   we will check against the pre-defined literals and the actual sizes.
+   This is a bit fraught - because soft translations can mess with our
+   buffer requirements *after* this call - OSS says "call setfrags first"
+*/
+
+/* It is possible to replace all the -EINVAL returns with an override that
+   just puts the allowable value in.  This may be what many OSS apps require
+*/
+
+static int set_queue_frags(struct sound_queue *sq, int bufs, int size)
+{
+	if (sq->locked) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: tried to set_queue_frags on a locked queue\n") ;
+#endif
+		return -EINVAL ;
+	}
+
+	if ((size < MIN_FRAG_SIZE) || (size > MAX_FRAG_SIZE))
+		return -EINVAL ;
+	size = (1<<size) ; /* now in bytes */
+	if (size > sq->bufSize)
+		return -EINVAL ; /* this might still not work */
+
+	if (bufs <= 0)
+		return -EINVAL ;
+	if (bufs > sq->numBufs) /* the user is allowed say "don't care" with 0x7fff */
+		bufs = sq->numBufs ;
+
+	/* there is, currently, no way to specify max_active separately
+	   from max_count.  This could be a LL driver issue - I guess
+	   if there is a requirement for these values to be different then
+	  we will have to pass that info. up to this level.
+	*/
+	sq->user_frags =
+	sq->max_active = bufs ;
+	sq->user_frag_size = size ;
+
+	return 0 ;
+}
+
 static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd,
 		    u_long arg)
 {
-	int val;
+	int val, result;
 	u_long fmt;
 	int data;
 	int size, nbufs;
@@ -937,85 +1156,167 @@
 	case SNDCTL_DSP_RESET:
 		sq_reset();
 		return 0;
+		break ;
+	case SNDCTL_DSP_GETFMTS:
+		fmt = dmasound.mach.hardware_afmts ; /* this is what OSS says.. */
+		return IOCTL_OUT(arg, fmt);
+		break ;
+	case SNDCTL_DSP_GETBLKSIZE:
+		/* this should tell the caller about bytes that the app can
+		   read/write - the app doesn't care about our internal buffers.
+		   We force sq_setup() here as per OSS 1.1 (which should
+		   compute the values necessary).
+		   Since there is no mechanism to specify read/write separately, for
+		   fds opened O_RDWR, the write_sq values will, arbitrarily, overwrite
+		   the read_sq ones.
+		*/
+		size = 0 ;
+#ifdef HAS_RECORD
+		if (dmasound.mach.record && (file->f_mode & FMODE_READ)) {
+			if ( !read_sq.locked )
+				sq_setup(&read_sq) ; /* set params */
+			size = read_sq.user_frag_size ;
+		}
+#endif
+		if (file->f_mode & FMODE_WRITE) {
+			if ( !write_sq.locked )
+				sq_setup(&write_sq) ;
+			size = write_sq.user_frag_size ;
+		}
+		return IOCTL_OUT(arg, size);
+		break ;
 	case SNDCTL_DSP_POST:
+		/* all we are going to do is to tell the LL that any
+		   partial frags can be queued for output.
+		   The LL will have to clear this flag when last output
+		   is queued.
+		*/
+		write_sq.syncing |= 0x2 ;
+		sq_play() ;
+		return 0 ;
 	case SNDCTL_DSP_SYNC:
-		return sq_fsync(file, file->f_dentry);
-
-		/* ++TeSche: before changing any of these it's
-		 * probably wise to wait until sound playing has
-		 * settled down. */
+		/* This call, effectively, has the same behaviour as SNDCTL_DSP_RESET
+		   except that it waits for output to finish before resetting
+		   everything - read, however, is killed imediately.
+		*/
+		result = 0 ;
+#ifdef HAS_RECORD
+		if ((file->f_mode & FMODE_READ) && dmasound.mach.record)
+			sq_reset_input() ;
+#endif
+		if (file->f_mode & FMODE_WRITE) {
+			result = sq_fsync(file, file->f_dentry);
+			sq_reset_output() ;
+		}
+		/* if we are the shared resource owner then release them */
+		if (file->f_mode & shared_resource_owner)
+			shared_resources_initialised = 0 ;
+		return result ;
+		break ;
 	case SNDCTL_DSP_SPEED:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_speed(data));
+		/* changing this on the fly will have wierd effects on the sound.
+		   Where there are rate conversions implemented in soft form - it
+		   will cause the _ctx_xxx() functions to be substituted.
+		   However, there doesn't appear to be any reason to dis-allow it from
+		   a driver pov.
+		*/
+		if (shared_resources_are_mine(file->f_mode)) {
+			IOCTL_IN(arg, data);
+			data = sound_set_speed(data) ;
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, data);
+		} else
+			return -EINVAL ;
+		break ;
+	/* OSS says these next 4 actions are undefined when the device is
+	   busy/active - we will just return -EINVAL.
+	   To be allowed to change one - (a) you have to own the right
+	    (b) the queue(s) must be quiescent
+	*/
 	case SNDCTL_DSP_STEREO:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_stereo(data));
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+			IOCTL_IN(arg, data);
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, sound_set_stereo(data));
+		} else
+			return -EINVAL ;
+		break ;
 	case SOUND_PCM_WRITE_CHANNELS:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+			IOCTL_IN(arg, data);
+			/* the user might ask for 20 channels, we will return 1 or 2 */
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
+		} else
+			return -EINVAL ;
+		break ;
 	case SNDCTL_DSP_SETFMT:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_format(data));
-	case SNDCTL_DSP_GETFMTS:
-		fmt = 0;
-		if (dmasound.trans_write) {
-			if (dmasound.trans_write->ct_ulaw)
-				fmt |= AFMT_MU_LAW;
-			if (dmasound.trans_write->ct_alaw)
-				fmt |= AFMT_A_LAW;
-			if (dmasound.trans_write->ct_s8)
-				fmt |= AFMT_S8;
-			if (dmasound.trans_write->ct_u8)
-				fmt |= AFMT_U8;
-			if (dmasound.trans_write->ct_s16be)
-				fmt |= AFMT_S16_BE;
-			if (dmasound.trans_write->ct_u16be)
-				fmt |= AFMT_U16_BE;
-			if (dmasound.trans_write->ct_s16le)
-				fmt |= AFMT_S16_LE;
-			if (dmasound.trans_write->ct_u16le)
-				fmt |= AFMT_U16_LE;
-		}
-		return IOCTL_OUT(arg, fmt);
-	case SNDCTL_DSP_GETBLKSIZE:
-		size = write_sq.block_size
-			* dmasound.soft.size * (dmasound.soft.stereo + 1)
-			/ (dmasound.hard.size * (dmasound.hard.stereo + 1));
-		return IOCTL_OUT(arg, size);
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+		    	int format;
+			IOCTL_IN(arg, data);
+			shared_resources_initialised = 0 ;
+			format = sound_set_format(data);
+			result = IOCTL_OUT(arg, format);
+			if (result < 0)
+				return result;
+			if (format != data)
+				return -EINVAL;
+			return 0;
+		} else
+			return -EINVAL ;
+		break ;
 	case SNDCTL_DSP_SUBDIVIDE:
+		return -EINVAL ;
 		break;
 	case SNDCTL_DSP_SETFRAGMENT:
-		if (write_sq.count || write_sq.active || write_sq.syncing)
-			return -EINVAL;
-		IOCTL_IN(arg, size);
-		nbufs = size >> 16;
-		if (nbufs < 2 || nbufs > write_sq.numBufs)
-			nbufs = write_sq.numBufs;
-		size &= 0xffff;
-		if (size >= 8 && size <= 29) {
-			size = 1 << size;
-			size *= dmasound.hard.size * (dmasound.hard.stereo + 1);
-			size /= dmasound.soft.size * (dmasound.soft.stereo + 1);
-			if (size > write_sq.bufSize)
-				size = write_sq.bufSize;
-		} else
-			size = write_sq.bufSize;
-		sq_setup(&write_sq, write_sq.numBufs, nbufs, size);
-		return IOCTL_OUT(arg,write_sq.bufSize | write_sq.numBufs << 16);
+		/* we can do this independently for the two queues - with the
+		   proviso that for fds opened O_RDWR we cannot separate the
+		   actions and both queues will be set per the last call.
+		   NOTE: this does *NOT* actually set the queue up - merely
+		   registers our intentions.
+		*/
+		IOCTL_IN(arg, data);
+		result = 0 ;
+		nbufs = (data >> 16) & 0x7fff ; /* 0x7fff is 'use maximum' */
+		size = data & 0xffff;
+#ifdef HAS_RECORD
+		if ((file->f_mode & FMODE_READ) && dmasound.mach.record) {
+			result = set_queue_frags(&read_sq, nbufs, size) ;
+			if (result)
+				return result ;
+		}
+#endif
+		if (file->f_mode & FMODE_WRITE) {
+			result = set_queue_frags(&write_sq, nbufs, size) ;
+			if (result)
+				return result ;
+		}
+		/* NOTE: this return value is irrelevant - OSS specifically says that
+		   the value is 'random' and that the user _must_ check the actual
+		   frags values using SNDCTL_DSP_GETBLKSIZE or similar */
+		return IOCTL_OUT(arg, data);
+		break ;
 	case SNDCTL_DSP_GETOSPACE:
-		info.fragments = write_sq.max_active - write_sq.count;
-		info.fragstotal = write_sq.max_active;
-		info.fragsize = write_sq.block_size;
-		info.bytes = info.fragments * info.fragsize;
-		if (copy_to_user((void *)arg, &info, sizeof(info)))
-			return -EFAULT;
-		return 0;
+		/*
+		*/
+		if (file->f_mode & FMODE_WRITE) {
+			if ( !write_sq.locked )
+				sq_setup(&write_sq) ;
+			info.fragments = write_sq.max_active - write_sq.count;
+			info.fragstotal = write_sq.max_active;
+			info.fragsize = write_sq.user_frag_size;
+			info.bytes = info.fragments * info.fragsize;
+			if (copy_to_user((void *)arg, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EINVAL ;
+		break ;
 	case SNDCTL_DSP_GETCAPS:
-		val = 1;        /* Revision level of this ioctl() */
+		val = dmasound.mach.capabilities & 0xffffff00;
 		return IOCTL_OUT(arg,val);
 
 	default:
@@ -1029,42 +1330,48 @@
 	owner:		THIS_MODULE,
 	llseek:		no_llseek,
 	write:		sq_write,
+	poll:		sq_poll,
 	ioctl:		sq_ioctl,
 	open:		sq_open,
 	release:	sq_release,
 #ifdef HAS_RECORD
-	read:		sq_read,
+	read:		NULL	/* default to no read for compat mode */
 #endif
 };
 
-static void __init sq_init(void)
+static int __init sq_init(void)
 {
 #ifndef MODULE
 	int sq_unit;
 #endif
+
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		sq_fops.read = sq_read ;
+#endif
 	sq_unit = register_sound_dsp(&sq_fops, -1);
-	if (sq_unit < 0)
-		return;
+	if (sq_unit < 0) {
+		printk(KERN_ERR "dmasound_core: couldn't register fops\n") ;
+		return sq_unit ;
+	}
 
 	write_sq_init_waitqueue();
+#ifdef HAS_RECORD
 	read_sq_init_waitqueue();
+#endif
 
-	/* whatever you like as startup mode for /dev/dsp,
-	 * (/dev/audio hasn't got a startup mode). note that
-	 * once changed a new open() will *not* restore these!
+	/* These parameters will be restored for every clean open()
+	 * in the case of multiple open()s (e.g. dsp0 & dsp1) they
+	 * will be set so long as the shared resources have no owner.
 	 */
-	dmasound.dsp.format = AFMT_U8;
-	dmasound.dsp.stereo = 0;
-	dmasound.dsp.size = 8;
-
-	/* set minimum rate possible without expanding */
-	dmasound.dsp.speed = dmasound.mach.min_dsp_speed;
-
-	/* before the first open to /dev/dsp this wouldn't be set */
-	dmasound.soft = dmasound.dsp;
-	dmasound.hard = dmasound.dsp;
 
-	sound_silence();
+	if (shared_resource_owner == 0) {
+		dmasound.soft = dmasound.mach.default_soft ;
+		dmasound.hard = dmasound.mach.default_hard ;
+		dmasound.dsp = dmasound.mach.default_soft ;
+		shared_resources_initialised = 0 ;
+	}
+	return 0 ;
 }
 
 
@@ -1072,12 +1379,68 @@
      *  /dev/sndstat
      */
 
+/* we allow more space for record-enabled because there are extra output lines.
+   the number here must include the amount we are prepared to give to the low-level
+   driver.
+*/
+
+#ifdef HAS_RECORD
+#define STAT_BUFF_LEN 1024
+#else
+#define STAT_BUFF_LEN 768
+#endif
+
+/* this is how much space we will allow the low-level driver to use
+   in the stat buffer.  Currently, 2 * (80 character line + <NL>).
+   We do not police this (it is up to the ll driver to be honest).
+*/
+
+#define LOW_LEVEL_STAT_ALLOC 162
+
 static struct {
     int busy;
-    char buf[512];	/* state.buf should not overflow! */
+    char buf[STAT_BUFF_LEN];	/* state.buf should not overflow! */
     int len, ptr;
 } state;
 
+/* publish this function for use by low-level code, if required */
+
+char *get_afmt_string(int afmt)
+{
+        switch(afmt) {
+            case AFMT_MU_LAW:
+                return "mu-law";
+                break;
+            case AFMT_A_LAW:
+                return "A-law";
+                break;
+            case AFMT_U8:
+                return "unsigned 8 bit";
+                break;
+            case AFMT_S8:
+                return "signed 8 bit";
+                break;
+            case AFMT_S16_BE:
+                return "signed 16 bit BE";
+                break;
+            case AFMT_U16_BE:
+                return "unsigned 16 bit BE";
+                break;
+            case AFMT_S16_LE:
+                return "signed 16 bit LE";
+                break;
+            case AFMT_U16_LE:
+                return "unsigned 16 bit LE";
+                break;
+	    case 0:
+		return "format not set" ;
+		break ;
+            default:
+                break ;
+        }
+        return "ERROR: Unsupported AFMT_XXXX code" ;
+}
+
 static int state_open(struct inode *inode, struct file *file)
 {
 	char *buffer = state.buf;
@@ -1090,52 +1453,76 @@
 	state.ptr = 0;
 	state.busy = 1;
 
-	len += sprintf(buffer+len, "%sDMA sound driver:\n", dmasound.mach.name);
+	len += sprintf(buffer+len, "%sDMA sound driver rev %03d :\n",
+		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
+		((dmasound.mach.version>>8) & 0x0f));
+	len += sprintf(buffer+len,
+		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
+		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
+		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
+
+	/* call the low-level module to fill in any stat info. that it has
+	   if present.  Maximum buffer usage is specified.
+	*/
 
-	len += sprintf(buffer+len, "\tsound.format = 0x%x",
-		       dmasound.soft.format);
-	switch (dmasound.soft.format) {
-	    case AFMT_MU_LAW:
-		len += sprintf(buffer+len, " (mu-law)");
-		break;
-	    case AFMT_A_LAW:
-		len += sprintf(buffer+len, " (A-law)");
-		break;
-	    case AFMT_U8:
-		len += sprintf(buffer+len, " (unsigned 8 bit)");
-		break;
-	    case AFMT_S8:
-		len += sprintf(buffer+len, " (signed 8 bit)");
-		break;
-	    case AFMT_S16_BE:
-		len += sprintf(buffer+len, " (signed 16 bit big)");
-		break;
-	    case AFMT_U16_BE:
-		len += sprintf(buffer+len, " (unsigned 16 bit big)");
-		break;
-	    case AFMT_S16_LE:
-		len += sprintf(buffer+len, " (signed 16 bit little)");
-		break;
-	    case AFMT_U16_LE:
-		len += sprintf(buffer+len, " (unsigned 16 bit little)");
-		break;
-	}
-	len += sprintf(buffer+len, "\n");
-	len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n",
-		       dmasound.soft.speed, dmasound.hard.speed);
-	len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n",
-		       dmasound.soft.stereo,
-		       dmasound.soft.stereo ? "stereo" : "mono");
 	if (dmasound.mach.state_info)
-	    len += dmasound.mach.state_info(buffer);
-	len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d"
-		       " sq.max_active = %d\n",
-		       write_sq.block_size, write_sq.max_count,
-		       write_sq.max_active);
-	len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n",
-		       write_sq.count, write_sq.rear_size);
-	len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n",
-		       write_sq.active, write_sq.syncing);
+		len += dmasound.mach.state_info(buffer+len,
+			(size_t) LOW_LEVEL_STAT_ALLOC) ;
+
+	/* make usage of the state buffer as deterministic as poss.
+	   exceptional conditions could cause overrun - and this is flagged as
+	   a kernel error.
+	*/
+
+	/* formats and settings */
+
+	len += sprintf(buffer+len,"\t\t === Formats & settings ===\n") ;
+	len += sprintf(buffer+len,"Parameter %20s%20s\n","soft","hard") ;
+	len += sprintf(buffer+len,"Format   :%20s%20s\n",
+		get_afmt_string(dmasound.soft.format),
+		get_afmt_string(dmasound.hard.format));
+
+	len += sprintf(buffer+len,"Samp Rate:%14d s/sec%14d s/sec\n",
+		       dmasound.soft.speed, dmasound.hard.speed);
+
+	len += sprintf(buffer+len,"Channels :%20s%20s\n",
+		       dmasound.soft.stereo ? "stereo" : "mono",
+		       dmasound.hard.stereo ? "stereo" : "mono" );
+
+	/* sound queue status */
+
+	len += sprintf(buffer+len,"\t\t === Sound Queue status ===\n");
+	len += sprintf(buffer+len,"Allocated:%8s%6s\n","Buffers","Size") ;
+	len += sprintf(buffer+len,"%9s:%8d%6d\n",
+		"write", write_sq.numBufs, write_sq.bufSize) ;
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		len += sprintf(buffer+len,"%9s:%8d%6d\n",
+			"read", read_sq.numBufs, read_sq.bufSize) ;
+#endif
+	len += sprintf(buffer+len,
+		"Current  : MaxFrg FragSiz MaxAct Frnt Rear "
+		"Cnt RrSize A B S L  xruns\n") ;
+	len += sprintf(buffer+len,"%9s:%7d%8d%7d%5d%5d%4d%7d%2d%2d%2d%2d%7d\n",
+		"write", write_sq.max_count, write_sq.block_size,
+		write_sq.max_active, write_sq.front, write_sq.rear,
+		write_sq.count, write_sq.rear_size, write_sq.active,
+		write_sq.busy, write_sq.syncing, write_sq.locked, write_sq.xruns) ;
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		len += sprintf(buffer+len,"%9s:%7d%8d%7d%5d%5d%4d%7d%2d%2d%2d%2d%7d\n",
+			"read", read_sq.max_count, read_sq.block_size,
+			read_sq.max_active, read_sq.front, read_sq.rear,
+			read_sq.count, read_sq.rear_size, read_sq.active,
+			read_sq.busy, read_sq.syncing, read_sq.locked, read_sq.xruns) ;
+#endif
+#ifdef DEBUG_DMASOUND
+printk("dmasound: stat buffer used %d bytes\n", len) ;
+#endif
+
+	if (len >= STAT_BUFF_LEN)
+		printk(KERN_ERR "dmasound_core: stat buffer overflowed!\n");
+
 	state.len = len;
 	return 0;
 }
@@ -1171,15 +1558,16 @@
 	release:	state_release,
 };
 
-static void __init state_init(void)
+static int __init state_init(void)
 {
 #ifndef MODULE
 	int state_unit;
 #endif
 	state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
 	if (state_unit < 0)
-		return;
+		return state_unit ;
 	state.busy = 0;
+	return 0 ;
 }
 
 
@@ -1191,6 +1579,7 @@
 
 int __init dmasound_init(void)
 {
+	int res ;
 #ifdef MODULE
 	if (irq_installed)
 		return -EBUSY;
@@ -1199,10 +1588,12 @@
 	/* Set up sound queue, /dev/audio and /dev/dsp. */
 
 	/* Set default settings. */
-	sq_init();
+	if ((res = sq_init()) < 0)
+		return res ;
 
 	/* Set up /dev/sndstat. */
-	state_init();
+	if ((res = state_init()) < 0)
+		return res ;
 
 	/* Set up /dev/mixer. */
 	mixer_init();
@@ -1215,8 +1606,21 @@
 	irq_installed = 1;
 #endif
 
-	printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n",
-	       numWriteBufs, writeBufSize);
+	printk(KERN_INFO "%s DMA sound driver rev %03d installed\n",
+		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
+		((dmasound.mach.version>>8) & 0x0f));
+	printk(KERN_INFO
+		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
+		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
+		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
+	printk(KERN_INFO "Write will use %4d fragments of %7d bytes as default\n",
+		numWriteBufs, writeBufSize) ;
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		printk(KERN_INFO
+			"Read  will use %4d fragments of %7d bytes as default\n",
+			numReadBufs, readBufSize) ;
+#endif
 
 	return 0;
 }
@@ -1228,6 +1632,7 @@
 	if (irq_installed) {
 		sound_silence();
 		dmasound.mach.irqcleanup();
+		irq_installed = 0;
 	}
 
 	write_sq_release_buffers();
@@ -1245,29 +1650,60 @@
 
 static int __init dmasound_setup(char *str)
 {
-	int ints[6];
+	int ints[6], size;
 
 	str = get_options(str, ARRAY_SIZE(ints), ints);
 
 	/* check the bootstrap parameter for "dmasound=" */
 
+	/* FIXME: other than in the most naive of cases there is no sense in these
+	 *	  buffers being other than powers of two.  This is not checked yet.
+	 */
+
 	switch (ints[0]) {
+#ifdef HAS_RECORD
+        case 5:
+                if ((ints[5] < 0) || (ints[5] > MAX_CATCH_RADIUS))
+                        printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
+                else
+                        catchRadius = ints[5];
+                /* fall through */
+        case 4:
+                if (ints[4] < MIN_BUFFERS)
+                        printk("dmasound_setup: illegal number of read buffers, using default = %d\n",
+                                 numReadBufs);
+                else
+                        numReadBufs = ints[4];
+                /* fall through */
+        case 3:
+		if ((size = ints[3]) < 256)  /* check for small buffer specs */
+			size <<= 10 ;
+                if (size < MIN_BUFSIZE || size > MAX_BUFSIZE)
+                        printk("dmasound_setup: illegal read buffer size, using default = %d\n", readBufSize);
+                else
+                        readBufSize = size;
+                /* fall through */
+#else
 	case 3:
 		if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
 			printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
 		else
 			catchRadius = ints[3];
 		/* fall through */
+#endif
 	case 2:
 		if (ints[1] < MIN_BUFFERS)
 			printk("dmasound_setup: illegal number of buffers, using default = %d\n", numWriteBufs);
 		else
 			numWriteBufs = ints[1];
-		if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE)
-			printk("dmasound_setup: illegal buffer size, using default = %dK\n", writeBufSize);
-		else
-			writeBufSize = ints[2];
-		break;
+		/* fall through */
+	case 1:
+		if ((size = ints[2]) < 256) /* check for small buffer specs */
+			size <<= 10 ;
+                if (size < MIN_BUFSIZE || size > MAX_BUFSIZE)
+                        printk("dmasound_setup: illegal write buffer size, using default = %d\n", writeBufSize);
+                else
+                        writeBufSize = size;
 	case 0:
 		break;
 	default:
@@ -1281,6 +1717,85 @@
 
 #endif /* !MODULE */
 
+    /*
+     *  Conversion tables
+     */
+
+#ifdef HAS_8BIT_TABLES
+/* 8 bit mu-law */
+
+char dmasound_ulaw2dma8[] = {
+	-126,	-122,	-118,	-114,	-110,	-106,	-102,	-98,
+	-94,	-90,	-86,	-82,	-78,	-74,	-70,	-66,
+	-63,	-61,	-59,	-57,	-55,	-53,	-51,	-49,
+	-47,	-45,	-43,	-41,	-39,	-37,	-35,	-33,
+	-31,	-30,	-29,	-28,	-27,	-26,	-25,	-24,
+	-23,	-22,	-21,	-20,	-19,	-18,	-17,	-16,
+	-16,	-15,	-15,	-14,	-14,	-13,	-13,	-12,
+	-12,	-11,	-11,	-10,	-10,	-9,	-9,	-8,
+	-8,	-8,	-7,	-7,	-7,	-7,	-6,	-6,
+	-6,	-6,	-5,	-5,	-5,	-5,	-4,	-4,
+	-4,	-4,	-4,	-4,	-3,	-3,	-3,	-3,
+	-3,	-3,	-3,	-3,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	0,
+	125,	121,	117,	113,	109,	105,	101,	97,
+	93,	89,	85,	81,	77,	73,	69,	65,
+	62,	60,	58,	56,	54,	52,	50,	48,
+	46,	44,	42,	40,	38,	36,	34,	32,
+	30,	29,	28,	27,	26,	25,	24,	23,
+	22,	21,	20,	19,	18,	17,	16,	15,
+	15,	14,	14,	13,	13,	12,	12,	11,
+	11,	10,	10,	9,	9,	8,	8,	7,
+	7,	7,	6,	6,	6,	6,	5,	5,
+	5,	5,	4,	4,	4,	4,	3,	3,
+	3,	3,	3,	3,	2,	2,	2,	2,
+	2,	2,	2,	2,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0
+};
+
+/* 8 bit A-law */
+
+char dmasound_alaw2dma8[] = {
+	-22,	-21,	-24,	-23,	-18,	-17,	-20,	-19,
+	-30,	-29,	-32,	-31,	-26,	-25,	-28,	-27,
+	-11,	-11,	-12,	-12,	-9,	-9,	-10,	-10,
+	-15,	-15,	-16,	-16,	-13,	-13,	-14,	-14,
+	-86,	-82,	-94,	-90,	-70,	-66,	-78,	-74,
+	-118,	-114,	-126,	-122,	-102,	-98,	-110,	-106,
+	-43,	-41,	-47,	-45,	-35,	-33,	-39,	-37,
+	-59,	-57,	-63,	-61,	-51,	-49,	-55,	-53,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-6,	-6,	-6,	-6,	-5,	-5,	-5,	-5,
+	-8,	-8,	-8,	-8,	-7,	-7,	-7,	-7,
+	-3,	-3,	-3,	-3,	-3,	-3,	-3,	-3,
+	-4,	-4,	-4,	-4,	-4,	-4,	-4,	-4,
+	21,	20,	23,	22,	17,	16,	19,	18,
+	29,	28,	31,	30,	25,	24,	27,	26,
+	10,	10,	11,	11,	8,	8,	9,	9,
+	14,	14,	15,	15,	12,	12,	13,	13,
+	86,	82,	94,	90,	70,	66,	78,	74,
+	118,	114,	126,	122,	102,	98,	110,	106,
+	43,	41,	47,	45,	35,	33,	39,	37,
+	59,	57,	63,	61,	51,	49,	55,	53,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	5,	5,	5,	5,	4,	4,	4,	4,
+	7,	7,	7,	7,	6,	6,	6,	6,
+	2,	2,	2,	2,	2,	2,	2,	2,
+	3,	3,	3,	3,	3,	3,	3,	3
+};
+#endif /* HAS_8BIT_TABLES */
 
     /*
      *  Visible symbols for modules
@@ -1300,14 +1815,4 @@
 EXPORT_SYMBOL(dmasound_ulaw2dma8);
 EXPORT_SYMBOL(dmasound_alaw2dma8);
 #endif
-#ifdef HAS_16BIT_TABLES
-EXPORT_SYMBOL(dmasound_ulaw2dma16);
-EXPORT_SYMBOL(dmasound_alaw2dma16);
-#endif
-#ifdef HAS_14BIT_TABLES
-EXPORT_SYMBOL(dmasound_ulaw2dma14l);
-EXPORT_SYMBOL(dmasound_ulaw2dma14h);
-EXPORT_SYMBOL(dmasound_alaw2dma14l);
-EXPORT_SYMBOL(dmasound_alaw2dma14h);
-#endif
-
+EXPORT_SYMBOL(get_afmt_string) ;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)