From 931e7cf24bb938df75f55cd48f459c53548ce304 Mon Sep 17 00:00:00 2001 From: Vidyakumar Athota Date: Thu, 10 Dec 2020 12:05:54 +0100 Subject: [PATCH] external: tinycompress: add support for compress plugins Add plugin support to use vendor specific compress implementation Bug: 166667071 Test: manual offload playback test Test: cts AudioTrackOffloadTest CRs-Fixed: 2563258 Change-Id: I71f588b9ed84b62d3b1b08dae7ce65f8fa649e6a --- Android.bp | 3 + compress.c | 184 +++++------ compress_hw.c | 138 ++++++++ compress_ops.h | 50 +++ compress_plugin.c | 423 +++++++++++++++++++++++++ include/tinycompress/compress_plugin.h | 89 ++++++ snd_utils.c | 149 +++++++++ snd_utils.h | 72 +++++ 8 files changed, 1003 insertions(+), 105 deletions(-) create mode 100644 compress_hw.c create mode 100644 compress_ops.h create mode 100644 compress_plugin.c create mode 100644 include/tinycompress/compress_plugin.h create mode 100644 snd_utils.c create mode 100644 snd_utils.h diff --git a/Android.bp b/Android.bp index f0000f1..c11a974 100644 --- a/Android.bp +++ b/Android.bp @@ -12,6 +12,9 @@ cc_library_shared { srcs: [ "compress.c", "utils.c", + "compress_hw.c", + "compress_plugin.c", + "snd_utils.c", ], shared_libs: [ "libcutils", diff --git a/compress.c b/compress.c index 5845cae..7422c4b 100644 --- a/compress.c +++ b/compress.c @@ -75,6 +75,8 @@ #include "sound/compress_params.h" #include "sound/compress_offload.h" #include "tinycompress/tinycompress.h" +#include "compress_ops.h" +#include "snd_utils.h" #define COMPR_ERR_MAX 128 @@ -91,8 +93,15 @@ struct compress { int nonblocking; unsigned int gapless_metadata; unsigned int next_track; + + struct compress_ops *ops; + void *data; + void *snd_node; }; +extern struct compress_ops compr_hw_ops; +extern struct compress_ops compr_plug_ops; + static int oops(struct compress *compress, int e, const char *fmt, ...) { va_list ap; @@ -120,75 +129,33 @@ static struct compress bad_compress = { int is_compress_running(struct compress *compress) { - return ((compress->fd > 0) && compress->running) ? 1 : 0; + return ((compress->fd >= 0) && compress->running) ? 1 : 0; } int is_compress_ready(struct compress *compress) { - return (compress->fd > 0) ? 1 : 0; + return (compress->fd >= 0) ? 1 : 0; } static int get_compress_version(struct compress *compress) { int version = 0; - if (ioctl(compress->fd, SNDRV_COMPRESS_IOCTL_VERSION, &version)) { + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_IOCTL_VERSION, &version)) { oops(compress, errno, "cant read version"); return -1; } return version; } -static bool _is_codec_supported(struct compress *compress, struct compr_config *config, - const struct snd_compr_caps *caps) -{ - bool codec = false; - unsigned int i; - - for (i = 0; i < caps->num_codecs; i++) { - if (caps->codecs[i] == config->codec->id) { - /* found the codec */ - codec = true; - break; - } - } - if (codec == false) { - oops(compress, ENXIO, "this codec is not supported"); - return false; - } - - if (config->fragment_size < caps->min_fragment_size) { - oops(compress, EINVAL, "requested fragment size %d is below min supported %d", - config->fragment_size, caps->min_fragment_size); - return false; - } - if (config->fragment_size > caps->max_fragment_size) { - oops(compress, EINVAL, "requested fragment size %d is above max supported %d", - config->fragment_size, caps->max_fragment_size); - return false; - } - if (config->fragments < caps->min_fragments) { - oops(compress, EINVAL, "requested fragments %d are below min supported %d", - config->fragments, caps->min_fragments); - return false; - } - if (config->fragments > caps->max_fragments) { - oops(compress, EINVAL, "requested fragments %d are above max supported %d", - config->fragments, caps->max_fragments); - return false; - } - - /* TODO: match the codec properties */ - return true; -} - -static bool _is_codec_type_supported(int fd, struct snd_codec *codec) +static bool _is_codec_type_supported(struct compress_ops *ops, void *data, + struct snd_codec *codec) { struct snd_compr_caps caps; bool found = false; unsigned int i; - if (ioctl(fd, SNDRV_COMPRESS_GET_CAPS, &caps)) { + if (ops->ioctl(data, SNDRV_COMPRESS_GET_CAPS, &caps)) { oops(&bad_compress, errno, "cannot get device caps"); return false; } @@ -218,7 +185,7 @@ struct compress *compress_open(unsigned int card, unsigned int device, struct compress *compress; struct snd_compr_params params; struct snd_compr_caps caps; - char fn[256]; + int compress_type; if (!config) { oops(&bad_compress, EINVAL, "passed bad config"); @@ -237,8 +204,6 @@ struct compress *compress_open(unsigned int card, unsigned int device, if (!compress->config) goto input_fail; - snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device); - compress->max_poll_wait_ms = DEFAULT_MAX_POLL_WAIT_MS; compress->flags = flags; @@ -247,17 +212,22 @@ struct compress *compress_open(unsigned int card, unsigned int device, goto config_fail; } - if (flags & COMPRESS_OUT) { - compress->fd = open(fn, O_RDONLY); - } else { - compress->fd = open(fn, O_WRONLY); - } + compress->snd_node = snd_utils_get_dev_node(card, device, NODE_COMPRESS); + compress_type = snd_utils_get_node_type(compress->snd_node); + if (compress_type == SND_NODE_TYPE_PLUGIN) + compress->ops = &compr_plug_ops; + else + compress->ops = &compr_hw_ops; + + compress->fd = compress->ops->open(card, device, flags, + &compress->data, compress->snd_node); if (compress->fd < 0) { - oops(&bad_compress, errno, "cannot open device '%s'", fn); + oops(&bad_compress, errno, "cannot open card(%u) device(%u)", + card, device); goto config_fail; } - if (ioctl(compress->fd, SNDRV_COMPRESS_GET_CAPS, &caps)) { + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_GET_CAPS, &caps)) { oops(compress, errno, "cannot get device caps"); goto codec_fail; } @@ -281,7 +251,7 @@ struct compress *compress_open(unsigned int card, unsigned int device, memcpy(compress->config, config, sizeof(*compress->config)); fill_compress_params(config, ¶ms); - if (ioctl(compress->fd, SNDRV_COMPRESS_SET_PARAMS, ¶ms)) { + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_SET_PARAMS, ¶ms)) { oops(&bad_compress, errno, "cannot set device"); goto codec_fail; } @@ -289,7 +259,8 @@ struct compress *compress_open(unsigned int card, unsigned int device, return compress; codec_fail: - close(compress->fd); + snd_utils_put_dev_node(compress->snd_node); + compress->ops->close(compress->data); compress->fd = -1; config_fail: free(compress->config); @@ -303,8 +274,8 @@ void compress_close(struct compress *compress) if (compress == &bad_compress) return; - if (compress->fd >= 0) - close(compress->fd); + snd_utils_put_dev_node(compress->snd_node); + compress->ops->close(compress->data); compress->running = 0; compress->fd = -1; free(compress->config); @@ -320,7 +291,7 @@ int compress_get_hpointer(struct compress *compress, if (!is_compress_ready(compress)) return oops(compress, ENODEV, "device not ready"); - if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &kavail)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_AVAIL, &kavail)) return oops(compress, errno, "cannot get avail"); if (0 == kavail.tstamp.sampling_rate) return oops(compress, ENODATA, "sample rate unknown"); @@ -340,7 +311,7 @@ int compress_get_tstamp(struct compress *compress, if (!is_compress_ready(compress)) return oops(compress, ENODEV, "device not ready"); - if (ioctl(compress->fd, SNDRV_COMPRESS_TSTAMP, &ktstamp)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_TSTAMP, &ktstamp)) return oops(compress, errno, "cannot get tstamp"); *samples = ktstamp.pcm_io_frames; @@ -361,12 +332,11 @@ int compress_write(struct compress *compress, const void *buf, unsigned int size return oops(compress, EINVAL, "Invalid flag set"); if (!is_compress_ready(compress)) return oops(compress, ENODEV, "device not ready"); - fds.fd = compress->fd; fds.events = POLLOUT; /*TODO: treat auto start here first */ while (size) { - if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_AVAIL, &avail)) return oops(compress, errno, "cannot get avail"); /* We can write if we have at least one fragment available @@ -377,13 +347,14 @@ int compress_write(struct compress *compress, const void *buf, unsigned int size if (compress->nonblocking) return total; - ret = poll(&fds, 1, compress->max_poll_wait_ms); + ret = compress->ops->poll(compress->data, &fds, 1, + compress->max_poll_wait_ms); if (fds.revents & POLLERR) { return oops(compress, EIO, "poll returned error!"); } /* A pause will cause -EBADFD or zero. * This is not an error, just stop writing */ - if ((ret == 0) || (ret == -EBADFD)) + if ((ret == 0) || (ret < 0 && errno == EBADFD)) break; if (ret < 0) return oops(compress, errno, "poll error"); @@ -396,12 +367,13 @@ int compress_write(struct compress *compress, const void *buf, unsigned int size to_write = avail.avail; else to_write = size; - written = write(compress->fd, cbuf, to_write); - /* If play was paused the write returns -EBADFD */ - if (written == -EBADFD) - break; - if (written < 0) + written = compress->ops->write(compress->data, cbuf, to_write); + if (written < 0) { + /* If play was paused the write returns -EBADFD */ + if (errno == EBADFD) + break; return oops(compress, errno, "write failed!"); + } size -= written; cbuf += written; @@ -423,11 +395,10 @@ int compress_read(struct compress *compress, void *buf, unsigned int size) return oops(compress, EINVAL, "Invalid flag set"); if (!is_compress_ready(compress)) return oops(compress, ENODEV, "device not ready"); - fds.fd = compress->fd; fds.events = POLLIN; while (size) { - if (ioctl(compress->fd, SNDRV_COMPRESS_AVAIL, &avail)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_AVAIL, &avail)) return oops(compress, errno, "cannot get avail"); if ( (avail.avail < frag_size) && (avail.avail < size) ) { @@ -437,13 +408,14 @@ int compress_read(struct compress *compress, void *buf, unsigned int size) if (compress->nonblocking) return total; - ret = poll(&fds, 1, compress->max_poll_wait_ms); + ret = compress->ops->poll(compress->data, &fds, 1, + compress->max_poll_wait_ms); if (fds.revents & POLLERR) { return oops(compress, EIO, "poll returned error!"); } /* A pause will cause -EBADFD or zero. * This is not an error, just stop reading */ - if ((ret == 0) || (ret == -EBADFD)) + if ((ret == 0) || (ret < 0 && errno == EBADFD)) break; if (ret < 0) return oops(compress, errno, "poll error"); @@ -456,12 +428,13 @@ int compress_read(struct compress *compress, void *buf, unsigned int size) to_read = avail.avail; else to_read = size; - num_read = read(compress->fd, cbuf, to_read); - /* If play was paused the read returns -EBADFD */ - if (num_read == -EBADFD) - break; - if (num_read < 0) + num_read = compress->ops->read(compress->data, cbuf, to_read); + if (num_read < 0) { + /* If play was paused the read returns -EBADFD */ + if (errno == EBADFD) + break; return oops(compress, errno, "read failed!"); + } size -= num_read; cbuf += num_read; @@ -475,7 +448,7 @@ int compress_start(struct compress *compress) { if (!is_compress_ready(compress)) return oops(compress, ENODEV, "device not ready"); - if (ioctl(compress->fd, SNDRV_COMPRESS_START)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_START)) return oops(compress, errno, "cannot start the stream"); compress->running = 1; return 0; @@ -486,7 +459,7 @@ int compress_stop(struct compress *compress) { if (!is_compress_running(compress)) return oops(compress, ENODEV, "device not ready"); - if (ioctl(compress->fd, SNDRV_COMPRESS_STOP)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_STOP)) return oops(compress, errno, "cannot stop the stream"); return 0; } @@ -495,14 +468,14 @@ int compress_pause(struct compress *compress) { if (!is_compress_running(compress)) return oops(compress, ENODEV, "device not ready"); - if (ioctl(compress->fd, SNDRV_COMPRESS_PAUSE)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_PAUSE)) return oops(compress, errno, "cannot pause the stream"); return 0; } int compress_resume(struct compress *compress) { - if (ioctl(compress->fd, SNDRV_COMPRESS_RESUME)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_RESUME)) return oops(compress, errno, "cannot resume the stream"); return 0; } @@ -511,7 +484,7 @@ int compress_drain(struct compress *compress) { if (!is_compress_running(compress)) return oops(compress, ENODEV, "device not ready"); - if (ioctl(compress->fd, SNDRV_COMPRESS_DRAIN)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_DRAIN)) return oops(compress, errno, "cannot drain the stream"); return 0; } @@ -523,7 +496,7 @@ int compress_partial_drain(struct compress *compress) if (!compress->next_track) return oops(compress, EPERM, "next track not signalled"); - if (ioctl(compress->fd, SNDRV_COMPRESS_PARTIAL_DRAIN)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_PARTIAL_DRAIN)) return oops(compress, errno, "cannot drain the stream\n"); compress->next_track = 0; return 0; @@ -536,7 +509,7 @@ int compress_next_track(struct compress *compress) if (!compress->gapless_metadata) return oops(compress, EPERM, "metadata not set"); - if (ioctl(compress->fd, SNDRV_COMPRESS_NEXT_TRACK)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_NEXT_TRACK)) return oops(compress, errno, "cannot set next track\n"); compress->next_track = 1; compress->gapless_metadata = 0; @@ -561,12 +534,12 @@ int compress_set_gapless_metadata(struct compress *compress, metadata.key = SNDRV_COMPRESS_ENCODER_PADDING; metadata.value[0] = mdata->encoder_padding; - if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_SET_METADATA, &metadata)) return oops(compress, errno, "can't set metadata for stream\n"); metadata.key = SNDRV_COMPRESS_ENCODER_DELAY; metadata.value[0] = mdata->encoder_delay; - if (ioctl(compress->fd, SNDRV_COMPRESS_SET_METADATA, &metadata)) + if (compress->ops->ioctl(compress->data, SNDRV_COMPRESS_SET_METADATA, &metadata)) return oops(compress, errno, "can't set metadata for stream\n"); compress->gapless_metadata = 1; return 0; @@ -588,25 +561,27 @@ int compress_set_next_track_param(struct compress *compress, bool is_codec_supported(unsigned int card, unsigned int device, unsigned int flags, struct snd_codec *codec) { - unsigned int dev_flag; + struct compress_ops *ops; + void *snd_node, *data; bool ret; - int fd; - char fn[256]; + int compress_type, fd; - snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device); - - if (flags & COMPRESS_OUT) - dev_flag = O_RDONLY; + snd_node = snd_utils_get_dev_node(card, device, NODE_COMPRESS); + compress_type = snd_utils_get_node_type(snd_node); + if (compress_type == SND_NODE_TYPE_PLUGIN) + ops = &compr_plug_ops; else - dev_flag = O_WRONLY; + ops = &compr_hw_ops; - fd = open(fn, dev_flag); + fd = ops->open(card, device, flags, &data, NULL); if (fd < 0) - return oops(&bad_compress, errno, "cannot open device '%s'", fn); + return oops(&bad_compress, errno, "cannot open card %u, device %u", + card, device); - ret = _is_codec_type_supported(fd, codec); + ret = _is_codec_type_supported(ops, data, codec); - close(fd); + snd_utils_put_dev_node(snd_node); + ops->close(data); return ret; } @@ -625,10 +600,9 @@ int compress_wait(struct compress *compress, int timeout_ms) struct pollfd fds; int ret; - fds.fd = compress->fd; fds.events = POLLOUT | POLLIN; - ret = poll(&fds, 1, timeout_ms); + ret = compress->ops->poll(compress->data, &fds, 1, timeout_ms); if (ret > 0) { if (fds.revents & POLLERR) return oops(compress, EIO, "poll returned error!"); diff --git a/compress_hw.c b/compress_hw.c new file mode 100644 index 0000000..b2252cc --- /dev/null +++ b/compress_hw.c @@ -0,0 +1,138 @@ +/* compress_hw.c +** +** Copyright (c) 2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "tinycompress/tinycompress.h" +#include "compress_ops.h" + +struct compress_hw_data { + unsigned int card; + unsigned int device; + unsigned int fd; +}; + +static int compress_hw_poll(void *data, struct pollfd *fds, + nfds_t nfds, int timeout) +{ + struct compress_hw_data *hw_data = data; + + fds->fd = hw_data->fd; + return poll(fds, nfds, timeout); +} + +static int compress_hw_write(void *data, const void *buf, size_t size) +{ + struct compress_hw_data *hw_data = data; + + return write(hw_data->fd, buf, size); +} + +static int compress_hw_read(void *data, void *buf, size_t size) +{ + struct compress_hw_data *hw_data = data; + + return read(hw_data->fd, buf, size); +} + +static int compress_hw_ioctl(void *data, unsigned int cmd, ...) +{ + struct compress_hw_data *hw_data = data; + va_list ap; + void *arg; + + va_start(ap, cmd); + arg = va_arg(ap, void *); + va_end(ap); + + return ioctl(hw_data->fd, cmd, arg); +} + +static void compress_hw_close(void *data) +{ + struct compress_hw_data *hw_data = data; + + if (hw_data->fd > 0) + close(hw_data->fd); + + free(hw_data); +} + +static int compress_hw_open(unsigned int card, unsigned int device, + unsigned int flags, void **data, __unused void *node) +{ + struct compress_hw_data *hw_data; + char fn[256]; + int fd; + + hw_data = calloc(1, sizeof(*hw_data)); + if (!hw_data) { + return -ENOMEM; + } + + snprintf(fn, sizeof(fn), "/dev/snd/comprC%uD%u", card, device); + + if (flags & COMPRESS_OUT) + fd = open(fn, O_RDONLY); + else + fd = open(fn, O_WRONLY); + + if (fd < 0) { + return fd; + } + + hw_data->card = card; + hw_data->device = device; + hw_data->fd = fd; + + *data = hw_data; + + return fd; +} + +struct compress_ops compr_hw_ops = { + .open = compress_hw_open, + .close = compress_hw_close, + .ioctl = compress_hw_ioctl, + .read = compress_hw_read, + .write = compress_hw_write, + .poll = compress_hw_poll, +}; diff --git a/compress_ops.h b/compress_ops.h new file mode 100644 index 0000000..a3147b7 --- /dev/null +++ b/compress_ops.h @@ -0,0 +1,50 @@ +/* compress.h +** +** Copyright (c) 2019, 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. +**/ + +#ifndef __COMPRESS_H__ +#define __COMPRESS_H__ + +#include + +#include "sound/compress_params.h" +#include "sound/compress_offload.h" + +struct compress_ops { + int (*open) (unsigned int card, unsigned int device, + unsigned int flags, void **data, void *node); + void (*close) (void *data); + int (*ioctl) (void *data, unsigned int cmd, ...); + int (*read) (void *data, void *buf, size_t size); + int (*write) (void *data, const void *buf, size_t size); + int (*poll) (void *data, struct pollfd *fds, nfds_t nfds, + int timeout); +}; + +#endif /* end of __PCM_H__ */ diff --git a/compress_plugin.c b/compress_plugin.c new file mode 100644 index 0000000..d445ae9 --- /dev/null +++ b/compress_plugin.c @@ -0,0 +1,423 @@ +/* compress_plugin.c +** +** Copyright (c) 2019, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "tinycompress/compress_plugin.h" +#include "sound/compress_offload.h" +#include "compress_ops.h" +#include "snd_utils.h" + +#define U32_MAX ((uint32_t)~0U) + +enum { + COMPRESS_PLUG_STATE_OPEN, + COMPRESS_PLUG_STATE_SETUP, + COMPRESS_PLUG_STATE_PREPARED, + COMPRESS_PLUG_STATE_PAUSE, + COMPRESS_PLUG_STATE_RUNNING, +}; + +struct compress_plug_data { + unsigned int card; + unsigned int device; + unsigned int fd; + unsigned int flags; + + void *dl_hdl; + COMPRESS_PLUGIN_OPEN_FN_PTR(); + + struct compress_plugin *plugin; + void *dev_node; +}; + +static int compress_plug_get_caps(struct compress_plug_data *plug_data, + struct snd_compr_caps *caps) +{ + struct compress_plugin *plugin = plug_data->plugin; + + return plugin->ops->get_caps(plugin, caps); +} + +static int compress_plug_set_params(struct compress_plug_data *plug_data, + struct snd_compr_params *params) +{ + struct compress_plugin *plugin = plug_data->plugin; + int rc; + + if (plugin->state != COMPRESS_PLUG_STATE_OPEN) + return -EBADFD; + + if (params->buffer.fragment_size == 0 || + params->buffer.fragments > U32_MAX / params->buffer.fragment_size || + params->buffer.fragments == 0) + return -EINVAL; + + rc = plugin->ops->set_params(plugin, params); + if (!rc) + plugin->state = COMPRESS_PLUG_STATE_SETUP; + + return rc; +} + +static int compress_plug_avail(struct compress_plug_data *plug_data, + struct snd_compr_avail *avail) +{ + struct compress_plugin *plugin = plug_data->plugin; + + return plugin->ops->avail(plugin, avail); +} + +static int compress_plug_tstamp(struct compress_plug_data *plug_data, + struct snd_compr_tstamp *tstamp) +{ + struct compress_plugin *plugin = plug_data->plugin; + + if (plugin->state != COMPRESS_PLUG_STATE_SETUP) + return -EBADFD; + + return plugin->ops->tstamp(plugin, tstamp); +} + +static int compress_plug_start(struct compress_plug_data *plug_data) +{ + struct compress_plugin *plugin = plug_data->plugin; + int rc; + + /* for playback moved to prepare in first write */ + /* for capture: move to prepare state set params */ + /* TODO: add direction in set params */ + if (plugin->state != COMPRESS_PLUG_STATE_PREPARED) + return -EBADFD; + + rc = plugin->ops->start(plugin); + if (!rc) + plugin->state = COMPRESS_PLUG_STATE_RUNNING; + + return rc; +} + +static int compress_plug_stop(struct compress_plug_data *plug_data) +{ + struct compress_plugin *plugin = plug_data->plugin; + int rc; + + if (plugin->state == COMPRESS_PLUG_STATE_PREPARED || + plugin->state == COMPRESS_PLUG_STATE_SETUP) + return -EBADFD; + + rc = plugin->ops->stop(plugin); + if (!rc) + plugin->state = COMPRESS_PLUG_STATE_SETUP; + + return rc; +} + +static int compress_plug_pause(struct compress_plug_data *plug_data) +{ + struct compress_plugin *plugin = plug_data->plugin; + int rc; + + if (plugin->state != COMPRESS_PLUG_STATE_RUNNING) + return -EBADFD; + + rc = plugin->ops->pause(plugin); + if (!rc) + plugin->state = COMPRESS_PLUG_STATE_PAUSE; + + return rc; +} + +static int compress_plug_resume(struct compress_plug_data *plug_data) +{ + struct compress_plugin *plugin = plug_data->plugin; + int rc; + + if (plugin->state != COMPRESS_PLUG_STATE_PAUSE) + return -EBADFD; + + rc = plugin->ops->resume(plugin); + if (!rc) + plugin->state = COMPRESS_PLUG_STATE_RUNNING; + + return rc; +} + +static int compress_plug_drain(struct compress_plug_data *plug_data) +{ + struct compress_plugin *plugin = plug_data->plugin; + + /* check if we will allow in pause */ + if (plugin->state != COMPRESS_PLUG_STATE_RUNNING) + return -EBADFD; + + return plugin->ops->drain(plugin); +} + +static int compress_plug_partial_drain(struct compress_plug_data *plug_data) +{ + struct compress_plugin *plugin = plug_data->plugin; + + /* check if we will allow in pause */ + if (plugin->state != COMPRESS_PLUG_STATE_RUNNING) + return -EBADFD; + + return plugin->ops->partial_drain(plugin); +} + +static int compress_plug_next_track(struct compress_plug_data *plug_data) +{ + struct compress_plugin *plugin = plug_data->plugin; + + /* transion to next track applied to running stream only */ + if (plugin->state != COMPRESS_PLUG_STATE_RUNNING) + return -EBADFD; + + return plugin->ops->next_track(plugin); +} + +static int compress_plug_ioctl(void *data, unsigned int cmd, ...) +{ + struct compress_plug_data *plug_data = data; + struct compress_plugin *plugin = plug_data->plugin; + int ret = 0; + va_list ap; + void *arg; + + va_start(ap, cmd); + arg = va_arg(ap, void *); + va_end(ap); + + switch (cmd) { + case SNDRV_COMPRESS_IOCTL_VERSION: + *((int*)arg) = SNDRV_COMPRESS_VERSION; + break; + case SNDRV_COMPRESS_GET_CAPS: + ret = compress_plug_get_caps(plug_data, arg); + break; + case SNDRV_COMPRESS_SET_PARAMS: + ret = compress_plug_set_params(plug_data, arg); + break; + case SNDRV_COMPRESS_AVAIL: + ret = compress_plug_avail(plug_data, arg); + break; + case SNDRV_COMPRESS_TSTAMP: + ret = compress_plug_tstamp(plug_data, arg); + break; + case SNDRV_COMPRESS_START: + ret = compress_plug_start(plug_data); + break; + case SNDRV_COMPRESS_STOP: + ret = compress_plug_stop(plug_data); + break; + case SNDRV_COMPRESS_PAUSE: + ret = compress_plug_pause(plug_data); + break; + case SNDRV_COMPRESS_RESUME: + ret = compress_plug_resume(plug_data); + break; + case SNDRV_COMPRESS_DRAIN: + ret = compress_plug_drain(plug_data); + break; + case SNDRV_COMPRESS_PARTIAL_DRAIN: + ret = compress_plug_partial_drain(plug_data); + break; + case SNDRV_COMPRESS_NEXT_TRACK: + ret = compress_plug_next_track(plug_data); + break; + default: + if (plugin->ops->ioctl) + ret = plugin->ops->ioctl(plugin, cmd, arg); + else + ret = -EINVAL; + break; + } + + return ret; +} + +static int compress_plug_poll(void *data, struct pollfd *fds, + nfds_t nfds, int timeout) +{ + struct compress_plug_data *plug_data = data; + struct compress_plugin *plugin = plug_data->plugin; + + if (plugin->state != COMPRESS_PLUG_STATE_RUNNING) + return -EBADFD; + + return plugin->ops->poll(plugin, fds, nfds, timeout); +} + + +static int compress_plug_read(void *data, void *buf, size_t size) +{ + struct compress_plug_data *plug_data = data; + struct compress_plugin *plugin = plug_data->plugin; + + if (plugin->state != COMPRESS_PLUG_STATE_RUNNING && + plugin->state != COMPRESS_PLUG_STATE_SETUP) + return -EBADFD; + + return plugin->ops->read(plugin, buf, size); +} + +static int compress_plug_write(void *data, const void *buf, size_t size) +{ + struct compress_plug_data *plug_data = data; + struct compress_plugin *plugin = plug_data->plugin; + int rc; + + if (plugin->state != COMPRESS_PLUG_STATE_SETUP && + plugin->state != COMPRESS_PLUG_STATE_PREPARED && + plugin->state != COMPRESS_PLUG_STATE_RUNNING) + return -EBADFD; + + rc = plugin->ops->write(plugin, buf, size); + if ((rc > 0) && (plugin->state == COMPRESS_PLUG_STATE_SETUP)) + plugin->state = COMPRESS_PLUG_STATE_PREPARED; + + return rc; +} + +static void compress_plug_close(void *data) +{ + struct compress_plug_data *plug_data = data; + struct compress_plugin *plugin = plug_data->plugin; + + plugin->ops->close(plugin); + dlclose(plug_data->dl_hdl); + + free(plug_data); +} + +static int compress_plug_open(unsigned int card, unsigned int device, + unsigned int flags, void **data, void *node) +{ + struct compress_plug_data *plug_data; + const char *err = NULL; + void *dl_hdl; + int rc = 0; + char *so_name, *open_fn, token[80], *name; + + plug_data = calloc(1, sizeof(*plug_data)); + if (!plug_data) { + return -ENOMEM; + } + + rc = snd_utils_get_str(node, "so-name", &so_name); + if (rc) { + fprintf(stderr, "%s: failed to get plugin lib name\n", + __func__); + goto err_get_lib; + } + + dl_hdl = dlopen(so_name, RTLD_NOW); + if (!dl_hdl) { + fprintf(stderr, "%s: unable to open %s, error: %s\n", + __func__, so_name, dlerror()); + goto err_dl_open; + } else { + fprintf(stderr, "%s: dlopen successful for %s\n", + __func__, so_name); + } + + sscanf(so_name, "lib%s", token); + name = strtok(token, "."); + open_fn = calloc(1, strlen(name) + strlen("_open") + 1); + if (!open_fn) { + rc = -ENOMEM; + goto err_open_fn; + } + + strncpy(open_fn, name, strlen(name) + 1); + strcat(open_fn, "_open"); + + plug_data->plugin_open_fn = dlsym(dl_hdl, open_fn); + err = dlerror(); + + if (err) { + fprintf(stderr, "%s: dlsym to open fn failed, err = '%s'\n", + __func__, err); + goto err_dlsym; + } + + rc = plug_data->plugin_open_fn(&plug_data->plugin, + card, device, flags); + if (rc) { + fprintf(stderr, "%s: failed to open plugin\n", __func__); + goto err_dlsym; + } + + /* Call snd-card-def to get card and compress nodes */ + /* Check how to manage fd for plugin */ + + plug_data->dl_hdl = dl_hdl; + plug_data->card = card; + plug_data->device = device; + plug_data->dev_node = node; + plug_data->flags = flags; + + *data = plug_data; + + plug_data->plugin->state = COMPRESS_PLUG_STATE_OPEN; + + return 0; + +err_dlsym: + free(open_fn); +err_open_fn: + dlclose(dl_hdl); +err_get_lib: +err_dl_open: + free(plug_data); + + return rc; +} + +struct compress_ops compr_plug_ops = { + .open = compress_plug_open, + .close = compress_plug_close, + .ioctl = compress_plug_ioctl, + .read = compress_plug_read, + .write = compress_plug_write, + .poll = compress_plug_poll, +}; diff --git a/include/tinycompress/compress_plugin.h b/include/tinycompress/compress_plugin.h new file mode 100644 index 0000000..abd59f2 --- /dev/null +++ b/include/tinycompress/compress_plugin.h @@ -0,0 +1,89 @@ +/* compress_plugin.h +** +** Copyright (c) 2019, 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. +**/ + +#ifndef __COMPRESS_PLUGIN_H__ +#define __COMPRESS_PLUGIN_H__ + +#include "sound/compress_params.h" +#include "sound/compress_offload.h" + +#define COMPRESS_PLUGIN_OPEN_FN(name) \ + int name##_open(struct compress_plugin **plugin, \ + unsigned int card, \ + unsigned int device, \ + unsigned int flags) + +#define COMPRESS_PLUGIN_OPEN_FN_PTR() \ + int (*plugin_open_fn) (struct compress_plugin **plugin, \ + unsigned int card, \ + unsigned int device, \ + unsigned int flags); + +struct compress_plugin; + +struct compress_plugin_ops { + void (*close) (struct compress_plugin *plugin); + int (*get_caps) (struct compress_plugin *plugin, + struct snd_compr_caps *caps); + int (*set_params) (struct compress_plugin *plugin, + struct snd_compr_params *params); + int (*avail) (struct compress_plugin *plugin, + struct snd_compr_avail *avail); + int (*tstamp) (struct compress_plugin *plugin, + struct snd_compr_tstamp *tstamp); + int (*write) (struct compress_plugin *plugin, + const void *buf, size_t size); + int (*read) (struct compress_plugin *plugin, + void *buf, size_t size); + int (*start) (struct compress_plugin *plugin); + int (*stop) (struct compress_plugin *plugin); + int (*pause) (struct compress_plugin *plugin); + int (*resume) (struct compress_plugin *plugin); + int (*drain) (struct compress_plugin *plugin); + int (*partial_drain) (struct compress_plugin *plugin); + int (*next_track) (struct compress_plugin *plugin); + int (*ioctl) (struct compress_plugin *plugin, int cmd, ...); + int (*poll) (struct compress_plugin *plugin, + struct pollfd *fds, nfds_t nfds, int timeout); +}; + +struct compress_plugin { + unsigned int card; + + struct compress_plugin_ops *ops; + + void *node; + int mode; + void *priv; + + unsigned int state; +}; + +#endif /* end of __COMPRESS_PLUGIN_H__ */ diff --git a/snd_utils.c b/snd_utils.c new file mode 100644 index 0000000..2c244cc --- /dev/null +++ b/snd_utils.c @@ -0,0 +1,149 @@ +/* snd_utils.c +** +** Copyright (c) 2019, 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 +#include +#include +#include "snd_utils.h" + +#define SND_DLSYM(h, p, s, err) \ +do { \ + err = 0; \ + p = dlsym(h, s); \ + if (!p) \ + err = -ENODEV; \ +} while(0) + +int snd_utils_get_int(struct snd_node *node, const char *prop, int *val) +{ + if (!node || !node->card_node || !node->dev_node) + return SND_NODE_TYPE_HW; + + return node->get_int(node->dev_node, prop, val); +} + +int snd_utils_get_str(struct snd_node *node, const char *prop, char **val) +{ + if (!node || !node->card_node || !node->dev_node) + return SND_NODE_TYPE_HW; + + return node->get_str(node->dev_node, prop, val); +} + +void snd_utils_put_dev_node(struct snd_node *node) +{ + if (!node) + return; + + if (node->card_node) + node->put_card(node->card_node); + + if (node->dl_hdl) + dlclose(node->dl_hdl); + + free(node); +} + +enum snd_node_type snd_utils_get_node_type(struct snd_node *node) +{ + int val = SND_NODE_TYPE_HW; + + if (!node || !node->card_node || !node->dev_node) + return SND_NODE_TYPE_HW; + + node->get_int(node->dev_node, "type", &val); + + return val; +}; + + +static int snd_utils_resolve_symbols(struct snd_node *node) +{ + void *dl = node->dl_hdl; + int err; + + SND_DLSYM(dl, node->get_card, "snd_card_def_get_card", err); + if (err) + goto done; + SND_DLSYM(dl, node->put_card, "snd_card_def_put_card", err); + if (err) + goto done; + SND_DLSYM(dl, node->get_node, "snd_card_def_get_node", err); + if (err) + goto done; + SND_DLSYM(dl, node->get_int, "snd_card_def_get_int", err); + if (err) + goto done; + SND_DLSYM(dl, node->get_str, "snd_card_def_get_str", err); + +done: + return err; +} + +struct snd_node *snd_utils_get_dev_node(unsigned int card, + unsigned int device, int dev_type) +{ + struct snd_node *node; + int rc = 0; + + node = calloc(1, sizeof(*node)); + if (!node) + return NULL; + + node->dl_hdl = dlopen("libsndcardparser.so", RTLD_NOW); + if (!node->dl_hdl) { + goto err_dl_open; + } + + rc = snd_utils_resolve_symbols(node); + if (rc < 0) + goto err_resolve_symbols; + + node->card_node = node->get_card(card); + if (!node->card_node) + goto err_resolve_symbols; + + node->dev_node = node->get_node(node->card_node, + device, dev_type); + if (!node->dev_node) + goto err_get_node; + + return node; + +err_get_node: + node->put_card(node->card_node); + +err_resolve_symbols: + dlclose(node->dl_hdl); + +err_dl_open: + free(node); + return NULL; +} diff --git a/snd_utils.h b/snd_utils.h new file mode 100644 index 0000000..d00f7f2 --- /dev/null +++ b/snd_utils.h @@ -0,0 +1,72 @@ +/* snd_utils.h +** +** Copyright (c) 2019, 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. +**/ + +#ifndef __SND_CARD_UTILS_H__ +#define __SND_CARD_UTILS_H__ + +#include + +struct snd_node { + void *card_node; + void *dev_node; + void *dl_hdl; + + void* (*get_card) (unsigned int card); + void (*put_card) (void *card); + void* (*get_node) (void *card, unsigned int id, + int type); + int (*get_int) (void *node, const char *prop, int *val); + int (*get_str) (void *node, const char *prop, char **val); +}; + +enum { + NODE_PCM, + NODE_MIXER, + NODE_COMPRESS, +}; + +enum snd_node_type { + SND_NODE_TYPE_HW = 0, + SND_NODE_TYPE_PLUGIN, + SND_NODE_TYPE_INVALID, +}; + +struct snd_node *snd_utils_get_dev_node(unsigned int card, + unsigned int device, int dev_type); + +void snd_utils_put_dev_node(struct snd_node *node); + +enum snd_node_type snd_utils_get_node_type(struct snd_node *node); + +int snd_utils_get_int(struct snd_node *node, const char *prop, int *val); + +int snd_utils_get_str(struct snd_node *node, const char *prop, char **val); + +#endif /* end of __SND_CARD_UTILS_H__ */