aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile6
-rw-r--r--src/mixer.c392
-rw-r--r--src/mixer_hw.c112
-rw-r--r--src/mixer_io.h51
-rw-r--r--src/mixer_plugin.c480
5 files changed, 966 insertions, 75 deletions
diff --git a/src/Makefile b/src/Makefile
index b278f47..79628b5 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -15,7 +15,7 @@ INCLUDE_DIRS = -I ../include
override CFLAGS := $(WARNINGS) $(INCLUDE_DIRS) -fPIC $(CFLAGS)
VPATH = ../include/tinyalsa
-OBJECTS = limits.o mixer.o pcm.o pcm_plugin.o pcm_hw.o snd_card_plugin.o
+OBJECTS = limits.o mixer.o pcm.o pcm_plugin.o pcm_hw.o snd_card_plugin.o mixer_plugin.o mixer_hw.o
LIBVERSION_MAJOR = $(TINYALSA_VERSION_MAJOR)
LIBVERSION = $(TINYALSA_VERSION)
@@ -35,6 +35,10 @@ mixer.o: mixer.c mixer.h
snd_card_plugin.o: snd_card_plugin.c snd_card_plugin.h
+mixer_plugin.o: mixer_plugin.c mixer_io.h
+
+mixer_hw.o: mixer_hw.c mixer_io.h
+
libtinyalsa.a: $(OBJECTS)
$(AR) $(ARFLAGS) $@ $^
diff --git a/src/mixer.c b/src/mixer.c
index e496e18..1c9e63b 100644
--- a/src/mixer.c
+++ b/src/mixer.c
@@ -29,6 +29,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
+#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
@@ -57,6 +58,9 @@
#include <sound/asound.h>
#include <tinyalsa/mixer.h>
+#include <tinyalsa/plugin.h>
+
+#include "mixer_io.h"
/** A mixer control.
* @ingroup libtinyalsa-mixer
@@ -68,6 +72,21 @@ struct mixer_ctl {
struct snd_ctl_elem_info info;
/** A list of string representations of enumerated values (only valid for enumerated controls) */
char **ename;
+ /** Pointer to the group that the control belongs to */
+ struct mixer_ctl_group *grp;
+};
+
+struct mixer_ctl_group {
+ /** A continuous array of mixer controls */
+ struct mixer_ctl *ctl;
+ /** The number of mixer controls */
+ unsigned int count;
+ /** The number of events associated with this group */
+ unsigned int event_cnt;
+ /** The operations corresponding to this group */
+ const struct mixer_ops *ops;
+ /** Private data for storing group specific data */
+ void *data;
};
/** A mixer handle.
@@ -78,10 +97,15 @@ struct mixer {
int fd;
/** Card information */
struct snd_ctl_card_info card_info;
- /** A continuous array of mixer controls */
- struct mixer_ctl *ctl;
- /** The number of mixer controls */
- unsigned int count;
+ /* Hardware (kernel interface) mixer control group */
+ struct mixer_ctl_group *h_grp;
+ /* Virtual (Plugin interface) mixer control group */
+ struct mixer_ctl_group *v_grp;
+ /* Total count of mixer controls from both groups */
+ unsigned int total_count;
+ /* Flag to track if card information is already retrieved */
+ bool is_card_info_retrieved;
+
};
static void mixer_cleanup_control(struct mixer_ctl *ctl)
@@ -96,25 +120,40 @@ static void mixer_cleanup_control(struct mixer_ctl *ctl)
}
}
+static void mixer_grp_close(struct mixer *mixer, struct mixer_ctl_group *grp)
+{
+ unsigned int n;
+
+ if (!grp)
+ return;
+
+ if (grp->ctl) {
+ for (n = 0; n < grp->count; n++)
+ mixer_cleanup_control(&grp->ctl[n]);
+ free(grp->ctl);
+ }
+
+ mixer->is_card_info_retrieved = false;
+}
+
/** Closes a mixer returned by @ref mixer_open.
* @param mixer A mixer handle.
* @ingroup libtinyalsa-mixer
*/
void mixer_close(struct mixer *mixer)
{
- unsigned int n;
-
if (!mixer)
return;
- if (mixer->fd >= 0)
- close(mixer->fd);
+ if (mixer->fd >= 0 && mixer->h_grp)
+ mixer->h_grp->ops->close(mixer->h_grp->data);
+ mixer_grp_close(mixer, mixer->h_grp);
- if (mixer->ctl) {
- for (n = 0; n < mixer->count; n++)
- mixer_cleanup_control(&mixer->ctl[n]);
- free(mixer->ctl);
- }
+#ifdef TINYALSA_USES_PLUGINS
+ if (mixer->v_grp)
+ mixer->v_grp->ops->close(mixer->v_grp->data);
+ mixer_grp_close(mixer, mixer->v_grp);
+#endif
free(mixer);
@@ -133,18 +172,17 @@ static void *mixer_realloc_z(void *ptr, size_t curnum, size_t newnum, size_t siz
return newp;
}
-static int add_controls(struct mixer *mixer)
+static int add_controls(struct mixer *mixer, struct mixer_ctl_group *grp)
{
struct snd_ctl_elem_list elist;
struct snd_ctl_elem_id *eid = NULL;
struct mixer_ctl *ctl;
- int fd = mixer->fd;
- const unsigned int old_count = mixer->count;
+ const unsigned int old_count = grp->count;
unsigned int new_count;
unsigned int n;
memset(&elist, 0, sizeof(elist));
- if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+ if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
goto fail;
if (old_count == elist.count)
@@ -153,12 +191,12 @@ static int add_controls(struct mixer *mixer)
if (old_count > elist.count)
return -1; /* driver has removed controls - this is bad */
- ctl = mixer_realloc_z(mixer->ctl, old_count, elist.count,
+ ctl = mixer_realloc_z(grp->ctl, old_count, elist.count,
sizeof(struct mixer_ctl));
if (!ctl)
goto fail;
- mixer->ctl = ctl;
+ grp->ctl = ctl;
/* ALSA drivers are not supposed to remove or re-order controls that
* have already been created so we know that any new controls must
@@ -174,18 +212,21 @@ static int add_controls(struct mixer *mixer)
elist.pids = eid;
- if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
+ if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
goto fail;
for (n = old_count; n < new_count; n++) {
- struct snd_ctl_elem_info *ei = &mixer->ctl[n].info;
+ struct snd_ctl_elem_info *ei = &grp->ctl[n].info;
ei->id.numid = eid[n - old_count].numid;
- if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
+ if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
goto fail_extend;
ctl[n].mixer = mixer;
+ ctl[n].grp = grp;
}
- mixer->count = new_count;
+ grp->count = new_count;
+ mixer->total_count += (new_count - old_count);
+
free(eid);
return 0;
@@ -196,13 +237,69 @@ fail_extend:
*/
mixer_cleanup_control(&ctl[n]);
- mixer->count = n; /* keep controls we successfully added */
+ grp->count = n; /* keep controls we successfully added */
+ mixer->total_count += (n - old_count);
/* fall through... */
fail:
free(eid);
return -1;
}
+static int mixer_grp_open(struct mixer *mixer, unsigned int card, bool is_hw)
+{
+ struct mixer_ctl_group *grp = NULL;
+ const struct mixer_ops *ops = NULL;
+ void *data = NULL;
+ int fd, ret;
+
+ grp = calloc(1, sizeof(*grp));
+ if (!grp)
+ return -ENOMEM;
+
+ if (is_hw) {
+ mixer->fd = -1;
+ fd = mixer_hw_open(card, &data, &ops);
+ if (fd < 0) {
+ ret = fd;
+ goto err_open;
+ }
+ mixer->fd = fd;
+ mixer->h_grp = grp;
+ }
+#ifdef TINYALSA_USES_PLUGINS
+ else {
+ ret = mixer_plugin_open(card, &data, &ops);
+ if (ret < 0)
+ goto err_open;
+ mixer->v_grp = grp;
+ }
+#endif
+ grp->ops = ops;
+ grp->data = data;
+
+ if (!mixer->is_card_info_retrieved) {
+ ret = grp->ops->ioctl(data, SNDRV_CTL_IOCTL_CARD_INFO,
+ &mixer->card_info);
+ if (ret < 0)
+ goto err_card_info;
+ mixer->is_card_info_retrieved = true;
+ }
+
+ ret = add_controls(mixer, grp);
+ if (ret < 0)
+ goto err_card_info;
+
+ return 0;
+
+err_card_info:
+ grp->ops->close(grp->data);
+
+err_open:
+ free(grp);
+ return ret;
+
+}
+
/** Opens a mixer for a given card.
* @param card The card to open the mixer for.
* @returns An initialized mixer handle.
@@ -211,24 +308,20 @@ fail:
struct mixer *mixer_open(unsigned int card)
{
struct mixer *mixer = NULL;
- int fd;
- char fn[256];
-
- snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
- fd = open(fn, O_RDWR);
- if (fd < 0)
- return 0;
+ int h_status, v_status = -1;
mixer = calloc(1, sizeof(*mixer));
if (!mixer)
goto fail;
- if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
- goto fail;
+ h_status = mixer_grp_open(mixer, card, true);
- mixer->fd = fd;
+#ifdef TINYALSA_USES_PLUGINS
+ v_status = mixer_grp_open(mixer, card, false);
+#endif
- if (add_controls(mixer) != 0)
+ /* both hw and virtual should fail for mixer_open to fail */
+ if (h_status < 0 && v_status < 0)
goto fail;
return mixer;
@@ -236,8 +329,7 @@ struct mixer *mixer_open(unsigned int card)
fail:
if (mixer)
mixer_close(mixer);
- else if (fd >= 0)
- close(fd);
+
return NULL;
}
@@ -260,10 +352,27 @@ fail:
*/
int mixer_add_new_ctls(struct mixer *mixer)
{
+ int rc1 = 0, rc2 = 0;
+
if (!mixer)
return 0;
- return add_controls(mixer);
+ /* add the h_grp controls */
+ if (mixer->h_grp)
+ rc1 = add_controls(mixer, mixer->h_grp);
+
+#ifdef TINYALSA_USES_PLUGINS
+ /* add the v_grp controls */
+ if (mixer->v_grp)
+ rc2 = add_controls(mixer, mixer->v_grp);
+#endif
+
+ if (rc1 < 0)
+ return rc1;
+ if (rc2 < 0)
+ return rc2;
+
+ return 0;
}
/** Gets the name of the mixer's card.
@@ -286,7 +395,7 @@ unsigned int mixer_get_num_ctls(const struct mixer *mixer)
if (!mixer)
return 0;
- return mixer->count;
+ return mixer->total_count;
}
/** Gets the number of mixer controls, that go by a specified name, for a given mixer.
@@ -297,6 +406,7 @@ unsigned int mixer_get_num_ctls(const struct mixer *mixer)
*/
unsigned int mixer_get_num_ctls_by_name(const struct mixer *mixer, const char *name)
{
+ struct mixer_ctl_group *grp;
unsigned int n;
unsigned int count = 0;
struct mixer_ctl *ctl;
@@ -304,11 +414,24 @@ unsigned int mixer_get_num_ctls_by_name(const struct mixer *mixer, const char *n
if (!mixer)
return 0;
- ctl = mixer->ctl;
+ if (mixer->h_grp) {
+ grp = mixer->h_grp;
+ ctl = grp->ctl;
- for (n = 0; n < mixer->count; n++)
- if (!strcmp(name, (char*) ctl[n].info.id.name))
- count++;
+ for (n = 0; n < grp->count; n++)
+ if (!strcmp(name, (char*) ctl[n].info.id.name))
+ count++;
+ }
+#ifdef TINYALSA_USES_PLUGINS
+ if (mixer->v_grp) {
+ grp = mixer->v_grp;
+ ctl = grp->ctl;
+
+ for (n = 0; n < grp->count; n++)
+ if (!strcmp(name, (char*) ctl[n].info.id.name))
+ count++;
+ }
+#endif
return count;
}
@@ -322,9 +445,21 @@ unsigned int mixer_get_num_ctls_by_name(const struct mixer *mixer, const char *n
*/
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->h_grp) {
+ grp = mixer->h_grp;
+ if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
+ return -1;
+ }
+
+#ifdef TINYALSA_USES_PLUGINS
+ if (mixer->v_grp) {
+ grp = mixer->v_grp;
+ if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
+ return -1;
}
+#endif
return 0;
}
@@ -338,25 +473,76 @@ int mixer_subscribe_events(struct mixer *mixer, int subscribe)
*/
int mixer_wait_event(struct mixer *mixer, int timeout)
{
- struct pollfd pfd;
+ struct pollfd *pfd;
+ struct mixer_ctl_group *grp;
+ int count = 0, num_fds = 0, i;
- pfd.fd = mixer->fd;
- pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
+ if (mixer->fd >= 0)
+ num_fds++;
+
+#ifdef TINYALSA_USES_PLUGINS
+ if (mixer->v_grp)
+ num_fds++;
+#endif
+
+ pfd = (struct pollfd *)calloc(sizeof(struct pollfd), num_fds);
+ if (!pfd)
+ return -ENOMEM;
+
+ if (mixer->fd >= 0) {
+ pfd[count].fd = mixer->fd;
+ pfd[count].events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
+ count++;
+ }
+
+#ifdef TINYALSA_USES_PLUGINS
+ if (mixer->v_grp) {
+ grp = mixer->v_grp;
+ if (!grp->ops->get_poll_fd(grp->data, pfd, count)) {
+ pfd[count].events = POLLIN | POLLERR | POLLNVAL;
+ count++;
+ }
+ }
+#endif
+
+ 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->h_grp;
+ grp->event_cnt++;
+ }
+#ifdef TINYALSA_USES_PLUGINS
+ else {
+ grp = mixer->v_grp;
+ grp->event_cnt++;
+ }
+#endif
+ return 1;
+ }
+ }
}
}
+static unsigned int mixer_grp_get_count(struct mixer_ctl_group *grp)
+{
+ if (!grp)
+ return 0;
+
+ return grp->count;
+}
+
/** Gets a mixer control handle, by the mixer control's id.
* For non-const access, see @ref mixer_get_ctl
* @param mixer An initialized mixer handle.
@@ -366,8 +552,22 @@ int mixer_wait_event(struct mixer *mixer, int timeout)
*/
const struct mixer_ctl *mixer_get_ctl_const(const struct mixer *mixer, unsigned int id)
{
- if (mixer && (id < mixer->count))
- return mixer->ctl + id;
+ unsigned int h_count;
+
+ if (!mixer || (id >= mixer->total_count))
+ return NULL;
+
+ h_count = mixer_grp_get_count(mixer->h_grp);
+
+ if (id < h_count)
+ return mixer->h_grp->ctl + id;
+#ifdef TINYALSA_USES_PLUGINS
+ else {
+ unsigned int v_count = mixer_grp_get_count(mixer->v_grp);
+ if ((id - h_count) < v_count)
+ return mixer->v_grp->ctl + (id - h_count);
+ }
+#endif
return NULL;
}
@@ -381,9 +581,22 @@ const struct mixer_ctl *mixer_get_ctl_const(const struct mixer *mixer, unsigned
*/
struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
{
- if (mixer && (id < mixer->count))
- return mixer->ctl + id;
+ unsigned int h_count;
+
+ if (!mixer || (id >= mixer->total_count))
+ return NULL;
+
+ h_count = mixer_grp_get_count(mixer->h_grp);
+ if (id < h_count)
+ return mixer->h_grp->ctl + id;
+#ifdef TINYALSA_USES_PLUGINS
+ else {
+ unsigned int v_count = mixer_grp_get_count(mixer->v_grp);
+ if ((id - h_count) < v_count)
+ return mixer->v_grp->ctl + (id - h_count);
+ }
+#endif
return NULL;
}
@@ -410,19 +623,34 @@ struct mixer_ctl *mixer_get_ctl_by_name_and_index(struct mixer *mixer,
const char *name,
unsigned int index)
{
+ struct mixer_ctl_group *grp;
unsigned int n;
struct mixer_ctl *ctl;
if (!mixer)
return NULL;
- ctl = mixer->ctl;
+ if (mixer->h_grp) {
+ grp = mixer->h_grp;
+ ctl = grp->ctl;
- for (n = 0; n < mixer->count; n++)
- if (!strcmp(name, (char*) ctl[n].info.id.name))
- if (index-- == 0)
- return mixer->ctl + n;
+ for (n = 0; n < grp->count; n++)
+ if (!strcmp(name, (char*) ctl[n].info.id.name))
+ if (index-- == 0)
+ return ctl + n;
+ }
+
+#ifdef TINYALSA_USES_PLUGINS
+ if (mixer->v_grp) {
+ grp = mixer->v_grp;
+ ctl = grp->ctl;
+ for (n = 0; n < grp->count; n++)
+ if (!strcmp(name, (char*) ctl[n].info.id.name))
+ if (index-- == 0)
+ return ctl + n;
+ }
+#endif
return NULL;
}
@@ -433,7 +661,13 @@ struct mixer_ctl *mixer_get_ctl_by_name_and_index(struct mixer *mixer,
*/
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)
+ return;
+
+ grp = ctl->grp;
+ grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &ctl->info);
}
/** Checks the control for TLV Read/Write access.
@@ -595,15 +829,17 @@ int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
*/
int mixer_ctl_get_value(const struct mixer_ctl *ctl, unsigned int id)
{
+ struct mixer_ctl_group *grp;
struct snd_ctl_elem_value ev;
int ret;
if (!ctl || (id >= ctl->info.count))
return -EINVAL;
+ grp = ctl->grp;
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;
@@ -641,6 +877,7 @@ int mixer_ctl_get_value(const struct mixer_ctl *ctl, unsigned int id)
*/
int mixer_ctl_get_array(const struct mixer_ctl *ctl, void *array, size_t count)
{
+ struct mixer_ctl_group *grp;
struct snd_ctl_elem_value ev;
int ret = 0;
size_t size;
@@ -650,6 +887,7 @@ int mixer_ctl_get_array(const struct mixer_ctl *ctl, void *array, size_t count)
if (!ctl || !count || !array)
return -EINVAL;
+ grp = ctl->grp;
total_count = ctl->info.count;
if ((ctl->info.type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
@@ -667,7 +905,7 @@ int mixer_ctl_get_array(const 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]);
@@ -687,7 +925,7 @@ int mixer_ctl_get_array(const 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);
@@ -696,7 +934,7 @@ int mixer_ctl_get_array(const 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]);
@@ -725,15 +963,17 @@ int mixer_ctl_get_array(const 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;
struct snd_ctl_elem_value ev;
int ret;
if (!ctl || (id >= ctl->info.count))
return -EINVAL;
+ grp = ctl->grp;
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;
@@ -763,7 +1003,7 @@ 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);
}
/** Sets the contents of a control's value array.
@@ -778,6 +1018,7 @@ int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
*/
int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
{
+ struct mixer_ctl_group *grp;
struct snd_ctl_elem_value ev;
size_t size;
void *dest;
@@ -786,6 +1027,7 @@ int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
if ((!ctl) || !count || !array)
return -EINVAL;
+ grp = ctl->grp;
total_count = ctl->info.count;
if ((ctl->info.type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
@@ -821,7 +1063,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;
@@ -837,7 +1079,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);
}
/** Gets the minimum value of an control.
@@ -887,6 +1129,7 @@ unsigned int mixer_ctl_get_num_enums(const struct mixer_ctl *ctl)
int mixer_ctl_fill_enum_string(struct mixer_ctl *ctl)
{
+ struct mixer_ctl_group *grp = ctl->grp;
struct snd_ctl_elem_info tmp;
unsigned int m;
char **enames;
@@ -902,7 +1145,7 @@ int mixer_ctl_fill_enum_string(struct mixer_ctl *ctl)
memset(&tmp, 0, sizeof(tmp));
tmp.id.numid = ctl->info.id.numid;
tmp.value.enumerated.item = m;
- 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[m] = strdup(tmp.value.enumerated.name);
if (!enames[m])
@@ -949,6 +1192,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;
unsigned int i, num_enums;
struct snd_ctl_elem_value ev;
int ret;
@@ -957,13 +1201,14 @@ int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
mixer_ctl_fill_enum_string(ctl) != 0)
return -EINVAL;
+ grp = ctl->grp;
num_enums = ctl->info.value.enumerated.items;
for (i = 0; i < num_enums; i++) {
if (!strcmp(string, ctl->ename[i])) {
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;
@@ -972,4 +1217,3 @@ int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
return -EINVAL;
}
-
diff --git a/src/mixer_hw.c b/src/mixer_hw.c
new file mode 100644
index 0000000..2e86dfa
--- /dev/null
+++ b/src/mixer_hw.c
@@ -0,0 +1,112 @@
+/* mixer_hw.c
+** Copyright (c) 2019, The Linux Foundation.
+**
+** 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"
+
+/** Store the hardware (kernel interface) mixer data */
+struct mixer_hw_data {
+ /* Card number for the mixer */
+ unsigned int card;
+ /* File descriptor of the mixer device node */
+ 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 const struct mixer_ops mixer_hw_ops = {
+ .close = mixer_hw_close,
+ .ioctl = mixer_hw_ioctl,
+};
+
+int mixer_hw_open(unsigned int card, void **data,
+ const 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)
+ return -1;
+
+ hw_data->card = card;
+ hw_data->fd = fd;
+ *data = hw_data;
+ *ops = &mixer_hw_ops;
+
+ return fd;
+}
diff --git a/src/mixer_io.h b/src/mixer_io.h
new file mode 100644
index 0000000..bb3bc44
--- /dev/null
+++ b/src/mixer_io.h
@@ -0,0 +1,51 @@
+/* mixer_io.h
+** Copyright (c) 2019, The Linux Foundation.
+**
+** 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 TINYALSA_SRC_MIXER_H
+#define TINYALSA_SRC_MIXER_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sound/asound.h>
+
+struct mixer_ops;
+
+int mixer_hw_open(unsigned int card, void **data,
+ const struct mixer_ops **ops);
+int mixer_plugin_open(unsigned int card, void **data,
+ const 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 /* TINYALSA_SRC_MIXER_H */
diff --git a/src/mixer_plugin.c b/src/mixer_plugin.c
new file mode 100644
index 0000000..3437171
--- /dev/null
+++ b/src/mixer_plugin.c
@@ -0,0 +1,480 @@
+/* mixer_plugin.c
+** Copyright (c) 2019, The Linux Foundation.
+**
+** 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 <tinyalsa/plugin.h>
+
+#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 "snd_card_plugin.h"
+#include "mixer_io.h"
+
+/** Encapulates the mixer plugin specific data */
+struct mixer_plug_data {
+ /** Card number associated with the plugin */
+ int card;
+ /** Device node for mixer */
+ void *mixer_node;
+ /** Pointer to the plugin's ops */
+ const struct mixer_plugin_ops *ops;
+ /** Pointer to plugin responsible to service the controls */
+ struct mixer_plugin *plugin;
+ /** Handle to the plugin library */
+ void *dl_hdl;
+};
+
+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) {
+ fprintf(stderr, "%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 = plug_data->ops->read_event(plugin, 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) {
+ plug_data->ops->subscribe_events(plugin, &mixer_plug_notifier_cb);
+ } else if (plugin->subscribed && !*subscribe) {
+ plug_data->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:
+ fprintf(stderr,"%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));
+
+ 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);
+
+ plug_data->ops->close(&plugin);
+ dlclose(plug_data->dl_hdl);
+
+ 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 const struct mixer_ops mixer_plug_ops = {
+ .close = mixer_plug_close,
+ .read_event = mixer_plug_read_event,
+ .get_poll_fd = mixer_plug_get_poll_fd,
+ .ioctl = mixer_plug_ioctl,
+};
+
+int mixer_plugin_open(unsigned int card, void **data,
+ const struct mixer_ops **ops)
+{
+ struct mixer_plug_data *plug_data;
+ struct mixer_plugin *plugin = NULL;
+ void *dl_hdl;
+ char *so_name;
+ int ret;
+
+ plug_data = calloc(1, sizeof(*plug_data));
+ if (!plug_data)
+ return -ENOMEM;
+
+ plug_data->mixer_node = snd_utils_open_mixer(card);
+ 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_lib_name;
+
+ }
+
+ dl_hdl = dlopen(so_name, RTLD_NOW);
+ if (!dl_hdl) {
+ fprintf(stderr, "%s: unable to open %s\n",
+ __func__, so_name);
+ goto err_dlopen;
+ }
+
+ dlerror();
+ plug_data->ops = dlsym(dl_hdl, "mixer_plugin_ops");
+ if (!plug_data->ops) {
+ fprintf(stderr, "%s: dlsym open fn failed: %s\n",
+ __func__, dlerror());
+ goto err_ops;
+ }
+
+ ret = plug_data->ops->open(&plugin, card);
+ if (ret) {
+ fprintf(stderr, "%s: failed to open plugin, err: %d\n",
+ __func__, ret);
+ goto err_ops;
+ }
+
+ 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;
+
+ return 0;
+
+err_ops:
+ dlclose(dl_hdl);
+err_dlopen:
+err_get_lib_name:
+ snd_utils_close_dev_node(plug_data->mixer_node);
+err_get_mixer_node:
+ free(plug_data);
+ return -1;
+}