Alsa-Devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Tring to support RME32 full-duplex
@ 2004-06-18 15:17 Takashi Iwai
  0 siblings, 0 replies; only message in thread
From: Takashi Iwai @ 2004-06-18 15:17 UTC (permalink / raw)
  To: alsa-devel

[-- Attachment #1: Type: text/plain, Size: 241 bytes --]

Hi,

(I believe this interests mainly Martin.)

The patch below adds (hopefully) the support the full-duplex on
RME32.  You'll need to applay the mmap-iomem patch before this one.

If you have an RME32, please test and debug it ;)


Takashi

[-- Attachment #2: Type: text/plain, Size: 35454 bytes --]

Index: alsa-kernel/pci/rme32.c
===================================================================
RCS file: /suse/tiwai/cvs/alsa/alsa-kernel/pci/rme32.c,v
retrieving revision 1.36
diff -u -r1.36 rme32.c
--- alsa-kernel/pci/rme32.c	18 Jun 2004 13:57:32 -0000	1.36
+++ alsa-kernel/pci/rme32.c	18 Jun 2004 15:10:48 -0000
@@ -52,6 +52,19 @@
  * patch would be welcome!
  * 
  * ****************************************************************************
+ *
+ * "The story after the long seeking" -- tiwai
+ *
+ * Ok, the situation regarding the full duplex is now improved a bit.
+ * In the fullduplex mode (given by the module parameter), the hardware buffer
+ * is split to halves for read and write directions at the DMA pointer.
+ * That is, the half above the current DMA pointer is used for write, and
+ * the half below is used for read.  To mangle this strange behavior, an
+ * software intermediate buffer is introduced.  This is, of course, not good
+ * from the viewpoint of the data transfer efficiency.  However, this allows
+ * you to use arbitrary buffer sizes, instead of the fixed I/O buffer size.
+ *
+ * ****************************************************************************
  */
 
 
@@ -68,6 +81,7 @@
 #include <sound/control.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/pcm-indirect.h>
 #include <sound/asoundef.h>
 #include <sound/initval.h>
 
@@ -76,6 +90,7 @@
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int fullduplex[SNDRV_CARDS]; // = {[0 ... (SNDRV_CARDS - 1)] = 1};
 static int boot_devs;
 
 module_param_array(index, int, boot_devs, 0444);
@@ -87,6 +102,8 @@
 module_param_array(enable, bool, boot_devs, 0444);
 MODULE_PARM_DESC(enable, "Enable RME Digi32 soundcard.");
 MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
+module_param_array(fullduplex, bool, boot_devs, 0444);
+MODULE_PARM_DESC(fullduplex, "Support full-duplex mode.");
 MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
 MODULE_DESCRIPTION("RME Digi32, Digi32/8, Digi32 PRO");
 MODULE_LICENSE("GPL");
@@ -168,6 +185,9 @@
 /* Block sizes in bytes */
 #define RME32_BLOCK_SIZE 8192
 
+/* Software intermediate buffer (max) size */
+#define RME32_MID_BUFFER_SIZE (1024*1024)
+
 /* Hardware revisions */
 #define RME32_32_REVISION 192
 #define RME32_328_REVISION_OLD 100
@@ -213,6 +233,12 @@
 	size_t playback_periodsize;	/* in bytes, zero if not used */
 	size_t capture_periodsize;	/* in bytes, zero if not used */
 
+	unsigned int fullduplex_mode;
+	int running;
+
+	snd_pcm_indirect_t playback_pcm;
+	snd_pcm_indirect_t capture_pcm;
+
 	snd_card_t *card;
 	snd_pcm_t *spdif_pcm;
 	snd_pcm_t *adat_pcm;
@@ -239,33 +265,16 @@
 
 static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream);
 
-static int
-snd_rme32_playback_trigger(snd_pcm_substream_t * substream, int cmd);
-
-static int
-snd_rme32_capture_trigger(snd_pcm_substream_t * substream, int cmd);
-
-static snd_pcm_uframes_t
-snd_rme32_playback_pointer(snd_pcm_substream_t * substream);
-
-static snd_pcm_uframes_t
-snd_rme32_capture_pointer(snd_pcm_substream_t * substream);
+static int snd_rme32_pcm_trigger(snd_pcm_substream_t * substream, int cmd);
 
 static void snd_rme32_proc_init(rme32_t * rme32);
 
 static int snd_rme32_create_switches(snd_card_t * card, rme32_t * rme32);
 
-static inline unsigned int snd_rme32_playback_ptr(rme32_t * rme32)
-{
-
-	return (readl(rme32->iobase + RME32_IO_GET_POS)
-		& RME32_RCR_AUDIO_ADDR_MASK) >> rme32->playback_frlog;
-}
-
-static inline unsigned int snd_rme32_capture_ptr(rme32_t * rme32)
+static inline unsigned int snd_rme32_pcm_byteptr(rme32_t * rme32)
 {
 	return (readl(rme32->iobase + RME32_IO_GET_POS)
-		& RME32_RCR_AUDIO_ADDR_MASK) >> rme32->capture_frlog;
+		& RME32_RCR_AUDIO_ADDR_MASK);
 }
 
 static int snd_rme32_ratecode(int rate)
@@ -281,6 +290,7 @@
 	return 0;
 }
 
+/* silence callback for halfduplex mode */
 static int snd_rme32_playback_silence(snd_pcm_substream_t * substream, int channel,	/* not used (interleaved data) */
 				      snd_pcm_uframes_t pos,
 				      snd_pcm_uframes_t count)
@@ -292,6 +302,7 @@
 	return 0;
 }
 
+/* copy callback for halfduplex mode */
 static int snd_rme32_playback_copy(snd_pcm_substream_t * substream, int channel,	/* not used (interleaved data) */
 				   snd_pcm_uframes_t pos,
 				   void *src, snd_pcm_uframes_t count)
@@ -305,6 +316,7 @@
 	return 0;
 }
 
+/* copy callback for halfduplex mode */
 static int snd_rme32_capture_copy(snd_pcm_substream_t * substream, int channel,	/* not used (interleaved data) */
 				  snd_pcm_uframes_t pos,
 				  void *dst, snd_pcm_uframes_t count)
@@ -320,14 +332,15 @@
 }
 
 /*
- * Digital output capabilites (S/PDIF)
+ * SPDIF I/O capabilites (half-duplex mode)
  */
-static snd_pcm_hardware_t snd_rme32_playback_spdif_info = {
+static snd_pcm_hardware_t snd_rme32_spdif_info = {
 	.info =		(SNDRV_PCM_INFO_MMAP |
 			 SNDRV_PCM_INFO_MMAP_VALID |
 			 SNDRV_PCM_INFO_MMAP_IOMEM |
 			 SNDRV_PCM_INFO_INTERLEAVED | 
-			 SNDRV_PCM_INFO_PAUSE),
+			 SNDRV_PCM_INFO_PAUSE |
+			 SNDRV_PCM_INFO_SYNC_START),
 	.formats =	(SNDRV_PCM_FMTBIT_S16_LE | 
 			 SNDRV_PCM_FMTBIT_S32_LE),
 	.rates =	(SNDRV_PCM_RATE_32000 |
@@ -346,41 +359,16 @@
 };
 
 /*
- * Digital input capabilites (S/PDIF)
+ * ADAT I/O capabilites (half-duplex mode)
  */
-static snd_pcm_hardware_t snd_rme32_capture_spdif_info = {
-	.info =		(SNDRV_PCM_INFO_MMAP |
-			 SNDRV_PCM_INFO_MMAP_VALID |
-			 SNDRV_PCM_INFO_MMAP_IOMEM |
-			 SNDRV_PCM_INFO_INTERLEAVED | 
-			 SNDRV_PCM_INFO_PAUSE),
-	.formats =	(SNDRV_PCM_FMTBIT_S16_LE | 
-			 SNDRV_PCM_FMTBIT_S32_LE),
-	.rates =	(SNDRV_PCM_RATE_32000 |
-			 SNDRV_PCM_RATE_44100 | 
-			 SNDRV_PCM_RATE_48000),
-	.rate_min =	32000,
-	.rate_max =	48000,
-	.channels_min =	2,
-	.channels_max =	2,
-	.buffer_bytes_max = RME32_BUFFER_SIZE,
-	.period_bytes_min = RME32_BLOCK_SIZE,
-	.period_bytes_max = RME32_BLOCK_SIZE,
-	.periods_min =	RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
-	.periods_max =	RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
-	.fifo_size =	0,
-};
-
-/*
- * Digital output capabilites (ADAT)
- */
-static snd_pcm_hardware_t snd_rme32_playback_adat_info =
+static snd_pcm_hardware_t snd_rme32_adat_info =
 {
 	.info =		     (SNDRV_PCM_INFO_MMAP |
 			      SNDRV_PCM_INFO_MMAP_VALID |
 			      SNDRV_PCM_INFO_MMAP_IOMEM |
 			      SNDRV_PCM_INFO_INTERLEAVED |
-			      SNDRV_PCM_INFO_PAUSE),
+			      SNDRV_PCM_INFO_PAUSE |
+			      SNDRV_PCM_INFO_SYNC_START),
 	.formats=            SNDRV_PCM_FMTBIT_S16_LE,
 	.rates =             (SNDRV_PCM_RATE_44100 | 
 			      SNDRV_PCM_RATE_48000),
@@ -397,28 +385,54 @@
 };
 
 /*
- * Digital input capabilites (ADAT)
+ * SPDIF I/O capabilites (full-duplex mode)
+ */
+static snd_pcm_hardware_t snd_rme32_spdif_fd_info = {
+	.info =		(SNDRV_PCM_INFO_MMAP |
+			 SNDRV_PCM_INFO_MMAP_VALID |
+			 SNDRV_PCM_INFO_INTERLEAVED | 
+			 SNDRV_PCM_INFO_PAUSE |
+			 SNDRV_PCM_INFO_SYNC_START),
+	.formats =	(SNDRV_PCM_FMTBIT_S16_LE | 
+			 SNDRV_PCM_FMTBIT_S32_LE),
+	.rates =	(SNDRV_PCM_RATE_32000 |
+			 SNDRV_PCM_RATE_44100 | 
+			 SNDRV_PCM_RATE_48000),
+	.rate_min =	32000,
+	.rate_max =	48000,
+	.channels_min =	2,
+	.channels_max =	2,
+	.buffer_bytes_max = RME32_MID_BUFFER_SIZE,
+	.period_bytes_min = RME32_BLOCK_SIZE,
+	.period_bytes_max = RME32_BLOCK_SIZE,
+	.periods_min =	2,
+	.periods_max =	RME32_MID_BUFFER_SIZE / RME32_BLOCK_SIZE,
+	.fifo_size =	0,
+};
+
+/*
+ * ADAT I/O capabilites (full-duplex mode)
  */
-static snd_pcm_hardware_t snd_rme32_capture_adat_info =
+static snd_pcm_hardware_t snd_rme32_adat_fd_info =
 {
 	.info =		     (SNDRV_PCM_INFO_MMAP |
 			      SNDRV_PCM_INFO_MMAP_VALID |
-			      SNDRV_PCM_INFO_MMAP_IOMEM |
 			      SNDRV_PCM_INFO_INTERLEAVED |
-			      SNDRV_PCM_INFO_PAUSE),
-	.formats =           SNDRV_PCM_FMTBIT_S16_LE,
+			      SNDRV_PCM_INFO_PAUSE |
+			      SNDRV_PCM_INFO_SYNC_START),
+	.formats=            SNDRV_PCM_FMTBIT_S16_LE,
 	.rates =             (SNDRV_PCM_RATE_44100 | 
 			      SNDRV_PCM_RATE_48000),
 	.rate_min =          44100,
 	.rate_max =          48000,
-	.channels_min =	     8,
+	.channels_min =      8,
 	.channels_max =	     8,
-	.buffer_bytes_max =  RME32_BUFFER_SIZE,
+	.buffer_bytes_max =  RME32_MID_BUFFER_SIZE,
 	.period_bytes_min =  RME32_BLOCK_SIZE,
 	.period_bytes_max =  RME32_BLOCK_SIZE,
-	.periods_min =	     RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
-	.periods_max =	     RME32_BUFFER_SIZE / RME32_BLOCK_SIZE,
-	.fifo_size =         0,
+	.periods_min =	    2,
+	.periods_max =	    RME32_MID_BUFFER_SIZE / RME32_BLOCK_SIZE,
+	.fifo_size =	    0,
 };
 
 static void snd_rme32_reset_dac(rme32_t *rme32)
@@ -680,9 +694,15 @@
 	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
 
-	runtime->dma_area = (void *)(rme32->iobase + RME32_IO_DATA_BUFFER);
-	runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER;
-	runtime->dma_bytes = RME32_BUFFER_SIZE;
+	if (rme32->fullduplex_mode) {
+		err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+		if (err < 0)
+			return err;
+	} else {
+		runtime->dma_area = (void *)(rme32->iobase + RME32_IO_DATA_BUFFER);
+		runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER;
+		runtime->dma_bytes = RME32_BUFFER_SIZE;
+	}
 
 	spin_lock_irq(&rme32->lock);
 	if ((rme32->rcreg & RME32_RCR_KMODE) &&
@@ -730,9 +750,15 @@
 	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
 
-	runtime->dma_area = (void *)(rme32->iobase + RME32_IO_DATA_BUFFER);
-	runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER;
-	runtime->dma_bytes = RME32_BUFFER_SIZE;
+	if (rme32->fullduplex_mode) {
+		err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+		if (err < 0)
+			return err;
+	} else {
+		runtime->dma_area = (void *)rme32->iobase + RME32_IO_DATA_BUFFER;
+		runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER;
+		runtime->dma_bytes = RME32_BUFFER_SIZE;
+	}
 
 	spin_lock_irqsave(&rme32->lock, flags);
 	/* enable AutoSync for record-preparing */
@@ -777,17 +803,15 @@
 	return 0;
 }
 
-static void snd_rme32_playback_start(rme32_t * rme32, int from_pause)
+static int snd_rme32_pcm_hw_free(snd_pcm_substream_t * substream)
 {
-	if (!from_pause) {
-		writel(0, rme32->iobase + RME32_IO_RESET_POS);
-	}
-
-	rme32->wcreg |= RME32_WCR_START;
-	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	if (! rme32->fullduplex_mode)
+		return 0;
+	return snd_pcm_lib_free_pages(substream);
 }
 
-static void snd_rme32_capture_start(rme32_t * rme32, int from_pause)
+static void snd_rme32_pcm_start(rme32_t * rme32, int from_pause)
 {
 	if (!from_pause) {
 		writel(0, rme32->iobase + RME32_IO_RESET_POS);
@@ -797,7 +821,7 @@
 	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
 }
 
-static void snd_rme32_playback_stop(rme32_t * rme32)
+static void snd_rme32_pcm_stop(rme32_t * rme32)
 {
 	/*
 	 * Check if there is an unconfirmed IRQ, if so confirm it, or else
@@ -811,16 +835,7 @@
 	if (rme32->wcreg & RME32_WCR_SEL)
 		rme32->wcreg |= RME32_WCR_MUTE;
 	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
-}
-
-static void snd_rme32_capture_stop(rme32_t * rme32)
-{
-	rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER);
-	if (rme32->rcreg & RME32_RCR_IRQ) {
-		writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ);
-	}
-	rme32->wcreg &= ~RME32_WCR_START;
-	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
+	writel(0, rme32->iobase + RME32_IO_RESET_POS);
 }
 
 static irqreturn_t
@@ -854,6 +869,18 @@
 	.mask = 0
 };
 
+static void snd_rme32_set_buffer_constraint(rme32_t *rme32, snd_pcm_runtime_t *runtime)
+{
+	if (! rme32->fullduplex_mode) {
+		snd_pcm_hw_constraint_minmax(runtime,
+					     SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+					     RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
+		snd_pcm_hw_constraint_list(runtime, 0,
+					   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+					   &hw_constraints_period_bytes);
+	}
+}
+
 static int snd_rme32_playback_spdif_open(snd_pcm_substream_t * substream)
 {
 	unsigned long flags;
@@ -873,7 +900,10 @@
 	rme32->playback_substream = substream;
 	spin_unlock_irqrestore(&rme32->lock, flags);
 
-	runtime->hw = snd_rme32_playback_spdif_info;
+	if (rme32->fullduplex_mode)
+		runtime->hw = snd_rme32_spdif_fd_info;
+	else
+		runtime->hw = snd_rme32_spdif_info;
 	if (rme32->pci->device == PCI_DEVICE_ID_DIGI32_PRO) {
 		runtime->hw.rates |= SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000;
 		runtime->hw.rate_max = 96000;
@@ -885,12 +915,8 @@
 		runtime->hw.rate_min = rate;
 		runtime->hw.rate_max = rate;
 	}       
-	snd_pcm_hw_constraint_minmax(runtime,
-				     SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-				     RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
-	snd_pcm_hw_constraint_list(runtime, 0,
-				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-				   &hw_constraints_period_bytes);
+
+	snd_rme32_set_buffer_constraint(rme32, runtime);
 
 	rme32->wcreg_spdif_stream = rme32->wcreg_spdif;
 	rme32->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
@@ -916,7 +942,10 @@
 	rme32->capture_substream = substream;
 	spin_unlock_irqrestore(&rme32->lock, flags);
 
-	runtime->hw = snd_rme32_capture_spdif_info;
+	if (rme32->fullduplex_mode)
+		runtime->hw = snd_rme32_spdif_fd_info;
+	else
+		runtime->hw = snd_rme32_spdif_info;
 	if (RME32_PRO_WITH_8414(rme32)) {
 		runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000;
 		runtime->hw.rate_max = 96000;
@@ -930,12 +959,7 @@
 		runtime->hw.rate_max = rate;
 	}
 
-	snd_pcm_hw_constraint_minmax(runtime,
-				     SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-				     RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
-	snd_pcm_hw_constraint_list(runtime, 0,
-				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-				   &hw_constraints_period_bytes);
+	snd_rme32_set_buffer_constraint(rme32, runtime);
 
 	return 0;
 }
@@ -960,7 +984,10 @@
 	rme32->playback_substream = substream;
 	spin_unlock_irqrestore(&rme32->lock, flags);
 	
-	runtime->hw = snd_rme32_playback_adat_info;
+	if (rme32->fullduplex_mode)
+		runtime->hw = snd_rme32_adat_fd_info;
+	else
+		runtime->hw = snd_rme32_adat_info;
 	if ((rme32->rcreg & RME32_RCR_KMODE) &&
 	    (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) {
                 /* AutoSync */
@@ -968,10 +995,8 @@
                 runtime->hw.rate_min = rate;
                 runtime->hw.rate_max = rate;
 	}        
-	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-				     RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-				   &hw_constraints_period_bytes);
+
+	snd_rme32_set_buffer_constraint(rme32, runtime);
 	return 0;
 }
 
@@ -983,7 +1008,10 @@
 	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
 
-	runtime->hw = snd_rme32_capture_adat_info;
+	if (rme32->fullduplex_mode)
+		runtime->hw = snd_rme32_adat_fd_info;
+	else
+		runtime->hw = snd_rme32_adat_info;
 	if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) {
 		if (!isadat) {
 			return -EIO;
@@ -1003,10 +1031,7 @@
 	rme32->capture_substream = substream;
 	spin_unlock_irqrestore(&rme32->lock, flags);
 
-	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-				     RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-				   &hw_constraints_period_bytes);
+	snd_rme32_set_buffer_constraint(rme32, runtime);
 	return 0;
 }
 
@@ -1045,139 +1070,174 @@
 static int snd_rme32_playback_prepare(snd_pcm_substream_t * substream)
 {
 	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
-	unsigned long flags;
 
-	spin_lock_irqsave(&rme32->lock, flags);
-	if (RME32_ISWORKING(rme32)) {
-		snd_rme32_playback_stop(rme32);
+	spin_lock(&rme32->lock);
+	if (rme32->fullduplex_mode) {
+		memset(&rme32->playback_pcm, 0, sizeof(rme32->playback_pcm));
+		rme32->playback_pcm.hw_buffer_size = RME32_BUFFER_SIZE;
+		rme32->playback_pcm.hw_queue_size = RME32_BUFFER_SIZE / 2;
+		rme32->playback_pcm.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	} else {
+		writel(0, rme32->iobase + RME32_IO_RESET_POS);
 	}
-	writel(0, rme32->iobase + RME32_IO_RESET_POS);
 	if (rme32->wcreg & RME32_WCR_SEL)
 		rme32->wcreg &= ~RME32_WCR_MUTE;
 	writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER);
-	spin_unlock_irqrestore(&rme32->lock, flags);
+	spin_unlock(&rme32->lock);
 	return 0;
 }
 
 static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream)
 {
 	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
-	unsigned long flags;
 
-	spin_lock_irqsave(&rme32->lock, flags);
-	if (RME32_ISWORKING(rme32)) {
-		snd_rme32_capture_stop(rme32);
+	spin_lock(&rme32->lock);
+	if (rme32->fullduplex_mode) {
+		memset(&rme32->capture_pcm, 0, sizeof(rme32->capture_pcm));
+		rme32->capture_pcm.hw_buffer_size = RME32_BUFFER_SIZE;
+		rme32->capture_pcm.hw_queue_size = RME32_BUFFER_SIZE / 2;
+		rme32->capture_pcm.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	} else {
+		writel(0, rme32->iobase + RME32_IO_RESET_POS);
 	}
-	writel(0, rme32->iobase + RME32_IO_RESET_POS);
-	spin_unlock_irqrestore(&rme32->lock, flags);
+	spin_unlock(&rme32->lock);
 	return 0;
 }
 
 static int
-snd_rme32_playback_trigger(snd_pcm_substream_t * substream, int cmd)
+snd_rme32_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
 {
 	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		if (!RME32_ISWORKING(rme32)) {
-			if (substream != rme32->playback_substream) {
-				return -EBUSY;
+	struct list_head *pos;
+	snd_pcm_substream_t *s;
+
+	spin_lock(&rme32->lock);
+	snd_pcm_group_for_each(pos, substream) {
+		s = snd_pcm_group_substream_entry(pos);
+		if (s != rme32->playback_substream &&
+		    s != rme32->capture_substream)
+			continue;
+		switch (cmd) {
+		case SNDRV_PCM_TRIGGER_START:
+			rme32->running++;
+			if (rme32->fullduplex_mode) {
+				/* remember the current DMA position */
+				if (s == rme32->playback_substream) {
+					rme32->playback_pcm.hw_data = snd_rme32_pcm_byteptr(rme32);
+					s->ops->ack(s); /* prefill buffer */
+				} else {
+					rme32->capture_pcm.hw_data = snd_rme32_pcm_byteptr(rme32);
+				}
 			}
-			snd_rme32_playback_start(rme32, 0);
+			break;
+		case SNDRV_PCM_TRIGGER_STOP:
+			rme32->running--;
+			break;
 		}
+		snd_pcm_trigger_done(s, substream);
+	}
+	
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (rme32->running && ! RME32_ISWORKING(rme32))
+			snd_rme32_pcm_start(rme32, 0);
 		break;
-
 	case SNDRV_PCM_TRIGGER_STOP:
-		if (RME32_ISWORKING(rme32)) {
-			if (substream != rme32->playback_substream) {
-				return -EBUSY;
-			}
-			snd_rme32_playback_stop(rme32);
-		}
+		if (! rme32->running && RME32_ISWORKING(rme32))
+			snd_rme32_pcm_stop(rme32);
 		break;
-
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		if (RME32_ISWORKING(rme32)) {
-			snd_rme32_playback_stop(rme32);
-		}
+		if (rme32->running && RME32_ISWORKING(rme32))
+			snd_rme32_pcm_stop(rme32);
 		break;
-
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		if (!RME32_ISWORKING(rme32)) {
-			snd_rme32_playback_start(rme32, 1);
-		}
+		if (rme32->running && ! RME32_ISWORKING(rme32))
+			snd_rme32_pcm_start(rme32, 1);
 		break;
-
-	default:
-		return -EINVAL;
 	}
+	spin_unlock(&rme32->lock);
 	return 0;
 }
 
-static int
-snd_rme32_capture_trigger(snd_pcm_substream_t * substream, int cmd)
+/* pointer callback for halfduplex mode */
+static snd_pcm_uframes_t
+snd_rme32_playback_pointer(snd_pcm_substream_t * substream)
 {
 	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	return snd_rme32_pcm_byteptr(rme32) >> rme32->playback_frlog;
+}
 
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		if (!RME32_ISWORKING(rme32)) {
-			if (substream != rme32->capture_substream) {
-				return -EBUSY;
-			}
-			snd_rme32_capture_start(rme32, 0);
-		}
-		break;
+static snd_pcm_uframes_t
+snd_rme32_capture_pointer(snd_pcm_substream_t * substream)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	return snd_rme32_pcm_byteptr(rme32) >> rme32->capture_frlog;
+}
 
-	case SNDRV_PCM_TRIGGER_STOP:
-		if (RME32_ISWORKING(rme32)) {
-			if (substream != rme32->capture_substream) {
-				return -EBUSY;
-			}
-			snd_rme32_capture_stop(rme32);
-		}
-		break;
 
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		if (RME32_ISWORKING(rme32)) {
-			snd_rme32_capture_stop(rme32);
-		}
-		break;
+/* ack and pointer callbacks for fullduplex mode */
+static void snd_rme32_pb_trans_copy(snd_pcm_substream_t *substream,
+				    snd_pcm_indirect_t *rec, size_t bytes)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + rec->hw_data,
+		    substream->runtime->dma_area + rec->sw_data, bytes);
+}
 
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		if (!RME32_ISWORKING(rme32)) {
-			snd_rme32_capture_start(rme32, 1);
-		}
-		break;
+static int snd_rme32_playback_fd_ack(snd_pcm_substream_t *substream)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	snd_pcm_indirect_playback_transfer(substream, &rme32->playback_pcm,
+					   snd_rme32_pb_trans_copy);
+	return 0;
+}
 
-	default:
-		return -EINVAL;
-	}
+static void snd_rme32_cp_trans_copy(snd_pcm_substream_t *substream,
+				    snd_pcm_indirect_t *rec, size_t bytes)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	memcpy_fromio(substream->runtime->dma_area + rec->sw_data,
+		      rme32->iobase + RME32_IO_DATA_BUFFER + rec->hw_data,
+		      bytes);
+}
 
+static int snd_rme32_capture_fd_ack(snd_pcm_substream_t *substream)
+{
+	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
+	snd_pcm_indirect_capture_transfer(substream, &rme32->capture_pcm,
+					  snd_rme32_cp_trans_copy);
 	return 0;
 }
 
 static snd_pcm_uframes_t
-snd_rme32_playback_pointer(snd_pcm_substream_t * substream)
+snd_rme32_playback_fd_pointer(snd_pcm_substream_t * substream)
 {
 	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
-	return snd_rme32_playback_ptr(rme32);
+	size_t ptr;
+
+	ptr = readl(rme32->iobase + RME32_IO_GET_POS) & RME32_RCR_AUDIO_ADDR_MASK;
+	return snd_pcm_indirect_playback_pointer(substream, &rme32->playback_pcm, ptr);
 }
 
 static snd_pcm_uframes_t
-snd_rme32_capture_pointer(snd_pcm_substream_t * substream)
+snd_rme32_capture_fd_pointer(snd_pcm_substream_t * substream)
 {
 	rme32_t *rme32 = _snd_pcm_substream_chip(substream);
-	return snd_rme32_capture_ptr(rme32);
+	size_t ptr;
+
+	ptr = readl(rme32->iobase + RME32_IO_GET_POS) & RME32_RCR_AUDIO_ADDR_MASK;
+	return snd_pcm_indirect_capture_pointer(substream, &rme32->playback_pcm, ptr);
 }
 
+/* for halfduplex mode */
 static snd_pcm_ops_t snd_rme32_playback_spdif_ops = {
 	.open =		snd_rme32_playback_spdif_open,
 	.close =	snd_rme32_playback_close,
 	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_rme32_playback_hw_params,
+	.hw_free =	snd_rme32_pcm_hw_free,
 	.prepare =	snd_rme32_playback_prepare,
-	.trigger =	snd_rme32_playback_trigger,
+	.trigger =	snd_rme32_pcm_trigger,
 	.pointer =	snd_rme32_playback_pointer,
 	.copy =		snd_rme32_playback_copy,
 	.silence =	snd_rme32_playback_silence,
@@ -1188,8 +1248,9 @@
 	.close =	snd_rme32_capture_close,
 	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_rme32_capture_hw_params,
+	.hw_free =	snd_rme32_pcm_hw_free,
 	.prepare =	snd_rme32_capture_prepare,
-	.trigger =	snd_rme32_capture_trigger,
+	.trigger =	snd_rme32_pcm_trigger,
 	.pointer =	snd_rme32_capture_pointer,
 	.copy =		snd_rme32_capture_copy,
 };
@@ -1200,7 +1261,7 @@
 	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_rme32_playback_hw_params,
 	.prepare =	snd_rme32_playback_prepare,
-	.trigger =	snd_rme32_playback_trigger,
+	.trigger =	snd_rme32_pcm_trigger,
 	.pointer =	snd_rme32_playback_pointer,
 	.copy =		snd_rme32_playback_copy,
 	.silence =	snd_rme32_playback_silence,
@@ -1212,11 +1273,58 @@
 	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_rme32_capture_hw_params,
 	.prepare =	snd_rme32_capture_prepare,
-	.trigger =	snd_rme32_capture_trigger,
+	.trigger =	snd_rme32_pcm_trigger,
 	.pointer =	snd_rme32_capture_pointer,
 	.copy =		snd_rme32_capture_copy,
 };
 
+/* for fullduplex mode */
+static snd_pcm_ops_t snd_rme32_playback_spdif_fd_ops = {
+	.open =		snd_rme32_playback_spdif_open,
+	.close =	snd_rme32_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_rme32_playback_hw_params,
+	.hw_free =	snd_rme32_pcm_hw_free,
+	.prepare =	snd_rme32_playback_prepare,
+	.trigger =	snd_rme32_pcm_trigger,
+	.pointer =	snd_rme32_playback_fd_pointer,
+	.ack =		snd_rme32_playback_fd_ack,
+};
+
+static snd_pcm_ops_t snd_rme32_capture_spdif_fd_ops = {
+	.open =		snd_rme32_capture_spdif_open,
+	.close =	snd_rme32_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_rme32_capture_hw_params,
+	.hw_free =	snd_rme32_pcm_hw_free,
+	.prepare =	snd_rme32_capture_prepare,
+	.trigger =	snd_rme32_pcm_trigger,
+	.pointer =	snd_rme32_capture_fd_pointer,
+	.ack =		snd_rme32_capture_fd_ack,
+};
+
+static snd_pcm_ops_t snd_rme32_playback_adat_fd_ops = {
+	.open =		snd_rme32_playback_adat_open,
+	.close =	snd_rme32_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_rme32_playback_hw_params,
+	.prepare =	snd_rme32_playback_prepare,
+	.trigger =	snd_rme32_pcm_trigger,
+	.pointer =	snd_rme32_playback_fd_pointer,
+	.ack =		snd_rme32_capture_fd_ack,
+};
+
+static snd_pcm_ops_t snd_rme32_capture_adat_fd_ops = {
+	.open =		snd_rme32_capture_adat_open,
+	.close =	snd_rme32_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_rme32_capture_hw_params,
+	.prepare =	snd_rme32_capture_prepare,
+	.trigger =	snd_rme32_pcm_trigger,
+	.pointer =	snd_rme32_capture_fd_pointer,
+	.ack =		snd_rme32_capture_fd_ack,
+};
+
 static void snd_rme32_free(void *private_data)
 {
 	rme32_t *rme32 = (rme32_t *) private_data;
@@ -1225,8 +1333,7 @@
 		return;
 	}
 	if (rme32->irq >= 0) {
-		snd_rme32_playback_stop(rme32);
-		snd_rme32_capture_stop(rme32);
+		snd_rme32_pcm_stop(rme32);
 		free_irq(rme32->irq, (void *) rme32);
 		rme32->irq = -1;
 	}
@@ -1294,12 +1401,22 @@
 	rme32->spdif_pcm->private_data = rme32;
 	rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm;
 	strcpy(rme32->spdif_pcm->name, "Digi32 IEC958");
-	snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK,
-			&snd_rme32_playback_spdif_ops);
-	snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE,
-			&snd_rme32_capture_spdif_ops);
-
-	rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+	if (rme32->fullduplex_mode) {
+		snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				&snd_rme32_playback_spdif_fd_ops);
+		snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE,
+				&snd_rme32_capture_spdif_fd_ops);
+		snd_pcm_lib_preallocate_pages_for_all(rme32->spdif_pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+						      snd_dma_continuous_data(GFP_KERNEL),
+						      0, RME32_MID_BUFFER_SIZE);
+		rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+	} else {
+		snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				&snd_rme32_playback_spdif_ops);
+		snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE,
+				&snd_rme32_capture_spdif_ops);
+		rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+	}
 
 	/* set up ALSA pcm device for ADAT */
 	if ((pci->device == PCI_DEVICE_ID_DIGI32) ||
@@ -1316,12 +1433,22 @@
 		rme32->adat_pcm->private_data = rme32;
 		rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm;
 		strcpy(rme32->adat_pcm->name, "Digi32 ADAT");
-		snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, 
-				&snd_rme32_playback_adat_ops);
-		snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, 
-				&snd_rme32_capture_adat_ops);
-		
-		rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+		if (rme32->fullduplex_mode) {
+			snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, 
+					&snd_rme32_playback_adat_fd_ops);
+			snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, 
+					&snd_rme32_capture_adat_fd_ops);
+			snd_pcm_lib_preallocate_pages_for_all(rme32->adat_pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+							      snd_dma_continuous_data(GFP_KERNEL),
+							      0, RME32_MID_BUFFER_SIZE);
+			rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+		} else {
+			snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, 
+					&snd_rme32_playback_adat_ops);
+			snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, 
+					&snd_rme32_capture_adat_ops);
+			rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+		}
 	}
 
 
@@ -1329,8 +1456,7 @@
 	rme32->capture_periodsize = 0;
 
 	/* make sure playback/capture is stopped, if by some reason active */
-	snd_rme32_playback_stop(rme32);
-	snd_rme32_capture_stop(rme32);
+	snd_rme32_pcm_stop(rme32);
 
         /* reset DAC */
         snd_rme32_reset_dac(rme32);
@@ -1375,6 +1501,10 @@
 	snd_iprintf(buffer, " (index #%d)\n", rme32->card->number + 1);
 
 	snd_iprintf(buffer, "\nGeneral settings\n");
+	if (rme32->fullduplex_mode)
+		snd_iprintf(buffer, "  Full-duplex mode\n");
+	else
+		snd_iprintf(buffer, "  Half-duplex mode\n");
 	if (RME32_PRO_WITH_8414(rme32)) {
 		snd_iprintf(buffer, "  receiver: CS8414\n");
 	} else {
@@ -1818,7 +1948,7 @@
 	int idx, err;
 	snd_kcontrol_t *kctl;
 
-	for (idx = 0; idx < 7; idx++) {
+	for (idx = 0; idx < (int)ARRAY_SIZE(snd_rme32_controls); idx++) {
 		if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme32_controls[idx], rme32))) < 0)
 			return err;
 		if (idx == 1)	/* IEC958 (S/PDIF) Stream */
Index: alsa-kernel/include/pcm-indirect.h
===================================================================
RCS file: alsa-kernel/include/pcm-indirect.h
diff -N alsa-kernel/include/pcm-indirect.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ alsa-kernel/include/pcm-indirect.h	18 Jun 2004 15:11:39 -0000
@@ -0,0 +1,159 @@
+/*
+ * Helper functions for indirect PCM data transfer
+ *
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *                   Jaroslav Kysela <perex@suse.cz>
+ *
+ *   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 __SOUND_PCM_INDIRECT_H
+#define __SOUND_PCM_INDIRECT_H
+
+#include <sound/pcm.h>
+
+typedef struct sndrv_pcm_indirect {
+	unsigned int hw_buffer_size;	/* Byte size of hardware buffer */
+	unsigned int hw_queue_size;	/* Max queue size of hw buffer (0 = buffer size) */
+	unsigned int hw_data;	/* Offset to next dst (or src) in hw ring buffer */
+	unsigned int hw_io;	/* Ring buffer hw pointer */
+	int hw_ready;		/* Bytes ready for play (or captured) in hw ring buffer */
+	unsigned int sw_buffer_size;	/* Byte size of software buffer */
+	unsigned int sw_data;	/* Offset to next dst (or src) in sw ring buffer */
+	unsigned int sw_io;	/* Current software pointer in bytes */
+	int sw_ready;		/* Bytes ready to be transferred to/from hw */
+	size_t appl_ptr;	/* Last seen appl_ptr */
+} snd_pcm_indirect_t;
+
+typedef void (*snd_pcm_indirect_copy_t)(snd_pcm_substream_t *substream,
+					snd_pcm_indirect_t *rec, size_t bytes);
+
+static inline void
+snd_pcm_indirect_playback_transfer(snd_pcm_substream_t *substream,
+				   snd_pcm_indirect_t *rec,
+				   snd_pcm_indirect_copy_t copy)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
+	snd_pcm_uframes_t qsize;
+
+	if (diff) {
+		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+			diff += runtime->boundary;
+		rec->sw_ready += frames_to_bytes(runtime, diff);
+		rec->appl_ptr = appl_ptr;
+	}
+	qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
+	while (rec->hw_ready < qsize && rec->sw_ready > 0) {
+		size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
+		size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
+		size_t bytes = rec->hw_buffer_size - rec->hw_ready;
+		if (rec->sw_ready < (int)bytes)
+			bytes = rec->sw_ready;
+		if (hw_to_end < bytes)
+			bytes = hw_to_end;
+		if (sw_to_end < bytes)
+			bytes = sw_to_end;
+		if (! bytes)
+			break;
+		copy(substream, rec, bytes);
+		rec->hw_data += bytes;
+		if ((int)rec->hw_data == rec->hw_buffer_size)
+			rec->hw_data = 0;
+		rec->sw_data += bytes;
+		if (rec->sw_data == rec->sw_buffer_size)
+			rec->sw_data = 0;
+		rec->hw_ready += bytes;
+		rec->sw_ready -= bytes;
+	}
+}
+
+static inline snd_pcm_uframes_t
+snd_pcm_indirect_playback_pointer(snd_pcm_substream_t *substream,
+				  snd_pcm_indirect_t *rec, size_t ptr)
+{
+	ssize_t bytes = ptr - rec->hw_io;
+	if (bytes < 0)
+		bytes += rec->hw_buffer_size;
+	rec->hw_io = ptr;
+	rec->hw_ready -= bytes;
+	rec->sw_io += bytes;
+	if (rec->sw_io >= rec->sw_buffer_size)
+		rec->sw_io -= rec->sw_buffer_size;
+	if (substream->ops->ack)
+		substream->ops->ack(substream);
+	return bytes_to_frames(substream->runtime, rec->sw_io);
+}
+
+
+static inline void
+snd_pcm_indirect_capture_transfer(snd_pcm_substream_t *substream,
+				  snd_pcm_indirect_t *rec,
+				  snd_pcm_indirect_copy_t copy)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
+	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
+
+	if (diff) {
+		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
+			diff += runtime->boundary;
+		rec->sw_ready -= frames_to_bytes(runtime, diff);
+		rec->appl_ptr = appl_ptr;
+	}
+	while (rec->hw_ready > 0 && 
+	       rec->sw_ready < (int)rec->sw_buffer_size) {
+		size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
+		size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
+		size_t bytes = rec->sw_buffer_size - rec->sw_ready;
+		if (rec->hw_ready < (int)bytes)
+			bytes = rec->hw_ready;
+		if (hw_to_end < bytes)
+			bytes = hw_to_end;
+		if (sw_to_end < bytes)
+			bytes = sw_to_end;
+		if (! bytes)
+			break;
+		copy(substream, rec, bytes);
+		rec->hw_data += bytes;
+		if ((int)rec->hw_data == rec->hw_buffer_size)
+			rec->hw_data = 0;
+		rec->sw_data += bytes;
+		if (rec->sw_data == rec->sw_buffer_size)
+			rec->sw_data = 0;
+		rec->hw_ready -= bytes;
+		rec->sw_ready += bytes;
+	}
+}
+
+static inline snd_pcm_uframes_t
+snd_pcm_indirect_capture_pointer(snd_pcm_substream_t *substream,
+				 snd_pcm_indirect_t *rec, size_t ptr)
+{
+	ssize_t bytes = ptr - rec->hw_io;
+	if (bytes < 0)
+		bytes += rec->hw_buffer_size;
+	rec->hw_io = ptr;
+	rec->hw_ready += bytes;
+	rec->sw_io += bytes;
+	if (rec->sw_io >= rec->sw_buffer_size)
+		rec->sw_io -= rec->sw_buffer_size;
+	if (substream->ops->ack)
+		substream->ops->ack(substream);
+	return bytes_to_frames(substream->runtime, rec->sw_io);
+}
+
+#endif /* __SOUND_PCM_INDIRECT_H */

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2004-06-18 15:17 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-06-18 15:17 Tring to support RME32 full-duplex Takashi Iwai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox