aboutsummaryrefslogtreecommitdiff
path: root/compress_plugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'compress_plugin.c')
-rw-r--r--compress_plugin.c423
1 files changed, 423 insertions, 0 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <poll.h>
+#include <dlfcn.h>
+
+#include <sys/ioctl.h>
+#include <linux/ioctl.h>
+#include <sound/asound.h>
+#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,
+};