diff options
-rw-r--r-- | Android.bp | 6 | ||||
-rw-r--r-- | examples/plugins/Android.bp | 15 | ||||
-rw-r--r-- | examples/plugins/sample_mixer_plugin.c | 319 | ||||
-rw-r--r-- | examples/plugins/sample_pcm_plugin.c | 331 | ||||
-rw-r--r-- | examples/sndcardparser/Android.bp | 7 | ||||
-rw-r--r-- | examples/sndcardparser/sample_sndcardparser.c | 269 | ||||
-rw-r--r-- | src/pcm_hw.c | 4 |
7 files changed, 949 insertions, 2 deletions
@@ -63,6 +63,12 @@ cc_library { }, } +cc_library_headers { + name: "libtinyalsav2_headers", + export_include_dirs: ["include"], + vendor_available: true, +} + cc_binary { name: "tinyplay2", host_supported: true, diff --git a/examples/plugins/Android.bp b/examples/plugins/Android.bp new file mode 100644 index 0000000..840480e --- /dev/null +++ b/examples/plugins/Android.bp @@ -0,0 +1,15 @@ +cc_library { + name: "libtinyalsav2_example_plugin_pcm", + vendor: true, + srcs: ["sample_pcm_plugin.c"], + cflags: ["-Werror", "-Wno-unused-parameter"], + header_libs: ["libtinyalsav2_headers"], +} + +cc_library { + name: "libtinyalsav2_example_plugin_mixer", + vendor: true, + srcs: ["sample_mixer_plugin.c"], + cflags: ["-Werror", "-Wno-unused-parameter"], + header_libs: ["libtinyalsav2_headers"], +} diff --git a/examples/plugins/sample_mixer_plugin.c b/examples/plugins/sample_mixer_plugin.c new file mode 100644 index 0000000..e1a7567 --- /dev/null +++ b/examples/plugins/sample_mixer_plugin.c @@ -0,0 +1,319 @@ +/* +** Copyright (c) 2021, The Linux Foundation. All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** * Neither the name of The Linux Foundation nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include <errno.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <sound/asound.h> +#include <tinyalsa/plugin.h> +#include <tinyalsa/asoundlib.h> + +#define SAMPLE_MIXER_PRIV_GET_CTL_PTR(p, idx) (p->ctls + idx) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +static const char *const sample_enum_text[] = {"One", "Two", "Three"}; + +struct sample_mixer_priv { + struct snd_control *ctls; + int ctl_count; + + struct snd_value_enum sample_enum; + + mixer_event_callback event_cb; +}; + +static int sample_mixer_int_ctl_get(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ + return 0; +} + +static int sample_mixer_int_ctl_put(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ +/* + * Integer values can be retrieved using: + * uint32_t val1 = (uint32_t)ev->value.integer.value[0]; + * uint32_t val2 = (uint32_t)ev->value.integer.value[1]; + * uint32_t val3 = (uint32_t)ev->value.integer.value[2]; + */ + return 0; +} + +static int sample_mixer_byte_array_ctl_get(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ + return 0; +} + +static int sample_mixer_byte_array_ctl_put(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ +/* + * Byte array payload can be retrieved using: + * void *payload = ev->value.bytes.data; + */ + + return 0; +} + +static int sample_mixer_tlv_ctl_get(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_tlv *ev) +{ + return 0; +} + +static int sample_mixer_tlv_ctl_put(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_tlv *tlv) +{ +/* + * TLV payload and len can be retrieved using: + * void *payload = &tlv->tlv[0]; + * size_t tlv_size = tlv->length; + */ + + return 0; +} + +static int sample_mixer_enum_ctl_get(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ + return 0; +} + +static int sample_mixer_enum_ctl_put(struct mixer_plugin *plugin, + struct snd_control *ctl, struct snd_ctl_elem_value *ev) +{ +/* + * Enum value can be retrieved using: + * unsigned int val = ev->value.enumerated.item[0]; + */ + return 0; +} + +static struct snd_value_int sample_mixer_ctl_value_int = + SND_VALUE_INTEGER(3, 0, 1000, 100); + +/* 512 max bytes for non-tlv byte controls */ +static struct snd_value_bytes byte_array_ctl_bytes = + SND_VALUE_BYTES(512); + +static struct snd_value_tlv_bytes sample_mixer_tlv_ctl_bytes = + SND_VALUE_TLV_BYTES(1024, sample_mixer_tlv_ctl_get, sample_mixer_tlv_ctl_put); + +static void create_integer_ctl(struct sample_mixer_priv *priv, + int ctl_idx, int pval, void *pdata) +{ + struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx); + char *ctl_name = strdup("Sample integer control"); + + /* pval and pdata can be retrieved using snd_control during get()/put() */ + INIT_SND_CONTROL_INTEGER(ctl, ctl_name, sample_mixer_int_ctl_get, + sample_mixer_int_ctl_put, sample_mixer_ctl_value_int, pval, pdata); +} + +static void create_byte_array_ctl(struct sample_mixer_priv *priv, + int ctl_idx, int pval, void *pdata) +{ + struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx); + char *ctl_name = strdup("Sample byte array control"); + + INIT_SND_CONTROL_BYTES(ctl, ctl_name, sample_mixer_byte_array_ctl_get, + sample_mixer_byte_array_ctl_put, byte_array_ctl_bytes, + pval, pdata); +} + +static void create_tlv_ctl(struct sample_mixer_priv *priv, + int ctl_idx, int pval, void *pdata) +{ + struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx); + char *ctl_name = strdup("Sample tlv control"); + + INIT_SND_CONTROL_TLV_BYTES(ctl, ctl_name, sample_mixer_tlv_ctl_bytes, + pval, pdata); +} + +static void create_enum_ctl(struct sample_mixer_priv *priv, + int ctl_idx, struct snd_value_enum *e, + int pval, void *pdata) +{ + struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx); + char *ctl_name = strdup("Sample enum control"); + + INIT_SND_CONTROL_ENUM(ctl, ctl_name, sample_mixer_enum_ctl_get, + sample_mixer_enum_ctl_put, e, pval, pdata); +} + +static int sample_mixer_form_ctls(struct sample_mixer_priv *priv, int ctl_idx) +{ + create_integer_ctl(priv, ctl_idx, 0, NULL); + ctl_idx++; + create_byte_array_ctl(priv, ctl_idx, 0, NULL); + ctl_idx++; + create_tlv_ctl(priv, ctl_idx, 0, NULL); + ctl_idx++; + create_enum_ctl(priv, ctl_idx, &priv->sample_enum, 0, NULL); + ctl_idx++; + + return 0; +} + +static ssize_t sample_mixer_read_event(struct mixer_plugin *plugin, + struct snd_ctl_event *ev, size_t size) +{ + /* Fill snd_ctl_event *ev before sending. + * Return : sizeof(struct snd_ctl_event), + * 0 in case no event present. + */ + + return 0; +} + +static int sample_mixer_subscribe_events(struct mixer_plugin *plugin, + mixer_event_callback event_cb) +{ + struct sample_mixer_priv *priv = plugin->priv; + + priv->event_cb = event_cb; + /* event_cb is the callback function which needs to be called + * when an event occurs. This will unblock poll() on mixer fd + * which is called from mixer_wait_event(). + * Once poll is unblocked, clients can call mixer_read_event() + * During unsubscribe(), event_cb is NULL. + */ + return 0; +} + +static int sample_mixer_alloc_ctls(struct sample_mixer_priv *priv) +{ + int ret = 0, i; + + priv->ctls = calloc(priv->ctl_count, sizeof(*priv->ctls)); + if (!priv->ctls) { + return -ENOMEM; + } + + priv->sample_enum.items = ARRAY_SIZE(sample_enum_text); + priv->sample_enum.texts = calloc(priv->sample_enum.items, sizeof(*priv->sample_enum.texts)); + + for (i = 0; i < ARRAY_SIZE(sample_enum_text); i++) + priv->sample_enum.texts[i] = strdup(sample_enum_text[i]); + + return sample_mixer_form_ctls(priv, 0); +} + +static void sample_mixer_free_ctls(struct sample_mixer_priv *priv) +{ + int num_enums, i; + struct snd_control *ctl = NULL; + + for (i = 0; i < priv->ctl_count; i++) { + ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, i); + if (ctl->name) + free((void *)ctl->name); + } + + num_enums = priv->sample_enum.items; + + for (i = 0; i < num_enums; i++) + free(priv->sample_enum.texts[i]); + + free(priv->sample_enum.texts); + priv->ctl_count = 0; + + if (priv->ctls) { + free(priv->ctls); + priv->ctls = NULL; + } +} + +static void sample_mixer_close(struct mixer_plugin **plugin) +{ + struct mixer_plugin *mp = *plugin; + struct sample_mixer_priv *priv = mp->priv; + + /* unblock mixer event during close */ + if (priv->event_cb) + priv->event_cb(mp); + sample_mixer_subscribe_events(mp, NULL); + sample_mixer_free_ctls(priv); + free(priv); + free(*plugin); + *plugin = NULL; +} + +int sample_mixer_open(struct mixer_plugin **plugin, unsigned int card) +{ + struct mixer_plugin *mp; + struct sample_mixer_priv *priv; + int i, ret = 0; + int ctl_cnt = 4; + + mp = calloc(1, sizeof(*mp)); + if (!mp) { + return -ENOMEM; + } + + priv = calloc(1, sizeof(*priv)); + if (!priv) { + ret = -ENOMEM; + goto err_priv_alloc; + } + + priv->ctl_count = ctl_cnt; + ret = sample_mixer_alloc_ctls(priv); + if (ret) + goto err_ctls_alloc; + + /* Register the controls */ + mp->controls = priv->ctls; + mp->num_controls = priv->ctl_count; + mp->priv = priv; + *plugin = mp; + + return 0; + +err_ctls_alloc: + sample_mixer_free_ctls(priv); + free(priv); + +err_priv_alloc: + free(mp); + return ret; +} + +struct mixer_plugin_ops mixer_plugin_ops = { + .open = sample_mixer_open, + .close = sample_mixer_close, + .subscribe_events = sample_mixer_subscribe_events, + .read_event = sample_mixer_read_event, +}; diff --git a/examples/plugins/sample_pcm_plugin.c b/examples/plugins/sample_pcm_plugin.c new file mode 100644 index 0000000..3b02435 --- /dev/null +++ b/examples/plugins/sample_pcm_plugin.c @@ -0,0 +1,331 @@ +/* +** Copyright (c) 2021, The Linux Foundation. All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** * Neither the name of The Linux Foundation nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include <errno.h> +#include <limits.h> +#include <sys/time.h> +#include <sys/mman.h> +#include <sound/asound.h> +#include <stdint.h> +#include <stdlib.h> +#include <strings.h> +#include <stdio.h> +#include <unistd.h> +#include <tinyalsa/plugin.h> +#include <tinyalsa/asoundlib.h> + +/* 2 words of uint32_t = 64 bits of mask */ +#define PCM_MASK_SIZE (2) +#define PCM_FORMAT_BIT(x) (1ULL << x) + +struct sample_pcm_priv { + FILE *fptr; + int session_id; + int channels; + int bitwidth; + int sample_rate; + unsigned int period_size; + snd_pcm_uframes_t total_size_frames; +}; + +struct pcm_plugin_hw_constraints sample_pcm_constrs = { + .access = 0, + .format = 0, + .bit_width = { + .min = 16, + .max = 32, + }, + .channels = { + .min = 1, + .max = 8, + }, + .rate = { + .min = 8000, + .max = 384000, + }, + .periods = { + .min = 1, + .max = 8, + }, + .period_bytes = { + .min = 96, + .max = 122880, + }, +}; + +static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, + int n) +{ + return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); +} + +static inline int param_is_interval(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) && + (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL); +} + +static unsigned int param_get_int(struct snd_pcm_hw_params *p, int n) +{ + if (param_is_interval(n)) { + struct snd_interval *i = param_to_interval(p, n); + if (i->integer) + return i->max; + } + return 0; +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline int snd_mask_val(const struct snd_mask *mask) +{ + int i; + for (i = 0; i < PCM_MASK_SIZE; i++) { + if (mask->bits[i]) + return ffs(mask->bits[i]) + (i << 5) - 1; + } + return 0; +} + +static int alsaformat_to_bitwidth(int format) +{ + switch (format) { + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S24_LE: + return 32; + case SNDRV_PCM_FORMAT_S8: + return 8; + case SNDRV_PCM_FORMAT_S24_3LE: + return 24; + default: + case SNDRV_PCM_FORMAT_S16_LE: + return 16; + }; +} + +static int param_get_mask_val(struct snd_pcm_hw_params *p, + int n) +{ + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + int val = snd_mask_val(m); + + return alsaformat_to_bitwidth(val); + } + return 0; +} + +static int sample_session_open(int sess_id, unsigned int mode, struct sample_pcm_priv *priv) +{ + char fname[128]; + + snprintf(fname, 128, "sample_pcm_data_%d.raw", sess_id); + priv->fptr = fopen(fname,"rwb+"); + if (priv->fptr == NULL) { + return -EIO; + } + rewind(priv->fptr); + return 0; +} + + +static int sample_session_write(struct sample_pcm_priv *priv, void *buff, size_t count) +{ + uint8_t *data = (uint8_t *)buff; + size_t len; + + len = fwrite(buff, 1, count, priv->fptr); + + if (len != count) + return -EIO; + + return 0; +} + +static int sample_pcm_hw_params(struct pcm_plugin *plugin, + struct snd_pcm_hw_params *params) +{ + struct sample_pcm_priv *priv = plugin->priv; + + priv->sample_rate = param_get_int(params, SNDRV_PCM_HW_PARAM_RATE); + priv->channels = param_get_int(params, SNDRV_PCM_HW_PARAM_CHANNELS); + priv->bitwidth = param_get_mask_val(params, SNDRV_PCM_HW_PARAM_FORMAT); + + return 0; +} + +static int sample_pcm_sw_params(struct pcm_plugin *plugin, + struct snd_pcm_sw_params *sparams) +{ + return 0; +} + +static int sample_pcm_sync_ptr(struct pcm_plugin *plugin, + struct snd_pcm_sync_ptr *sync_ptr) +{ + return 0; +} + +static int sample_pcm_writei_frames(struct pcm_plugin *plugin, struct snd_xferi *x) +{ + struct sample_pcm_priv *priv = plugin->priv; + void *buff; + size_t count; + + buff = x->buf; + count = x->frames * (priv->channels * (priv->bitwidth) / 8); + + return sample_session_write(priv, buff, count); +} + +static int sample_pcm_readi_frames(struct pcm_plugin *plugin, struct snd_xferi *x) +{ + return 0; +} + +static int sample_pcm_ttstamp(struct pcm_plugin *plugin, int *tstamp) +{ + return 0; +} + +static int sample_pcm_prepare(struct pcm_plugin *plugin) +{ + return 0; +} + +static int sample_pcm_start(struct pcm_plugin *plugin) +{ + return 0; +} + +static int sample_pcm_drop(struct pcm_plugin *plugin) +{ + return 0; +} + +static int sample_pcm_close(struct pcm_plugin *plugin) +{ + struct sample_pcm_priv *priv = plugin->priv; + int ret = 0; + + fclose(priv->fptr); + free(plugin->priv); + free(plugin); + + return ret; +} + +static int sample_pcm_poll(struct pcm_plugin *plugin, struct pollfd *pfd, + nfds_t nfds, int timeout) +{ + return 0; +} + +static void* sample_pcm_mmap(struct pcm_plugin *plugin, void *addr, size_t length, int prot, + int flags, off_t offset) +{ + return MAP_FAILED; +} + +static int sample_pcm_munmap(struct pcm_plugin *plugin, void *addr, size_t length) +{ + return 0; +} + +int sample_pcm_open(struct pcm_plugin **plugin, unsigned int card, + unsigned int device, unsigned int mode) +{ + struct pcm_plugin *sample_pcm_plugin; + struct sample_pcm_priv *priv; + int ret = 0, session_id = device; + + sample_pcm_plugin = calloc(1, sizeof(struct pcm_plugin)); + if (!sample_pcm_plugin) + return -ENOMEM; + + priv = calloc(1, sizeof(struct sample_pcm_priv)); + if (!priv) { + ret = -ENOMEM; + goto err_plugin_free; + } + + sample_pcm_constrs.access = (PCM_FORMAT_BIT(SNDRV_PCM_ACCESS_RW_INTERLEAVED) | + PCM_FORMAT_BIT(SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)); + sample_pcm_constrs.format = (PCM_FORMAT_BIT(SNDRV_PCM_FORMAT_S16_LE) | + PCM_FORMAT_BIT(SNDRV_PCM_FORMAT_S24_LE) | + PCM_FORMAT_BIT(SNDRV_PCM_FORMAT_S24_3LE) | + PCM_FORMAT_BIT(SNDRV_PCM_FORMAT_S32_LE)); + + sample_pcm_plugin->card = card; + sample_pcm_plugin->mode = mode; + sample_pcm_plugin->constraints = &sample_pcm_constrs; + sample_pcm_plugin->priv = priv; + + priv->session_id = session_id; + + ret = sample_session_open(session_id, mode, priv); + if (ret) { + errno = -ret; + goto err_priv_free; + } + *plugin = sample_pcm_plugin; + return 0; + +err_priv_free: + free(priv); +err_plugin_free: + free(sample_pcm_plugin); + return ret; +} + +struct pcm_plugin_ops pcm_plugin_ops = { + .open = sample_pcm_open, + .close = sample_pcm_close, + .hw_params = sample_pcm_hw_params, + .sw_params = sample_pcm_sw_params, + .sync_ptr = sample_pcm_sync_ptr, + .writei_frames = sample_pcm_writei_frames, + .readi_frames = sample_pcm_readi_frames, + .ttstamp = sample_pcm_ttstamp, + .prepare = sample_pcm_prepare, + .start = sample_pcm_start, + .drop = sample_pcm_drop, + .mmap = sample_pcm_mmap, + .munmap = sample_pcm_munmap, + .poll = sample_pcm_poll, +}; diff --git a/examples/sndcardparser/Android.bp b/examples/sndcardparser/Android.bp new file mode 100644 index 0000000..9ca8812 --- /dev/null +++ b/examples/sndcardparser/Android.bp @@ -0,0 +1,7 @@ +cc_library { + name: "libsndcardparser_example", + vendor: true, + srcs: ["sample_sndcardparser.c"], + cflags: ["-Werror"], +} + diff --git a/examples/sndcardparser/sample_sndcardparser.c b/examples/sndcardparser/sample_sndcardparser.c new file mode 100644 index 0000000..90e3d12 --- /dev/null +++ b/examples/sndcardparser/sample_sndcardparser.c @@ -0,0 +1,269 @@ +/* +** Copyright (c) 2021, The Linux Foundation. All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above +** copyright notice, this list of conditions and the following +** disclaimer in the documentation and/or other materials provided +** with the distribution. +** * Neither the name of The Linux Foundation nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +#define VIRTUAL_SND_CARD_ID 100 +#define MAX_PATH 256 +#define BUF_SIZE 1024 + +enum snd_node_type { + NODE_TYPE_HW = 0, + NODE_TYPE_PLUGIN, + NODE_TYPE_INVALID, +}; + +enum { + NODE_PCM, + NODE_COMPR, + NODE_MIXER, + NODE_MAX, +}; + +struct snd_node_ops { + /** Function pointer to get card definition */ + void* (*open_card)(unsigned int card); + /** Function pointer to release card definition */ + void (*close_card)(void *card); + /** Get interger type properties from device definition */ + int (*get_int)(void *node, const char *prop, int *val); + /** Get string type properties from device definition */ + int (*get_str)(void *node, const char *prop, char **val); + /** Function pointer to get mixer definition */ + void* (*get_mixer)(void *card); + /** Function pointer to get PCM definition */ + void* (*get_pcm)(void *card, unsigned int id); + /** Function pointer to get COMPRESS definition */ + void* (*get_compress)(void *card, unsigned int id); +}; + +struct snd_dev_def { + unsigned int device; + int type; + const char *name; + const char *so_name; + int playback; //used only for pcm node + int capture; //used only for pcm node + /* add custom props here */ +}; + +struct snd_dev_def_card { + unsigned int card; + char *name; + + /* child device details */ + int num_pcm_nodes; + struct snd_dev_def *pcm_dev_def; + + struct snd_dev_def *mixer_dev_def; +}; + +struct snd_dev_def pcm_devs[] = { + {100, NODE_TYPE_PLUGIN, "PCM100", "libtinyalsav2_example_plugin_pcm.so", 1, 0}, + /* Add other plugin info here */ +}; + +struct snd_dev_def mixer_dev = + {VIRTUAL_SND_CARD_ID, NODE_TYPE_PLUGIN, "virtual-snd-card", "libtinyalsav2_example_plugin_mixer.so", 0, 0}; + +void *snd_card_def_open_card(unsigned int card) +{ + struct snd_dev_def_card *card_def = NULL; + struct snd_dev_def *pcm_dev_def = NULL; + struct snd_dev_def *mixer_dev_def = NULL; + int num_pcm = ARRAY_SIZE(pcm_devs); + int i; + + if (card != VIRTUAL_SND_CARD_ID) + return NULL; + + card_def = calloc(1, sizeof(struct snd_dev_def_card)); + if (!card_def) + return card_def; + + card_def->card = card; + card_def->name = strdup("virtual-snd-card"); + + /* fill pcm device node info */ + card_def->num_pcm_nodes = num_pcm; + pcm_dev_def = calloc(num_pcm, sizeof(struct snd_dev_def)); + if (!pcm_dev_def) + goto free_card_def; + + for (i = 0; i < num_pcm; i++) + memcpy(&pcm_dev_def[i], &pcm_devs[i], sizeof(struct snd_dev_def)); + + card_def->pcm_dev_def = pcm_dev_def; + + /* fill mixer device node info */ + mixer_dev_def = calloc(1, sizeof(struct snd_dev_def)); + if (!mixer_dev_def) + goto free_pcm_dev; + + memcpy(mixer_dev_def, &mixer_dev, sizeof(struct snd_dev_def)); + + card_def->mixer_dev_def = mixer_dev_def; + return card_def; +free_pcm_dev: + free(pcm_dev_def); +free_card_def: + free(card_def->name); + free(card_def); + return NULL; +} + +void snd_card_def_close_card(void *card_node) +{ + struct snd_dev_def_card *defs = (struct snd_dev_def_card *)card_node; + struct snd_dev_def *pcm_dev_def = NULL; + struct snd_dev_def *mixer_dev_def = NULL; + + if (!defs) + return; + + pcm_dev_def = defs->pcm_dev_def; + if (pcm_dev_def) + free(pcm_dev_def); + + mixer_dev_def = defs->mixer_dev_def; + if (!mixer_dev_def) + goto free_defs; + + free(mixer_dev_def); +free_defs: + free(defs->name); + free(defs); +} + +void *snd_card_def_get_node(void *card_node, unsigned int id, int type) +{ + struct snd_dev_def_card *card_def = (struct snd_dev_def_card *)card_node; + struct snd_dev_def *dev_def = NULL; + int i; + + if (!card_def) + return NULL; + + if (type >= NODE_MAX) + return NULL; + + if (type == NODE_MIXER) + return card_def->mixer_dev_def; + + if (type == NODE_PCM) + dev_def = card_def->pcm_dev_def; + + for (i = 0; i < card_def->num_pcm_nodes; i++) { + if (dev_def[i].device == id) { + return &dev_def[i]; + } + } + + return NULL; +} + +int snd_card_def_get_int(void *node, const char *prop, int *val) +{ + struct snd_dev_def *dev_def = (struct snd_dev_def *)node; + int ret = -EINVAL; + + if (!dev_def || !prop || !val) + return ret; + + if (!strcmp(prop, "type")) { + *val = dev_def->type; + return 0; + } else if (!strcmp(prop, "id")) { + *val = dev_def->device; + return 0; + } else if (!strcmp(prop, "playback")) { + *val = dev_def->playback; + return 0; + } else if (!strcmp(prop, "capture")) { + *val = dev_def->capture; + return 0; + } + + return ret; +} + +int snd_card_def_get_str(void *node, const char *prop, char **val) +{ + struct snd_dev_def *dev_def = (struct snd_dev_def *)node; + int ret = -EINVAL; + + if (!dev_def || !prop) + return ret; + + if (!strcmp(prop, "so-name")) { + if (dev_def->so_name) { + *val = (char *)dev_def->so_name; + return 0; + } + } + + if (!strcmp(prop, "name")) { + if (dev_def->name) { + *val = (char *)dev_def->name; + return 0; + } + } + + return ret; +} + +void *snd_card_def_get_pcm(void *card_node, unsigned int id) +{ + return snd_card_def_get_node(card_node, id, NODE_PCM); +} + +void *snd_card_def_get_compress(void *card_node, unsigned int id) +{ + return snd_card_def_get_node(card_node, id, NODE_COMPR); +} + +void *snd_card_def_get_mixer(void *card_node) +{ + return snd_card_def_get_node(card_node, 1, NODE_MIXER); +} + +struct snd_node_ops snd_card_ops = { + .open_card = snd_card_def_open_card, + .close_card = snd_card_def_close_card, + .get_int = snd_card_def_get_int, + .get_str = snd_card_def_get_str, + .get_pcm = snd_card_def_get_pcm, + .get_compress = snd_card_def_get_compress, + .get_mixer = snd_card_def_get_mixer, +}; diff --git a/src/pcm_hw.c b/src/pcm_hw.c index 4792895..e5588a0 100644 --- a/src/pcm_hw.c +++ b/src/pcm_hw.c @@ -50,7 +50,7 @@ struct pcm_hw_data { /** Device number for the pcm device */ unsigned int device; /** File descriptor to the pcm device file node */ - unsigned int fd; + int fd; /** Pointer to the pcm node from snd card definiton */ struct snd_node *node; }; @@ -59,7 +59,7 @@ static void pcm_hw_close(void *data) { struct pcm_hw_data *hw_data = data; - if (hw_data->fd > 0) + if (hw_data->fd >= 0) close(hw_data->fd); free(hw_data); |