aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBhalchandra Gajare <gajare@codeaurora.org>2021-01-19 15:57:46 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-01-19 15:57:46 +0000
commitfff4cea9bc9251c8afd6d2b187612c85f13c3bf4 (patch)
treea6b6560e253a48e79729541129d7eecf67b6dd47
parentfaba7ee47afa079937bb8969a709e7ec393084b6 (diff)
parentda302cc198d8b7f5cc60b943dd224c603318af97 (diff)
downloadtinyalsa-fff4cea9bc9251c8afd6d2b187612c85f13c3bf4.tar.gz
tinyalsa: add support for Mixer plugins am: da302cc198
Original change: https://android-review.googlesource.com/c/platform/external/tinyalsa/+/1554366 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I5627cf5f15d715222f34a3ad1f288a80cfc97536
-rw-r--r--Android.bp2
-rw-r--r--include/tinyalsa/asoundlib.h26
-rw-r--r--include/tinyalsa/mixer_plugin.h193
-rw-r--r--mixer.c465
-rw-r--r--mixer_hw.c122
-rw-r--r--mixer_io.h52
-rw-r--r--mixer_plugin.c495
7 files changed, 1266 insertions, 89 deletions
diff --git a/Android.bp b/Android.bp
index 01f1573..1b6afbf 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8,6 +8,8 @@ cc_library {
},
srcs: [
"mixer.c",
+ "mixer_hw.c",
+ "mixer_plugin.c",
"pcm.c",
"pcm_hw.c",
"pcm_plugin.c",
diff --git a/include/tinyalsa/asoundlib.h b/include/tinyalsa/asoundlib.h
index 2ca74b2..063a256 100644
--- a/include/tinyalsa/asoundlib.h
+++ b/include/tinyalsa/asoundlib.h
@@ -153,6 +153,31 @@ enum mixer_ctl_type {
MIXER_CTL_TYPE_MAX,
};
+#define CTL_ELEM_ID_NAME_MAXLEN 44
+
+typedef int ctl_elem_iface_t;
+
+struct ctl_elem_id {
+ unsigned int numid;
+ ctl_elem_iface_t iface;
+ unsigned int device;
+ unsigned int subdevice;
+ unsigned char name[CTL_ELEM_ID_NAME_MAXLEN];
+ unsigned int index;
+};
+
+
+struct ctl_event {
+ int type;
+ union {
+ struct {
+ unsigned int mask;
+ struct ctl_elem_id id;
+ } elem;
+ unsigned char data8[60];
+ } data;
+};
+
/* Open and close a stream */
struct pcm *pcm_open(unsigned int card, unsigned int device,
unsigned int flags, struct pcm_config *config);
@@ -317,6 +342,7 @@ int mixer_ctl_get_range_max(struct mixer_ctl *ctl);
int mixer_subscribe_events(struct mixer *mixer, int subscribe);
int mixer_wait_event(struct mixer *mixer, int timeout);
int mixer_consume_event(struct mixer *mixer);
+int mixer_read_event(struct mixer *mixer, struct ctl_event *ev);
#if defined(__cplusplus)
} /* extern "C" */
diff --git a/include/tinyalsa/mixer_plugin.h b/include/tinyalsa/mixer_plugin.h
new file mode 100644
index 0000000..e8ef91a
--- /dev/null
+++ b/include/tinyalsa/mixer_plugin.h
@@ -0,0 +1,193 @@
+/*
+** 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 __MIXER_PLUGIN_H__
+#define __MIXER_PLUGIN_H__
+
+#define MIXER_PLUGIN_OPEN_FN(name) \
+ int name##_open(struct mixer_plugin **plugin, \
+ unsigned int card)
+
+#define MIXER_PLUGIN_OPEN_FN_PTR() \
+ int (*mixer_plugin_open_fn) (struct mixer_plugin **plugin, \
+ unsigned int card) \
+
+struct mixer_plugin;
+
+typedef void (*event_callback)(struct mixer_plugin *);
+
+struct mixer_plugin_ops {
+ void (*close) (struct mixer_plugin **plugin);
+ int (*subscribe_events) (struct mixer_plugin *plugin,
+ event_callback event_cb);
+ ssize_t (*read_event) (struct mixer_plugin *plugin,
+ struct ctl_event *ev, size_t size);
+};
+
+struct snd_control {
+ ctl_elem_iface_t iface;
+ unsigned int access;
+ const char *name;
+ snd_ctl_elem_type_t type;
+ void *value;
+ int (*get) (struct mixer_plugin *plugin,
+ struct snd_control *control,
+ struct snd_ctl_elem_value *ev);
+ int (*put) (struct mixer_plugin *plugin,
+ struct snd_control *control,
+ struct snd_ctl_elem_value *ev);
+ uint32_t private_value;
+ void *private_data;
+};
+
+struct mixer_plugin {
+ unsigned int card;
+ struct mixer_plugin_ops *ops;
+ void *priv;
+
+ int eventfd;
+ int subscribed;
+ int event_cnt;
+
+ struct snd_control *controls;
+ unsigned int num_controls;
+};
+
+struct snd_value_enum {
+ unsigned int items;
+ char **texts;
+};
+
+struct snd_value_bytes {
+ unsigned int size;
+};
+
+struct snd_value_tlv_bytes {
+ unsigned int size;
+ int (*get) (struct mixer_plugin *plugin,
+ struct snd_control *control,
+ struct snd_ctl_tlv *tlv);
+ int (*put) (struct mixer_plugin *plugin,
+ struct snd_control *control,
+ struct snd_ctl_tlv *tlv);
+};
+
+struct snd_value_int {
+ unsigned int count;
+ int min;
+ int max;
+ int step;
+};
+
+/* static initializers */
+
+#define SND_VALUE_ENUM(etexts, eitems) \
+ {.texts = etexts, .items = eitems}
+
+#define SND_VALUE_BYTES(csize) \
+ {.size = csize }
+
+#define SND_VALUE_INTEGER(icount, imin, imax, istep) \
+ {.count = icount, .min = imin, .max = imax, .step = istep }
+
+#define SND_VALUE_TLV_BYTES(csize, cget, cput) \
+ {.size = csize, .get = cget, .put = cput }
+
+#define SND_CONTROL_ENUM(cname, cget, cput, cenum, priv_val, priv_data) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED, \
+ .name = cname, .value = &cenum, .get = cget, .put = cput, \
+ .private_value = priv_val, .private_data = priv_data, \
+ }
+
+#define SND_CONTROL_BYTES(cname, cget, cput, cbytes, priv_val, priv_data) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .type = SNDRV_CTL_ELEM_TYPE_BYTES, \
+ .name = cname, .value = &cbytes, .get = cget, .put = cput, \
+ .private_value = priv_val, .private_data = priv_data, \
+ }
+
+#define SND_CONTROL_INTEGER(cname, cget, cput, cint, priv_val, priv_data) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER, \
+ .name = cname, .value = &cint, .get = cget, .put = cput, \
+ .private_value = priv_val, .private_data = priv_data, \
+ }
+
+#define SND_CONTROL_TLV_BYTES(cname, cbytes, priv_val, priv_data) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE, \
+ .type = SNDRV_CTL_ELEM_TYPE_BYTES, \
+ .name = cname, .value = &cbytes, \
+ .private_value = priv_val, .private_data = priv_data, \
+ }
+
+/* pointer based initializers */
+#define INIT_SND_CONTROL_INTEGER(c, cname, cget, cput, cint, pval, pdata) \
+ { \
+ c->iface = SNDRV_CTL_ELEM_IFACE_MIXER; \
+ c->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; \
+ c->type = SNDRV_CTL_ELEM_TYPE_INTEGER; \
+ c->name = cname; c->value = &cint; c->get = cget; c->put = cput; \
+ c->private_value = pval; c->private_data = pdata; \
+ }
+
+#define INIT_SND_CONTROL_BYTES(c, cname, cget, cput, cint, pval, pdata) \
+ { \
+ c->iface = SNDRV_CTL_ELEM_IFACE_MIXER; \
+ c->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; \
+ c->type = SNDRV_CTL_ELEM_TYPE_BYTES; \
+ c->name = cname; c->value = &cint; c->get = cget; c->put = cput; \
+ c->private_value = pval; c->private_data = pdata; \
+ }
+
+#define INIT_SND_CONTROL_ENUM(c, cname, cget, cput, cenum, pval, pdata) \
+ { \
+ c->iface = SNDRV_CTL_ELEM_IFACE_MIXER; \
+ c->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; \
+ c->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; \
+ c->name = cname; c->value = cenum; c->get = cget; c->put = cput; \
+ c->private_value = pval; c->private_data = pdata; \
+ }
+#define INIT_SND_CONTROL_TLV_BYTES(c, cname, cbytes, priv_val, priv_data) \
+ { \
+ c->iface = SNDRV_CTL_ELEM_IFACE_MIXER; \
+ c->access = SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE; \
+ c->type = SNDRV_CTL_ELEM_TYPE_BYTES; \
+ c->name = cname; c->value = &cbytes; \
+ c->private_value = priv_val; c->private_data = priv_data; \
+ }
+#endif /* end of __MIXER_PLUGIN_H__ */
diff --git a/mixer.c b/mixer.c
index f3fdb62..87747c5 100644
--- a/mixer.c
+++ b/mixer.c
@@ -40,6 +40,7 @@
#include <sys/ioctl.h>
#include <linux/ioctl.h>
+
#define __force
#define __bitwise
#define __user
@@ -50,122 +51,258 @@
#endif
#include <tinyalsa/asoundlib.h>
+#include "mixer_io.h"
struct mixer_ctl {
- struct mixer *mixer;
+ struct mixer_ctl_group *grp;
struct snd_ctl_elem_info *info;
char **ename;
bool info_retrieved;
};
-struct mixer {
- int fd;
- struct snd_ctl_card_info card_info;
+struct mixer_ctl_group {
struct snd_ctl_elem_info *elem_info;
struct mixer_ctl *ctl;
unsigned int count;
+ int event_cnt;
+
+ struct mixer_ops *ops;
+ void *data;
};
-void mixer_close(struct mixer *mixer)
+struct mixer {
+ int fd;
+ struct snd_ctl_card_info card_info;
+
+ /* hardware/physical mixer control group */
+ struct mixer_ctl_group *hw_grp;
+
+ /*
+ * Virutal mixer control group.
+ * Currently supports one virtual mixer (.so)
+ * per card. Could be extended to multiple
+ */
+ struct mixer_ctl_group *virt_grp;
+
+ unsigned int total_ctl_count;
+};
+
+static void mixer_grp_close(struct mixer_ctl_group *grp)
{
- unsigned int n,m;
+ unsigned int n, m;
- if (!mixer)
+ if (!grp)
return;
- if (mixer->fd >= 0)
- close(mixer->fd);
-
- if (mixer->ctl) {
- for (n = 0; n < mixer->count; n++) {
- if (mixer->ctl[n].ename) {
- unsigned int max = mixer->ctl[n].info->value.enumerated.items;
+ if (grp->ctl) {
+ for (n = 0; n < grp->count; n++) {
+ if (grp->ctl[n].ename) {
+ unsigned int max = grp->ctl[n].info->value.enumerated.items;
for (m = 0; m < max; m++)
- free(mixer->ctl[n].ename[m]);
- free(mixer->ctl[n].ename);
+ free(grp->ctl[n].ename[m]);
+ free(grp->ctl[n].ename);
}
}
- free(mixer->ctl);
+ free(grp->ctl);
}
- if (mixer->elem_info)
- free(mixer->elem_info);
+ if (grp->elem_info)
+ free(grp->elem_info);
+
+ free(grp);
+}
+
+void mixer_close(struct mixer *mixer)
+{
+ if (!mixer)
+ return;
+
+ if (mixer->fd >= 0 && mixer->hw_grp)
+ mixer->hw_grp->ops->close(mixer->hw_grp->data);
+ mixer_grp_close(mixer->hw_grp);
+
+ if (mixer->virt_grp)
+ mixer->virt_grp->ops->close(mixer->virt_grp->data);
+ mixer_grp_close(mixer->virt_grp);
free(mixer);
/* TODO: verify frees */
}
-struct mixer *mixer_open(unsigned int card)
+static int mixer_grp_open(struct mixer_ctl_group **ctl_grp,
+ struct mixer_ops *ops,
+ void *data, int *num_ctls_in_grp)
{
+ struct mixer_ctl_group *grp;
struct snd_ctl_elem_list elist;
struct snd_ctl_elem_id *eid = NULL;
- struct mixer *mixer = NULL;
unsigned int n;
- int fd;
- char fn[256];
+ int ret;
- snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
- fd = open(fn, O_RDWR);
- if (fd < 0)
- return 0;
+ grp = calloc(1, sizeof(*grp));
+ if (!grp)
+ return -ENOMEM;
memset(&elist, 0, sizeof(elist));
- if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
- goto fail;
-
- mixer = calloc(1, sizeof(*mixer));
- if (!mixer)
- goto fail;
-
- mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
- mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
- if (!mixer->ctl || !mixer->elem_info)
- goto fail;
+ ret = ops->ioctl(data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist);
+ if (ret < 0)
+ goto err_get_elem_list;
- if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
- goto fail;
+ grp->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
+ grp->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
+ if (!grp->ctl || !grp->elem_info) {
+ ret = -ENOMEM;
+ goto err_ctl_alloc;
+ }
- eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
- if (!eid)
- goto fail;
+ eid = calloc(elist.count, sizeof(*eid));
+ if (!eid) {
+ ret = -ENOMEM;
+ goto err_ctl_alloc;
+ }
- mixer->count = elist.count;
- mixer->fd = fd;
- elist.space = mixer->count;
+ grp->count = elist.count;
+ elist.space = grp->count;
elist.pids = eid;
- if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
- goto fail;
+ ret = ops->ioctl(data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist);
+ if (ret < 0)
+ goto err_ctl_alloc;
- for (n = 0; n < mixer->count; n++) {
- struct mixer_ctl *ctl = mixer->ctl + n;
+ for (n = 0; n < grp->count; n++) {
+ struct mixer_ctl *ctl = grp->ctl + n;
- ctl->mixer = mixer;
- ctl->info = mixer->elem_info + n;
+ ctl->grp = grp;
+ ctl->info = grp->elem_info + n;
ctl->info->id.numid = eid[n].numid;
strncpy((char *)ctl->info->id.name, (char *)eid[n].name,
SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
ctl->info->id.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
}
+ grp->data = data;
+ grp->ops = ops;
+ *ctl_grp = grp;
+ *num_ctls_in_grp = grp->count;
+
+ free(eid);
+ return 0;
+
+err_ctl_alloc:
+
free(eid);
+ free(grp->elem_info);
+ free(grp->ctl);
+
+err_get_elem_list:
+
+ free(grp);
+ return ret;
+
+}
+
+static int mixer_do_hw_open(struct mixer *mixer, unsigned int card)
+{
+ struct mixer_ops *ops;
+ void *data;
+ int fd, ret, num_grp_ctls = 0;
+
+ mixer->fd = -1;
+ fd = mixer_hw_open(card, &data, &ops);
+ if (fd < 0)
+ return fd;
+
+ ret = ops->ioctl(data, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info);
+ if (ret < 0)
+ goto err_card_info;
+
+ ret = mixer_grp_open(&mixer->hw_grp, ops, data, &num_grp_ctls);
+ if (ret < 0)
+ goto err_card_info;
+
+ mixer->total_ctl_count += num_grp_ctls;
+
+ mixer->fd = fd;
+ return 0;
+
+err_card_info:
+ ops->close(data);
+ return ret;
+
+}
+
+static int mixer_do_plugin_open(struct mixer *mixer, unsigned int card,
+ int is_hw_open_failed)
+{
+ struct mixer_ops *ops;
+ void *data;
+ int ret, num_grp_ctls = 0;
+
+ ret = mixer_plugin_open(card, &data, &ops);
+ if (ret < 0)
+ return ret;
+
+ /* Get card_info if hw_open failed */
+ if (is_hw_open_failed) {
+ ret = ops->ioctl(data, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info);
+ if (ret < 0)
+ goto err_card_info;
+ }
+
+ ret = mixer_grp_open(&mixer->virt_grp, ops, data, &num_grp_ctls);
+ if (ret < 0)
+ goto err_card_info;
+
+ mixer->total_ctl_count += num_grp_ctls;
+ return 0;
+
+err_card_info:
+ ops->close(data);
+ return ret;
+
+}
+
+struct mixer *mixer_open(unsigned int card)
+{
+ struct mixer *mixer = NULL;
+ int h_status, v_status;
+
+ mixer = calloc(1, sizeof(*mixer));
+ if (!mixer)
+ goto fail;
+
+ /* open hardware node first */
+ h_status = mixer_do_hw_open(mixer, card);
+
+ /*
+ * open the virtual node even if hw_open fails
+ * since hw_open is expected to fail for virtual cards
+ * for which kernel does not register mixer node
+ */
+ //TODO: open virtual node only if mixer is defined under snd-card-def
+ v_status = mixer_do_plugin_open(mixer, card, h_status);
+
+ /* Fail mixer_open if both hw and plugin nodes cannot be opened */
+ if (h_status < 0 && v_status < 0)
+ goto fail;
+
return mixer;
fail:
- /* TODO: verify frees in failure case */
- if (eid)
- free(eid);
if (mixer)
mixer_close(mixer);
- else if (fd >= 0)
- close(fd);
+
return 0;
}
static bool mixer_ctl_get_elem_info(struct mixer_ctl* ctl)
{
+ struct mixer_ctl_group *grp = ctl->grp;
+ unsigned int i;
+
if (!ctl->info_retrieved) {
- if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info) < 0)
+ if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO,
+ ctl->info) < 0)
return false;
ctl->info_retrieved = true;
}
@@ -178,11 +315,11 @@ static bool mixer_ctl_get_elem_info(struct mixer_ctl* ctl)
if (!enames)
return false;
- for (unsigned int i = 0; i < ctl->info->value.enumerated.items; i++) {
+ for (i = 0; i < ctl->info->value.enumerated.items; i++) {
memset(&tmp, 0, sizeof(tmp));
tmp.id.numid = ctl->info->id.numid;
tmp.value.enumerated.item = i;
- if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
+ if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
goto fail;
enames[i] = strdup(tmp.value.enumerated.name);
if (!enames[i])
@@ -206,17 +343,35 @@ unsigned int mixer_get_num_ctls(struct mixer *mixer)
if (!mixer)
return 0;
- return mixer->count;
+ return mixer->total_ctl_count;
+}
+
+static int mixer_grp_get_count(struct mixer_ctl_group *grp)
+{
+ if (!grp)
+ return 0;
+
+ return grp->count;
}
struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
{
struct mixer_ctl *ctl;
+ unsigned int hw_ctl_count, virt_ctl_count;
+
+ if (!mixer || (id >= mixer->total_ctl_count))
+ return NULL;
+
+ hw_ctl_count = mixer_grp_get_count(mixer->hw_grp);
+ virt_ctl_count = mixer_grp_get_count(mixer->virt_grp);
- if (!mixer || (id >= mixer->count))
+ if (id < hw_ctl_count)
+ ctl = mixer->hw_grp->ctl + id;
+ else if ((id - hw_ctl_count) < virt_ctl_count)
+ ctl = mixer->virt_grp->ctl + (id - hw_ctl_count);
+ else
return NULL;
- ctl = mixer->ctl + id;
if (!mixer_ctl_get_elem_info(ctl))
return NULL;
@@ -225,21 +380,41 @@ struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
{
+ struct mixer_ctl_group *grp;
unsigned int n;
+ int hw_ctl_count = mixer_grp_get_count(mixer->hw_grp);
if (!mixer)
return NULL;
- for (n = 0; n < mixer->count; n++)
- if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
- return mixer_get_ctl(mixer, n);
+ if (mixer->hw_grp) {
+ grp = mixer->hw_grp;
+
+ for (n = 0; n < grp->count; n++)
+ if (!strcmp(name, (char*) grp->elem_info[n].id.name))
+ return mixer_get_ctl(mixer, n);
+ }
+
+ if (mixer->virt_grp) {
+ grp = mixer->virt_grp;
+
+ for (n = 0; n < grp->count; n++)
+ if (!strcmp(name, (char*) grp->elem_info[n].id.name))
+ return mixer_get_ctl(mixer, n + hw_ctl_count);
+ }
return NULL;
}
void mixer_ctl_update(struct mixer_ctl *ctl)
{
- ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
+ struct mixer_ctl_group *grp;
+
+ if (!ctl || !ctl->grp)
+ return;
+ grp = ctl->grp;
+
+ grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
}
const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
@@ -332,6 +507,7 @@ int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
{
+ struct mixer_ctl_group *grp = ctl->grp;
struct snd_ctl_elem_value ev;
int ret;
@@ -340,7 +516,7 @@ int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
memset(&ev, 0, sizeof(ev));
ev.id.numid = ctl->info->id.numid;
- ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+ ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
if (ret < 0)
return ret;
@@ -371,6 +547,7 @@ int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl)
int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
{
+ struct mixer_ctl_group *grp = ctl->grp;
struct snd_ctl_elem_value ev;
int ret = 0;
size_t size;
@@ -397,7 +574,7 @@ int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
switch (ctl->info->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
- ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+ ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
if (ret < 0)
return ret;
size = sizeof(ev.value.integer.value[0]);
@@ -417,7 +594,7 @@ int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
return -ENOMEM;
tlv->numid = ctl->info->id.numid;
tlv->length = count;
- ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv);
+ ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_READ, tlv);
source = tlv->tlv;
memcpy(array, source, count);
@@ -426,7 +603,7 @@ int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
return ret;
} else {
- ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+ ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
if (ret < 0)
return ret;
size = sizeof(ev.value.bytes.data[0]);
@@ -450,6 +627,8 @@ int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
{
+ struct mixer_ctl_group *grp = ctl->grp;
+
struct snd_ctl_elem_value ev;
int ret;
@@ -458,7 +637,7 @@ int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
memset(&ev, 0, sizeof(ev));
ev.id.numid = ctl->info->id.numid;
- ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
+ ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
if (ret < 0)
return ret;
@@ -483,11 +662,12 @@ int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
return -EINVAL;
}
- return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+ return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
}
int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
{
+ struct mixer_ctl_group *grp = ctl->grp;
struct snd_ctl_elem_value ev;
size_t size;
void *dest;
@@ -531,7 +711,7 @@ int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
tlv->length = count;
memcpy(tlv->tlv, array, count);
- ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
+ ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
free(tlv);
return ret;
@@ -552,7 +732,7 @@ int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
memcpy(dest, array, size * count);
- return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+ return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
}
int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
@@ -591,6 +771,7 @@ const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
{
+ struct mixer_ctl_group *grp = ctl->grp;
unsigned int i, num_enums;
struct snd_ctl_elem_value ev;
int ret;
@@ -604,7 +785,7 @@ int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
memset(&ev, 0, sizeof(ev));
ev.value.enumerated.item[0] = i;
ev.id.numid = ctl->info->id.numid;
- ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
+ ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
if (ret < 0)
return ret;
return 0;
@@ -623,8 +804,22 @@ int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
*/
int mixer_subscribe_events(struct mixer *mixer, int subscribe)
{
- if (ioctl(mixer->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
- return -1;
+ struct mixer_ctl_group *grp;
+
+ if (mixer->hw_grp) {
+ grp = mixer->hw_grp;
+ if (!subscribe)
+ grp->event_cnt = 0;
+ if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
+ return -1;
+ }
+
+ if (mixer->virt_grp) {
+ grp = mixer->virt_grp;
+ if (!subscribe)
+ grp->event_cnt = 0;
+ if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
+ return -1;
}
return 0;
}
@@ -639,22 +834,58 @@ int mixer_subscribe_events(struct mixer *mixer, int subscribe)
*/
int mixer_wait_event(struct mixer *mixer, int timeout)
{
- struct pollfd pfd;
+ struct pollfd pfd[2];
+ struct mixer_ctl_group *grp;
+ int count = 0, num_fds = 0, i;
+
+ memset(pfd, 0, sizeof(struct pollfd) * 2);
+
+ if (mixer->fd >= 0)
+ num_fds++;
+
+ if (mixer->virt_grp)
+ num_fds++;
+
+
+ /* TODO wait events for virt fd */
+ if (mixer->fd >= 0) {
+ pfd[count].fd = mixer->fd;
+ pfd[count].events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
+ count++;
+ }
+
+ if (mixer->virt_grp) {
+ grp = mixer->virt_grp;
+ if (!grp->ops->get_poll_fd(grp->data, pfd, count)) {
+ pfd[count].events = POLLIN | POLLERR | POLLNVAL;
+ count++;
+ }
+ }
- pfd.fd = mixer->fd;
- pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
+ if (!count)
+ return 0;
for (;;) {
int err;
- err = poll(&pfd, 1, timeout);
+ err = poll(pfd, count, timeout);
if (err < 0)
return -errno;
if (!err)
return 0;
- if (pfd.revents & (POLLERR | POLLNVAL))
- return -EIO;
- if (pfd.revents & (POLLIN | POLLOUT))
- return 1;
+ for (i = 0; i < count; i++) {
+ if (pfd[i].revents & (POLLERR | POLLNVAL))
+ return -EIO;
+ if (pfd[i].revents & (POLLIN | POLLOUT)) {
+ if ((i == 0) && mixer->fd >= 0) {
+ grp = mixer->hw_grp;
+ grp->event_cnt++;
+ } else {
+ grp = mixer->virt_grp;
+ grp->event_cnt++;
+ }
+ return 1;
+ }
+ }
}
}
@@ -668,13 +899,69 @@ int mixer_wait_event(struct mixer *mixer, int timeout)
* @returns 0 on success. -errno on failure.
* @ingroup libtinyalsa-mixer
*/
-int mixer_consume_event(struct mixer *mixer) {
+int mixer_consume_event(struct mixer *mixer)
+{
struct snd_ctl_event ev;
- ssize_t count = read(mixer->fd, &ev, sizeof(ev));
+ struct mixer_ctl_group *grp;
+ ssize_t count = 0;
+
// Exporting the actual event would require exposing snd_ctl_event
// via the header file, and all associated structs.
// The events generally tell you exactly which value changed,
// but reading values you're interested isn't hard and simplifies
// the interface greatly.
- return (count >= 0) ? 0 : -errno;
+ if (mixer->hw_grp) {
+ grp = mixer->hw_grp;
+ if (grp->event_cnt) {
+ grp->event_cnt--;
+ count = grp->ops->read_event(grp->data, &ev, sizeof(ev));
+ return (count >= 0) ? 0 : -errno;
+ }
+ }
+
+ if (mixer->virt_grp) {
+ grp = mixer->virt_grp;
+ if (grp->event_cnt) {
+ grp->event_cnt--;
+ count += grp->ops->read_event(grp->data, &ev, sizeof(ev));
+ return (count >= 0) ? 0 : -errno;
+ }
+ }
+ return 0;
+}
+
+/** Read a mixer event.
+ * If mixer_subscribe_events has been called,
+ * mixer_wait_event will identify when a control value has changed.
+ * This function will read and clear a single event from the mixer
+ * so that further events can be alerted.
+ *
+ * @param mixer A mixer handle.
+ * @param ev snd_ctl_event pointer where event needs to be read
+ * @returns 0 on success. -errno on failure.
+ * @ingroup libtinyalsa-mixer
+ */
+int mixer_read_event(struct mixer *mixer, struct ctl_event *ev)
+{
+ struct mixer_ctl_group *grp;
+ ssize_t count = 0;
+
+ if (mixer->hw_grp) {
+ grp = mixer->hw_grp;
+ if (grp->event_cnt) {
+ grp->event_cnt--;
+ count = grp->ops->read_event(grp->data, (struct snd_ctl_event *)ev, sizeof(*ev));
+ return (count >= 0) ? 0 : -errno;
+ }
+ }
+
+ if (mixer->virt_grp) {
+ grp = mixer->virt_grp;
+ if (grp->event_cnt) {
+ grp->event_cnt--;
+ count = grp->ops->read_event(grp->data, (struct snd_ctl_event *)ev, sizeof(*ev));
+ return (count >= 0) ? 0 : -errno;
+ }
+ }
+ return 0;
}
diff --git a/mixer_hw.c b/mixer_hw.c
new file mode 100644
index 0000000..9e01647
--- /dev/null
+++ b/mixer_hw.c
@@ -0,0 +1,122 @@
+/* mixer_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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <poll.h>
+
+#include <sys/ioctl.h>
+
+#include <linux/ioctl.h>
+#include <sound/asound.h>
+
+#include "mixer_io.h"
+
+struct mixer_hw_data {
+ unsigned int card;
+ int fd;
+};
+
+static void mixer_hw_close(void *data)
+{
+ struct mixer_hw_data *hw_data = data;
+
+ if (!hw_data)
+ return;
+
+ if (hw_data->fd >= 0)
+ close(hw_data->fd);
+
+ hw_data->fd = -1;
+ free(hw_data);
+ hw_data = NULL;
+}
+
+static int mixer_hw_ioctl(void *data, unsigned int cmd, ...)
+{
+ struct mixer_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 ssize_t mixer_hw_read_event(void *data, struct snd_ctl_event *ev,
+ size_t size)
+{
+ struct mixer_hw_data *hw_data = data;
+
+ return read(hw_data->fd, ev, size);
+}
+
+static struct mixer_ops mixer_hw_ops = {
+ .close = mixer_hw_close,
+ .get_poll_fd = NULL,
+ .read_event = mixer_hw_read_event,
+ .ioctl = mixer_hw_ioctl,
+};
+
+int mixer_hw_open(unsigned int card, void **data,
+ struct mixer_ops **ops)
+{
+ struct mixer_hw_data *hw_data;
+ int fd;
+ char fn[256];
+
+ snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
+ fd = open(fn, O_RDWR);
+ if (fd < 0)
+ return fd;
+
+ hw_data = calloc(1, sizeof(*hw_data));
+ if (!hw_data) {
+ close(fd);
+ return -1;
+ }
+
+ hw_data->card = card;
+ hw_data->fd = fd;
+ *data = hw_data;
+ *ops = &mixer_hw_ops;
+
+ return fd;
+}
diff --git a/mixer_io.h b/mixer_io.h
new file mode 100644
index 0000000..77daae0
--- /dev/null
+++ b/mixer_io.h
@@ -0,0 +1,52 @@
+/* mixer_io.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 __MIXER_H__
+#define __MIXER_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sound/asound.h>
+
+struct mixer_ops;
+
+int mixer_hw_open(unsigned int card, void **data,
+ struct mixer_ops **ops);
+int mixer_plugin_open(unsigned int card, void **data,
+ struct mixer_ops **ops);
+
+struct mixer_ops {
+ void (*close) (void *data);
+ int (*get_poll_fd) (void *data, struct pollfd *pfd, int count);
+ ssize_t (*read_event) (void *data, struct snd_ctl_event *ev, size_t size);
+ int (*ioctl) (void *data, unsigned int cmd, ...);
+};
+
+#endif /* end of __MIXER_H__ */
diff --git a/mixer_plugin.c b/mixer_plugin.c
new file mode 100644
index 0000000..a5e0660
--- /dev/null
+++ b/mixer_plugin.c
@@ -0,0 +1,495 @@
+/* mixer_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 <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <poll.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <sys/eventfd.h>
+#include <sys/ioctl.h>
+
+#include <linux/ioctl.h>
+#include <sound/asound.h>
+
+#include <tinyalsa/asoundlib.h>
+#include <tinyalsa/mixer_plugin.h>
+#include "snd_utils.h"
+
+#include "mixer_io.h"
+
+struct mixer_plug_data {
+ int card;
+ void *mixer_node;
+
+ struct mixer_plugin *plugin;
+ void *dl_hdl;
+ MIXER_PLUGIN_OPEN_FN_PTR();
+};
+
+static int mixer_plug_get_elem_id(struct mixer_plug_data *plug_data,
+ struct snd_ctl_elem_id *id, unsigned int offset)
+{
+ struct mixer_plugin *plugin = plug_data->plugin;
+ struct snd_control *ctl;
+
+ if (offset >= plugin->num_controls) {
+ printf("%s: invalid offset %u\n", __func__, offset);
+ return -EINVAL;
+ }
+
+ ctl = plugin->controls + offset;
+ id->numid = offset;
+ id->iface = ctl->iface;
+
+ strncpy((char *)id->name, (char *)ctl->name,
+ sizeof(id->name));
+
+ return 0;
+}
+
+static int mixer_plug_info_enum(struct snd_control *ctl,
+ struct snd_ctl_elem_info *einfo)
+{
+ struct snd_value_enum *val = ctl->value;
+
+ einfo->count = 1;
+ einfo->value.enumerated.items = val->items;
+
+ if (einfo->value.enumerated.item > val->items)
+ return -EINVAL;
+
+ strncpy(einfo->value.enumerated.name,
+ val->texts[einfo->value.enumerated.item],
+ sizeof(einfo->value.enumerated.name));
+
+ return 0;
+}
+
+static int mixer_plug_info_bytes(struct snd_control *ctl,
+ struct snd_ctl_elem_info *einfo)
+{
+ struct snd_value_bytes *val;
+ struct snd_value_tlv_bytes *val_tlv;
+
+ if (ctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
+ val_tlv = ctl->value;
+ einfo->count = val_tlv->size;
+ } else {
+ val = ctl->value;
+ einfo->count = val->size;
+ }
+
+ return 0;
+}
+
+static int mixer_plug_info_integer(struct snd_control *ctl,
+ struct snd_ctl_elem_info *einfo)
+{
+ struct snd_value_int *val = ctl->value;
+
+ einfo->count = val->count;
+ einfo->value.integer.min = val->min;
+ einfo->value.integer.max = val->max;
+ einfo->value.integer.step = val->step;
+
+ return 0;
+}
+
+void mixer_plug_notifier_cb(struct mixer_plugin *plugin)
+{
+ eventfd_write(plugin->eventfd, 1);
+ plugin->event_cnt++;
+}
+
+/* In consume_event/read, do not call eventfd_read until all events are read from list.
+ This will make poll getting unblocked until all events are read */
+static ssize_t mixer_plug_read_event(void *data, struct snd_ctl_event *ev, size_t size)
+{
+ struct mixer_plug_data *plug_data = data;
+ struct mixer_plugin *plugin = plug_data->plugin;
+ eventfd_t evfd;
+ ssize_t result = 0;
+
+ result = plugin->ops->read_event(plugin, (struct ctl_event *)ev, size);
+
+ if (result > 0) {
+ plugin->event_cnt -= result / sizeof(struct snd_ctl_event);
+ if (plugin->event_cnt == 0)
+ eventfd_read(plugin->eventfd, &evfd);
+ }
+
+ return result;
+}
+
+static int mixer_plug_subscribe_events(struct mixer_plug_data *plug_data,
+ int *subscribe)
+{
+ struct mixer_plugin *plugin = plug_data->plugin;
+ eventfd_t evfd;
+
+ if (*subscribe < 0 || *subscribe > 1) {
+ *subscribe = plugin->subscribed;
+ return -EINVAL;
+ }
+
+ if (*subscribe && !plugin->subscribed) {
+ plugin->ops->subscribe_events(plugin, &mixer_plug_notifier_cb);
+ } else if (plugin->subscribed && !*subscribe) {
+ plugin->ops->subscribe_events(plugin, NULL);
+
+ if (plugin->event_cnt)
+ eventfd_read(plugin->eventfd, &evfd);
+
+ plugin->event_cnt = 0;
+ }
+
+ plugin->subscribed = *subscribe;
+ return 0;
+}
+
+static int mixer_plug_get_poll_fd(void *data, struct pollfd *pfd, int count)
+{
+ struct mixer_plug_data *plug_data = data;
+ struct mixer_plugin *plugin = plug_data->plugin;
+
+ if (plugin->eventfd != -1) {
+ pfd[count].fd = plugin->eventfd;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int mixer_plug_tlv_write(struct mixer_plug_data *plug_data,
+ struct snd_ctl_tlv *tlv)
+{
+ struct mixer_plugin *plugin = plug_data->plugin;
+ struct snd_control *ctl;
+ struct snd_value_tlv_bytes *val_tlv;
+
+ ctl = plugin->controls + tlv->numid;
+ val_tlv = ctl->value;
+
+ return val_tlv->put(plugin, ctl, tlv);
+}
+
+static int mixer_plug_tlv_read(struct mixer_plug_data *plug_data,
+ struct snd_ctl_tlv *tlv)
+{
+ struct mixer_plugin *plugin = plug_data->plugin;
+ struct snd_control *ctl;
+ struct snd_value_tlv_bytes *val_tlv;
+
+ ctl = plugin->controls + tlv->numid;
+ val_tlv = ctl->value;
+
+ return val_tlv->get(plugin, ctl, tlv);
+}
+
+static int mixer_plug_elem_write(struct mixer_plug_data *plug_data,
+ struct snd_ctl_elem_value *ev)
+{
+ struct mixer_plugin *plugin = plug_data->plugin;
+ struct snd_control *ctl;
+ int ret;
+
+ ret = mixer_plug_get_elem_id(plug_data, &ev->id, ev->id.numid);
+ if (ret < 0)
+ return ret;
+
+ ctl = plugin->controls + ev->id.numid;
+
+ return ctl->put(plugin, ctl, ev);
+}
+
+static int mixer_plug_elem_read(struct mixer_plug_data *plug_data,
+ struct snd_ctl_elem_value *ev)
+{
+ struct mixer_plugin *plugin = plug_data->plugin;
+ struct snd_control *ctl;
+ int ret;
+
+ ret = mixer_plug_get_elem_id(plug_data, &ev->id, ev->id.numid);
+ if (ret < 0)
+ return ret;
+
+ ctl = plugin->controls + ev->id.numid;
+
+ return ctl->get(plugin, ctl, ev);
+
+}
+
+static int mixer_plug_get_elem_info(struct mixer_plug_data *plug_data,
+ struct snd_ctl_elem_info *einfo)
+{
+ struct mixer_plugin *plugin = plug_data->plugin;
+ struct snd_control *ctl;
+ int ret;
+
+ ret = mixer_plug_get_elem_id(plug_data, &einfo->id,
+ einfo->id.numid);
+ if (ret < 0)
+ return ret;
+
+ ctl = plugin->controls + einfo->id.numid;
+ einfo->type = ctl->type;
+ einfo->access = ctl->access;
+
+ switch (einfo->type) {
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ ret = mixer_plug_info_enum(ctl, einfo);
+ if (ret < 0)
+ return ret;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_BYTES:
+ ret = mixer_plug_info_bytes(ctl, einfo);
+ if (ret < 0)
+ return ret;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ ret = mixer_plug_info_integer(ctl, einfo);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ printf("%s: unknown type %d\n", __func__, einfo->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mixer_plug_get_elem_list(struct mixer_plug_data *plug_data,
+ struct snd_ctl_elem_list *elist)
+{
+ struct mixer_plugin *plugin = plug_data->plugin;
+ unsigned int avail;
+ struct snd_ctl_elem_id *id;
+ int ret;
+
+ elist->count = plugin->num_controls;
+ elist->used = 0;
+ avail = elist->space;
+
+ while (avail > 0) {
+ id = elist->pids + elist->used;
+ ret = mixer_plug_get_elem_id(plug_data, id, elist->used);
+ if (ret < 0)
+ return ret;
+
+ avail--;
+ elist->used++;
+ }
+
+ return 0;
+}
+
+static int mixer_plug_get_card_info(struct mixer_plug_data *plug_data,
+ struct snd_ctl_card_info *card_info)
+{
+ /*TODO: Fill card_info here from snd-card-def */
+ memset(card_info, 0, sizeof(*card_info));
+ card_info->card = plug_data->card;
+ memcpy(card_info->id, "card_id", sizeof(card_info->id));
+ memcpy(card_info->driver, "mymixer-so-name", sizeof(card_info->driver));
+ memcpy(card_info->name, "card-name", sizeof(card_info->name));
+ memcpy(card_info->longname, "card-name", sizeof(card_info->longname));
+ memcpy(card_info->mixername, "mixer-name", sizeof(card_info->mixername));
+
+ printf("%s: card = %d\n", __func__, plug_data->card);
+
+ return 0;
+}
+
+static void mixer_plug_close(void *data)
+{
+ struct mixer_plug_data *plug_data = data;
+ struct mixer_plugin *plugin = plug_data->plugin;
+ eventfd_t evfd;
+
+ if (plugin->event_cnt)
+ eventfd_read(plugin->eventfd, &evfd);
+
+ plugin->ops->close(&plugin);
+ dlclose(plug_data->dl_hdl);
+ snd_utils_put_dev_node(plug_data->mixer_node);
+ free(plug_data);
+ plug_data = NULL;
+}
+
+static int mixer_plug_ioctl(void *data, unsigned int cmd, ...)
+{
+ struct mixer_plug_data *plug_data = data;
+ int ret;
+ va_list ap;
+ void *arg;
+
+ va_start(ap, cmd);
+ arg = va_arg(ap, void *);
+ va_end(ap);
+
+ switch (cmd) {
+ case SNDRV_CTL_IOCTL_CARD_INFO:
+ ret = mixer_plug_get_card_info(plug_data, arg);
+ break;
+ case SNDRV_CTL_IOCTL_ELEM_LIST:
+ ret = mixer_plug_get_elem_list(plug_data, arg);
+ break;
+ case SNDRV_CTL_IOCTL_ELEM_INFO:
+ ret = mixer_plug_get_elem_info(plug_data, arg);
+ break;
+ case SNDRV_CTL_IOCTL_ELEM_READ:
+ ret = mixer_plug_elem_read(plug_data, arg);
+ break;
+ case SNDRV_CTL_IOCTL_ELEM_WRITE:
+ ret = mixer_plug_elem_write(plug_data, arg);
+ break;
+ case SNDRV_CTL_IOCTL_TLV_READ:
+ ret = mixer_plug_tlv_read(plug_data, arg);
+ break;
+ case SNDRV_CTL_IOCTL_TLV_WRITE:
+ ret = mixer_plug_tlv_write(plug_data, arg);
+ break;
+ case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
+ ret = mixer_plug_subscribe_events(plug_data, arg);
+ break;
+ default:
+ /* TODO: plugin should support ioctl */
+ ret = -EFAULT;
+ break;
+ }
+
+ return ret;
+}
+
+static struct mixer_ops mixer_plug_ops = {
+ .close = mixer_plug_close,
+ .get_poll_fd = mixer_plug_get_poll_fd,
+ .read_event = mixer_plug_read_event,
+ .ioctl = mixer_plug_ioctl,
+};
+
+int mixer_plugin_open(unsigned int card, void **data,
+ struct mixer_ops **ops)
+{
+ struct mixer_plug_data *plug_data;
+ struct mixer_plugin *plugin = NULL;
+ const char *err = NULL;
+ void *dl_hdl;
+ char *name, *so_name;
+ char *open_fn_name, token[80];
+ int ret;
+
+ plug_data = calloc(1, sizeof(*plug_data));
+ if (!plug_data)
+ return -ENOMEM;
+
+ /* mixer id is fixed to 1 in snd-card-def xml */
+ plug_data->mixer_node = snd_utils_get_dev_node(card, 1, NODE_MIXER);
+ if (!plug_data->mixer_node) {
+ /* Do not print error here.
+ * It is valid for card to not have virtual mixer node
+ */
+ goto err_get_mixer_node;
+ }
+
+ ret = snd_utils_get_str(plug_data->mixer_node, "so-name",
+ &so_name);
+ if(ret) {
+ fprintf(stderr, "%s: mixer so-name not found for card %u\n",
+ __func__, card);
+ goto err_get_mixer_node;
+
+ }
+
+ dl_hdl = dlopen(so_name, RTLD_NOW);
+ if (!dl_hdl) {
+ fprintf(stderr, "%s: unable to open %s\n",
+ __func__, so_name);
+ goto err_get_mixer_node;
+ }
+
+ sscanf(so_name, "lib%s", token);
+ name = strtok(token, ".");
+
+ open_fn_name = calloc(1, strlen(name) + strlen("_open") + 1);
+ if (!open_fn_name) {
+ ret = -ENOMEM;
+ goto err_get_mixer_node;
+ }
+
+ strncpy(open_fn_name, name, strlen(name) + 1);
+ strcat(open_fn_name, "_open");
+
+ printf("%s - %s\n", __func__, open_fn_name);
+
+ dlerror();
+ plug_data->mixer_plugin_open_fn = dlsym(dl_hdl, open_fn_name);
+ if (err) {
+ fprintf(stderr, "%s: dlsym open fn failed: %s\n",
+ __func__, err);
+ goto err_get_name;
+ }
+ ret = plug_data->mixer_plugin_open_fn(&plugin, card);
+ if (ret) {
+ fprintf(stderr, "%s: failed to open plugin, err: %d\n",
+ __func__, ret);
+ goto err_get_name;
+ }
+
+ plug_data->plugin = plugin;
+ plug_data->card = card;
+ plug_data->dl_hdl = dl_hdl;
+ plugin->eventfd = eventfd(0, 0);
+
+ *data = plug_data;
+ *ops = &mixer_plug_ops;
+
+ printf("%s: card = %d\n", __func__, plug_data->card);
+
+ return 0;
+
+err_get_name:
+ snd_utils_put_dev_node(plug_data->mixer_node);
+ dlclose(dl_hdl);
+
+err_get_mixer_node:
+
+ free(plug_data);
+ return -1;
+}