Support pcm drain ops

Signed-off-by: Guodong Hu <quic_guodhu@quicinc.com>
diff --git a/include/tinyalsa/pcm.h b/include/tinyalsa/pcm.h
index 9fca92d..35318a5 100644
--- a/include/tinyalsa/pcm.h
+++ b/include/tinyalsa/pcm.h
@@ -361,6 +361,8 @@
 
 int pcm_start(struct pcm *pcm);
 
+int pcm_drain(struct pcm *pcm);
+
 int pcm_stop(struct pcm *pcm);
 
 int pcm_wait(struct pcm *pcm, int timeout);
diff --git a/include/tinyalsa/plugin.h b/include/tinyalsa/plugin.h
index b2f97b9..055734c 100644
--- a/include/tinyalsa/plugin.h
+++ b/include/tinyalsa/plugin.h
@@ -124,7 +124,10 @@
     int (*prepare) (struct pcm_plugin *plugin);
     /** Start data transfer from/to the plugin */
     int (*start) (struct pcm_plugin *plugin);
-    /** Drop pcm frames */
+    /** Signal the plugin to drain PCM */
+    int (*drain) (struct pcm_plugin *plugin);
+    /** Stop a PCM dropping pending frames if drain() is NOT called.
+     *  Stop a PCM preserving pending frames if drain() is called. */
     int (*drop) (struct pcm_plugin *plugin);
     /** Any custom or alsa specific ioctl implementation */
     int (*ioctl) (struct pcm_plugin *plugin,
diff --git a/src/pcm.c b/src/pcm.c
index d681563..1b2103a 100644
--- a/src/pcm.c
+++ b/src/pcm.c
@@ -1218,6 +1218,22 @@
     return 0;
 }
 
+/** Drains a PCM.
+ * @param pcm A PCM handle.
+ * @return On success, zero; on failure, a negative number.
+ * @ingroup libtinyalsa-pcm
+ */
+int pcm_drain(struct pcm *pcm)
+{
+    if (!pcm_is_ready(pcm))
+        return -1;
+
+    if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_DRAIN) < 0)
+        return oops(pcm, errno, "cannot drain channel");
+
+    return 0;
+}
+
 /** Stops a PCM.
  * @param pcm A PCM handle.
  * @return On success, zero; on failure, a negative number.
diff --git a/src/pcm_plugin.c b/src/pcm_plugin.c
index b6b69aa..4d2651c 100644
--- a/src/pcm_plugin.c
+++ b/src/pcm_plugin.c
@@ -622,6 +622,16 @@
     return rc;
 }
 
+static int pcm_plug_drain(struct pcm_plug_data *plug_data)
+{
+    struct pcm_plugin *plugin = plug_data->plugin;
+
+    if (plugin->state != PCM_PLUG_STATE_RUNNING)
+        return -EBADFD;
+
+    return plug_data->ops->drain(plugin);
+}
+
 static int pcm_plug_ioctl(void *data, unsigned int cmd, ...)
 {
     struct pcm_plug_data *plug_data = data;
@@ -659,6 +669,9 @@
     case SNDRV_PCM_IOCTL_START:
         ret = pcm_plug_start(plug_data);
         break;
+    case SNDRV_PCM_IOCTL_DRAIN:
+        ret = pcm_plug_drain(plug_data);
+        break;
     case SNDRV_PCM_IOCTL_DROP:
         ret = pcm_plug_drop(plug_data);
         break;