tinyalsa: add support for PCM plugins
Update the pcm framework to support plugins. Resolve the pcm device node
to be either kernel device or virtual device and setup function pointers
accordingly. Implement framework functionality for pcm_plugin.c for ease
of plugin development. Plugin itself is compiled as shared object (.so)
and dynamically linked from pcm_plugin.c.
Signed-off-by: Bhalchandra Gajare <gajare@codeaurora.org>
Signed-off-by: Rohit kumar <rohitkr@codeaurora.org>
diff --git a/src/pcm.c b/src/pcm.c
index a32871e..7a8defc 100644
--- a/src/pcm.c
+++ b/src/pcm.c
@@ -59,6 +59,8 @@
#include <tinyalsa/pcm.h>
#include <tinyalsa/limits.h>
+#include "pcm_io.h"
+#include "snd_card_plugin.h"
#ifndef PARAM_MAX
#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL
@@ -68,6 +70,7 @@
#define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2)
#endif /* SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP */
+
static inline int param_is_mask(int p)
{
return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
@@ -234,6 +237,12 @@
long pcm_delay;
/** The subdevice corresponding to the PCM */
unsigned int subdevice;
+ /** Pointer to the pcm ops, either hw or plugin */
+ const struct pcm_ops *ops;
+ /** Private data for pcm_hw or pcm_plugin */
+ void *data;
+ /** Pointer to the pcm node from snd card definition */
+ struct snd_node *snd_node;
};
static int oops(struct pcm *pcm, int e, const char *fmt, ...)
@@ -383,7 +392,7 @@
param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS,
SNDRV_PCM_ACCESS_RW_INTERLEAVED);
- if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) {
+ if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) {
int errno_copy = errno;
oops(pcm, -errno, "cannot set hw params");
return -errno_copy;
@@ -440,7 +449,7 @@
while (pcm->boundary * 2 <= INT_MAX - pcm->buffer_size)
pcm->boundary *= 2;
- if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
+ if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) {
int errno_copy = errno;
oops(pcm, -errno, "cannot set sw params");
return -errno_copy;
@@ -519,7 +528,8 @@
}
} else {
pcm->sync_ptr->flags = flags;
- if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0) {
+ if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_SYNC_PTR,
+ pcm->sync_ptr) < 0) {
oops(pcm, errno, "failed to sync mmap ptr");
return -1;
}
@@ -599,19 +609,30 @@
unsigned int flags)
{
struct snd_pcm_hw_params *params;
- char fn[256];
+ void *snd_node = NULL, *data;
+ const struct pcm_ops *ops;
int fd;
- snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
- flags & PCM_IN ? 'c' : 'p');
+ ops = &hw_ops;
+ fd = ops->open(card, device, flags, &data, snd_node);
- if (flags & PCM_NONBLOCK)
- fd = open(fn, O_RDWR | O_NONBLOCK);
- else
- fd = open(fn, O_RDWR);
-
+#ifdef TINYALSA_USES_PLUGINS
if (fd < 0) {
- fprintf(stderr, "cannot open device '%s': %s\n", fn, strerror(errno));
+ int pcm_type;
+ snd_node = snd_utils_open_pcm(card, device);
+ pcm_type = snd_utils_get_node_type(snd_node);
+ if (!snd_node || pcm_type != SND_NODE_TYPE_PLUGIN) {
+ fprintf(stderr, "no device (hw/plugin) for card(%u), device(%u)",
+ card, device);
+ goto err_open;
+ }
+ ops = &plug_ops;
+ fd = ops->open(card, device, flags, &data, snd_node);
+ }
+#endif
+ if (fd < 0) {
+ fprintf(stderr, "cannot open card(%d) device (%d): %s\n",
+ card, device, strerror(errno));
goto err_open;
}
@@ -620,19 +641,27 @@
goto err_calloc;
param_init(params);
- if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
+ if (ops->ioctl(data, SNDRV_PCM_IOCTL_HW_REFINE, params)) {
fprintf(stderr, "SNDRV_PCM_IOCTL_HW_REFINE error (%d)\n", errno);
goto err_hw_refine;
}
- close(fd);
+#ifdef TINYALSA_USES_PLUGINS
+ if (snd_node)
+ snd_utils_close_dev_node(snd_node);
+#endif
+ ops->close(data);
return (struct pcm_params *)params;
err_hw_refine:
free(params);
err_calloc:
- close(fd);
+#ifdef TINYALSA_USES_PLUGINS
+ if (snd_node)
+ snd_utils_close_dev_node(snd_node);
+#endif
+ ops->close(data);
err_open:
return NULL;
}
@@ -787,8 +816,8 @@
munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
}
- if (pcm->fd >= 0)
- close(pcm->fd);
+ snd_utils_close_dev_node(pcm->snd_node);
+ pcm->ops->close(pcm->data);
pcm->buffer_size = 0;
pcm->fd = -1;
free(pcm);
@@ -851,29 +880,39 @@
{
struct pcm *pcm;
struct snd_pcm_info info;
- char fn[256];
int rc;
pcm = calloc(1, sizeof(struct pcm));
if (!pcm)
return &bad_pcm;
- snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
- flags & PCM_IN ? 'c' : 'p');
+ /* Default to hw_ops, attemp plugin open only if hw (/dev/snd/pcm*) open fails */
+ pcm->ops = &hw_ops;
+ pcm->fd = pcm->ops->open(card, device, flags, &pcm->data, NULL);
- pcm->flags = flags;
-
- if (flags & PCM_NONBLOCK)
- pcm->fd = open(fn, O_RDWR | O_NONBLOCK);
- else
- pcm->fd = open(fn, O_RDWR);
-
+#ifdef TINYALSA_USES_PLUGINS
if (pcm->fd < 0) {
- oops(pcm, errno, "cannot open device '%s'", fn);
+ int pcm_type;
+ pcm->snd_node = snd_utils_open_pcm(card, device);
+ pcm_type = snd_utils_get_node_type(pcm->snd_node);
+ if (!pcm->snd_node || pcm_type != SND_NODE_TYPE_PLUGIN) {
+ oops(pcm, -ENODEV, "no device (hw/plugin) for card(%u), device(%u)",
+ card, device);
+ return pcm;
+ }
+ pcm->ops = &plug_ops;
+ pcm->fd = pcm->ops->open(card, device, flags, &pcm->data, pcm->snd_node);
+ }
+#endif
+ if (pcm->fd < 0) {
+ oops(pcm, errno, "cannot open device (%u) for card (%u)",
+ device, card);
return pcm;
}
- if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
+ pcm->flags = flags;
+
+ if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_INFO, &info)) {
oops(pcm, errno, "cannot get info");
goto fail_close;
}
@@ -891,7 +930,7 @@
#ifdef SNDRV_PCM_IOCTL_TTSTAMP
if (pcm->flags & PCM_MONOTONIC) {
int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
- rc = ioctl(pcm->fd, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
+ rc = pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
if (rc < 0) {
oops(pcm, rc, "cannot set timestamp type");
goto fail;
@@ -911,7 +950,11 @@
if (flags & PCM_MMAP)
munmap(pcm->mmap_buffer, pcm_frames_to_bytes(pcm, pcm->buffer_size));
fail_close:
- close(pcm->fd);
+#ifdef TINYALSA_USES_PLUGINS
+ if (pcm->snd_node)
+ snd_utils_close_dev_node(pcm->snd_node);
+#endif
+ pcm->ops->close(pcm->data);
pcm->fd = -1;
return pcm;
}
@@ -970,7 +1013,7 @@
*/
int pcm_prepare(struct pcm *pcm)
{
- if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
+ if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_PREPARE) < 0)
return oops(pcm, errno, "cannot prepare channel");
/* get appl_ptr and avail_min from kernel */
@@ -991,7 +1034,7 @@
return -1;
if (pcm->mmap_status->state != PCM_STATE_RUNNING) {
- if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START) < 0)
+ if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_START) < 0)
return oops(pcm, errno, "cannot start channel");
}
@@ -1005,7 +1048,7 @@
*/
int pcm_stop(struct pcm *pcm)
{
- if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_DROP) < 0)
+ if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_DROP) < 0)
return oops(pcm, errno, "cannot stop channel");
return 0;
@@ -1288,7 +1331,7 @@
*/
if (!is_playback && state == PCM_STATE_PREPARED &&
frames >= pcm->config.start_threshold) {
- err = ioctl(pcm->fd, SNDRV_PCM_IOCTL_START);
+ err = pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_START);
if (err == -1)
return -1;
/* state = PCM_STATE_RUNNING */
@@ -1326,7 +1369,7 @@
/* start playback if written >= start_threshold */
if (is_playback && state == PCM_STATE_PREPARED &&
pcm->buffer_size - avail >= pcm->config.start_threshold) {
- err = ioctl(pcm->fd, SNDRV_PCM_IOCTL_START);
+ err = pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_START);
if (err == -1)
break;
}
@@ -1365,9 +1408,9 @@
transfer.frames = frames;
transfer.result = 0;
- res = ioctl(pcm->fd, is_playback
- ? SNDRV_PCM_IOCTL_WRITEI_FRAMES
- : SNDRV_PCM_IOCTL_READI_FRAMES, &transfer);
+ res = pcm->ops->ioctl(pcm->data, is_playback
+ ? SNDRV_PCM_IOCTL_WRITEI_FRAMES
+ : SNDRV_PCM_IOCTL_READI_FRAMES, &transfer);
return res == 0 ? (int) transfer.result : -1;
}