ALSA: pcmtest: Add support for pcm pausing
authorIvan Orlov <ivan.orlov0322@gmail.com>
Tue, 22 Aug 2023 15:05:41 +0000 (19:05 +0400)
committerTakashi Iwai <tiwai@suse.de>
Thu, 24 Aug 2023 07:58:22 +0000 (09:58 +0200)
Add pause push/release support to the virtual PCM test driver. Add
'suspend' boolean field to the pcmtst_buf_iter structure, so we can
pause the timer without shutting it down. Update the trigger callback
handler correspondingly. Extract buffer initialization to the
'reset_buf_iterator' function since it is used in multiple places now.

Signed-off-by: Ivan Orlov <ivan.orlov0322@gmail.com>
Link: https://lore.kernel.org/r/20230822150541.8450-1-ivan.orlov0322@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/drivers/pcmtest.c

index 27cbb9d..b59b78a 100644 (file)
@@ -108,6 +108,7 @@ struct pcmtst_buf_iter {
        size_t total_bytes;                     // Total bytes read/written
        size_t chan_block;                      // Bytes in one channel buffer when non-interleaved
        struct snd_pcm_substream *substream;
+       bool suspend;                           // We need to pause timer without shutting it down
        struct timer_list timer_instance;
 };
 
@@ -115,7 +116,8 @@ static struct snd_pcm_hardware snd_pcmtst_hw = {
        .info = (SNDRV_PCM_INFO_INTERLEAVED |
                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
                 SNDRV_PCM_INFO_NONINTERLEAVED |
-                SNDRV_PCM_INFO_MMAP_VALID),
+                SNDRV_PCM_INFO_MMAP_VALID |
+                SNDRV_PCM_INFO_PAUSE),
        .formats =              SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
        .rates =                SNDRV_PCM_RATE_8000_48000,
        .rate_min =             8000,
@@ -346,6 +348,9 @@ static void timer_timeout(struct timer_list *data)
        v_iter = from_timer(v_iter, data, timer_instance);
        substream = v_iter->substream;
 
+       if (v_iter->suspend)
+               return;
+
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !v_iter->is_buf_corrupted)
                check_buf_block(v_iter, substream->runtime);
        else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
@@ -358,7 +363,9 @@ static void timer_timeout(struct timer_list *data)
                v_iter->period_pos %= v_iter->period_bytes;
                snd_pcm_period_elapsed(substream);
        }
-       mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL + inject_delay);
+
+       if (!v_iter->suspend)
+               mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL + inject_delay);
 }
 
 static int snd_pcmtst_pcm_open(struct snd_pcm_substream *substream)
@@ -373,19 +380,15 @@ static int snd_pcmtst_pcm_open(struct snd_pcm_substream *substream)
        if (!v_iter)
                return -ENOMEM;
 
+       v_iter->substream = substream;
        runtime->hw = snd_pcmtst_hw;
        runtime->private_data = v_iter;
-       v_iter->substream = substream;
-       v_iter->buf_pos = 0;
-       v_iter->is_buf_corrupted = false;
-       v_iter->period_pos = 0;
-       v_iter->total_bytes = 0;
 
        playback_capture_test = 0;
        ioctl_reset_test = 0;
 
        timer_setup(&v_iter->timer_instance, timer_timeout, 0);
-       mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL);
+
        return 0;
 }
 
@@ -400,10 +403,40 @@ static int snd_pcmtst_pcm_close(struct snd_pcm_substream *substream)
        return 0;
 }
 
+static inline void reset_buf_iterator(struct pcmtst_buf_iter *v_iter)
+{
+       v_iter->buf_pos = 0;
+       v_iter->is_buf_corrupted = false;
+       v_iter->period_pos = 0;
+       v_iter->total_bytes = 0;
+}
+
+static inline void start_pcmtest_timer(struct pcmtst_buf_iter *v_iter)
+{
+       v_iter->suspend = false;
+       mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL);
+}
+
 static int snd_pcmtst_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
+       struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
+
        if (inject_trigger_err)
                return -EINVAL;
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               reset_buf_iterator(v_iter);
+               start_pcmtest_timer(v_iter);
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               start_pcmtest_timer(v_iter);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               // We can't call timer_shutdown_sync here, as it is forbidden to sleep here
+               v_iter->suspend = true;
+               break;
+       }
 
        return 0;
 }