From 986b8e338782a4c8aab83e626949ed5457eb5b69 Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Wed, 4 Sep 2019 15:32:35 -0700 Subject: 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 Signed-off-by: Rohit kumar --- src/pcm.c | 121 ++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 39 deletions(-) (limited to 'src/pcm.c') 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 #include +#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 @@ struct pcm { 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 @@ int pcm_set_config(struct pcm *pcm, const struct pcm_config *config) 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 @@ int pcm_set_config(struct pcm *pcm, const struct pcm_config *config) 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 @@ static int pcm_sync_ptr(struct pcm *pcm, int flags) } } 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 @@ struct pcm_params *pcm_params_get(unsigned int card, unsigned int device, 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'); - - if (flags & PCM_NONBLOCK) - fd = open(fn, O_RDWR | O_NONBLOCK); - else - fd = open(fn, O_RDWR); + ops = &hw_ops; + fd = ops->open(card, device, flags, &data, snd_node); +#ifdef TINYALSA_USES_PLUGINS + if (fd < 0) { + 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 device '%s': %s\n", fn, strerror(errno)); + fprintf(stderr, "cannot open card(%d) device (%d): %s\n", + card, device, strerror(errno)); goto err_open; } @@ -620,19 +641,27 @@ struct pcm_params *pcm_params_get(unsigned int card, unsigned int device, 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 @@ int pcm_close(struct pcm *pcm) 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_open(unsigned int card, unsigned int device, { 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'); - - pcm->flags = flags; - - if (flags & PCM_NONBLOCK) - pcm->fd = open(fn, O_RDWR | O_NONBLOCK); - else - pcm->fd = open(fn, O_RDWR); + /* 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); +#ifdef TINYALSA_USES_PLUGINS + if (pcm->fd < 0) { + 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 '%s'", fn); + 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 @@ struct pcm *pcm_open(unsigned int card, unsigned int device, #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 @@ fail: 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_unlink(struct pcm *pcm) */ 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 @@ int pcm_start(struct pcm *pcm) 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_start(struct pcm *pcm) */ 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 @@ int pcm_mmap_transfer(struct pcm *pcm, void *buffer, unsigned int frames) */ 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 @@ int pcm_mmap_transfer(struct pcm *pcm, void *buffer, unsigned int frames) /* 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 @@ static int pcm_rw_transfer(struct pcm *pcm, void *data, unsigned int frames) 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; } -- cgit v1.2.3