diff mbox series

[RFC,1/3] ALSA: pcm: add snd_pcm_period_elapsed() variant without acquiring lock of PCM substream

Message ID 20210606091838.80812-2-o-takashi@sakamocchi.jp
State New
Headers show
Series [RFC,1/3] ALSA: pcm: add snd_pcm_period_elapsed() variant without acquiring lock of PCM substream | expand

Commit Message

Takashi Sakamoto June 6, 2021, 9:18 a.m. UTC
Current implementation of ALSA PCM core has a kernel API,
snd_pcm_period_elapsed(), for drivers to queue event to awaken processes
from waiting for available frames. The function voluntarily acquires lock
of PCM substream, therefore it is not called in process context for any
PCM operation since the lock is already acquired.

It is convenient for packet-oriented driver, at least for drivers to audio
and music unit in IEEE 1394 bus. The drivers are allowed by Linux
FireWire subsystem to process isochronous packets queued till recent
isochronous cycle in process context in any time.

This commit adds snd_pcm_period_elapsed() variant,
snd_pcm_period_elapsed_without_lock(), for drivers to queue the event in
the process context.
---
 include/sound/pcm.h  | 53 +++++++++++++++++++++++++++++++++++++++++++-
 sound/core/pcm_lib.c | 25 ++++-----------------
 2 files changed, 56 insertions(+), 22 deletions(-)
diff mbox series

Patch

diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 2e1200d17d0c..a4eaa48584da 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -11,6 +11,7 @@ 
 #include <sound/asound.h>
 #include <sound/memalloc.h>
 #include <sound/minors.h>
+#include <sound/core.h>
 #include <linux/poll.h>
 #include <linux/mm.h>
 #include <linux/bitops.h>
@@ -1066,7 +1067,57 @@  void snd_pcm_set_ops(struct snd_pcm * pcm, int direction,
 void snd_pcm_set_sync(struct snd_pcm_substream *substream);
 int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
 		      unsigned int cmd, void *arg);                      
-void snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
+
+void __snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
+
+/**
+ * snd_pcm_period_elapsed - update the pcm status for the next period
+ * @substream: the pcm substream instance
+ *
+ * This function is called when the batch of PCM frames as the same as period of PCM buffer are
+ * processed in audio data transmission. It's typically called by any type of IRQ handler when
+ * hardware IRQ occurs to notify the event. It acquires lock of PCM substream, then will update the
+ * current pointer, wake up sleepers, etc.
+ *
+ * Developer should pay enough attention that some callbacks in &snd_pcm_ops are done by the call of
+ * function:
+ *
+ * - .pointer - to retrieve current position of audio data transmission by frame count or XRUN state.
+ * - .trigger - with SNDRV_PCM_TRIGGER_STOP at XRUN or DRAINING state.
+ * - .get_time_info - to retrieve audio time stamp.
+ *
+ * Even if more than one periods have elapsed since the last call, you have to call this only once.
+ */
+static inline void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
+{
+	unsigned long flags;
+
+	if (snd_BUG_ON(!substream))
+		return;
+
+	snd_pcm_stream_lock_irqsave(substream, flags);
+	__snd_pcm_period_elapsed(substream);
+	snd_pcm_stream_unlock_irqrestore(substream, flags);
+}
+
+/**
+ * snd_pcm_period_elapsed_without_lock() - update the pcm status for the next period without
+ *					   acquiring lock of PCM substream.
+ * @substream: the pcm substream instance
+ *
+ * This function is variant of ``snd_pcm_period_elapsed()`` without voluntarily acquiring lock of
+ * PCM substream. It's intended to use for the case that PCM driver operates PCM frames under
+ * acquiring lock of PCM substream; e.g. in callback of any operation of &snd_pcm_ops in process
+ * context, then queueing period wakeup event.
+ */
+static inline void snd_pcm_period_elapsed_without_lock(struct snd_pcm_substream *substream)
+{
+	if (snd_BUG_ON(!substream))
+		return;
+
+	__snd_pcm_period_elapsed(substream);
+}
+
 snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
 				     void *buf, bool interleaved,
 				     snd_pcm_uframes_t frames, bool in_kernel);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index b7e3d8f44511..33ad4ab0ec3a 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1777,28 +1777,13 @@  int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
 }
 EXPORT_SYMBOL(snd_pcm_lib_ioctl);
 
-/**
- * snd_pcm_period_elapsed - update the pcm status for the next period
- * @substream: the pcm substream instance
- *
- * This function is called from the interrupt handler when the
- * PCM has processed the period size.  It will update the current
- * pointer, wake up sleepers, etc.
- *
- * Even if more than one periods have elapsed since the last call, you
- * have to call this only once.
- */
-void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
+// The pointer to PCM substream should not be NULL as precondition.
+void __snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime;
-	unsigned long flags;
 
-	if (snd_BUG_ON(!substream))
-		return;
-
-	snd_pcm_stream_lock_irqsave(substream, flags);
 	if (PCM_RUNTIME_CHECK(substream))
-		goto _unlock;
+		return;
 	runtime = substream->runtime;
 
 	if (!snd_pcm_running(substream) ||
@@ -1811,10 +1796,8 @@  void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
 #endif
  _end:
 	kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
- _unlock:
-	snd_pcm_stream_unlock_irqrestore(substream, flags);
 }
-EXPORT_SYMBOL(snd_pcm_period_elapsed);
+EXPORT_SYMBOL(__snd_pcm_period_elapsed);
 
 /*
  * Wait until avail_min data becomes available