1*d0c94b83SXin Li /* mixer_plugin.c
2*d0c94b83SXin Li **
3*d0c94b83SXin Li ** Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
4*d0c94b83SXin Li **
5*d0c94b83SXin Li ** Redistribution and use in source and binary forms, with or without
6*d0c94b83SXin Li ** modification, are permitted provided that the following conditions are
7*d0c94b83SXin Li ** met:
8*d0c94b83SXin Li ** * Redistributions of source code must retain the above copyright
9*d0c94b83SXin Li ** notice, this list of conditions and the following disclaimer.
10*d0c94b83SXin Li ** * Redistributions in binary form must reproduce the above
11*d0c94b83SXin Li ** copyright notice, this list of conditions and the following
12*d0c94b83SXin Li ** disclaimer in the documentation and/or other materials provided
13*d0c94b83SXin Li ** with the distribution.
14*d0c94b83SXin Li ** * Neither the name of The Linux Foundation nor the names of its
15*d0c94b83SXin Li ** contributors may be used to endorse or promote products derived
16*d0c94b83SXin Li ** from this software without specific prior written permission.
17*d0c94b83SXin Li **
18*d0c94b83SXin Li ** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
19*d0c94b83SXin Li ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20*d0c94b83SXin Li ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
21*d0c94b83SXin Li ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22*d0c94b83SXin Li ** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23*d0c94b83SXin Li ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24*d0c94b83SXin Li ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25*d0c94b83SXin Li ** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26*d0c94b83SXin Li ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27*d0c94b83SXin Li ** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
28*d0c94b83SXin Li ** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29*d0c94b83SXin Li **/
30*d0c94b83SXin Li
31*d0c94b83SXin Li #include <stdio.h>
32*d0c94b83SXin Li #include <stdlib.h>
33*d0c94b83SXin Li #include <stdint.h>
34*d0c94b83SXin Li #include <stdarg.h>
35*d0c94b83SXin Li #include <stdbool.h>
36*d0c94b83SXin Li #include <string.h>
37*d0c94b83SXin Li #include <unistd.h>
38*d0c94b83SXin Li #include <fcntl.h>
39*d0c94b83SXin Li #include <errno.h>
40*d0c94b83SXin Li #include <ctype.h>
41*d0c94b83SXin Li #include <poll.h>
42*d0c94b83SXin Li #include <dlfcn.h>
43*d0c94b83SXin Li #include <sys/eventfd.h>
44*d0c94b83SXin Li #include <sys/ioctl.h>
45*d0c94b83SXin Li
46*d0c94b83SXin Li #include <linux/ioctl.h>
47*d0c94b83SXin Li #include <sound/asound.h>
48*d0c94b83SXin Li
49*d0c94b83SXin Li #include <tinyalsa/asoundlib.h>
50*d0c94b83SXin Li #include <tinyalsa/mixer_plugin.h>
51*d0c94b83SXin Li #include "snd_utils.h"
52*d0c94b83SXin Li
53*d0c94b83SXin Li #include "mixer_io.h"
54*d0c94b83SXin Li
55*d0c94b83SXin Li struct mixer_plug_data {
56*d0c94b83SXin Li int card;
57*d0c94b83SXin Li void *mixer_node;
58*d0c94b83SXin Li
59*d0c94b83SXin Li struct mixer_plugin *plugin;
60*d0c94b83SXin Li void *dl_hdl;
61*d0c94b83SXin Li MIXER_PLUGIN_OPEN_FN_PTR();
62*d0c94b83SXin Li };
63*d0c94b83SXin Li
mixer_plug_get_elem_id(struct mixer_plug_data * plug_data,struct snd_ctl_elem_id * id,unsigned int offset)64*d0c94b83SXin Li static int mixer_plug_get_elem_id(struct mixer_plug_data *plug_data,
65*d0c94b83SXin Li struct snd_ctl_elem_id *id, unsigned int offset)
66*d0c94b83SXin Li {
67*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
68*d0c94b83SXin Li struct snd_control *ctl;
69*d0c94b83SXin Li
70*d0c94b83SXin Li if (offset >= plugin->num_controls) {
71*d0c94b83SXin Li printf("%s: invalid offset %u\n", __func__, offset);
72*d0c94b83SXin Li return -EINVAL;
73*d0c94b83SXin Li }
74*d0c94b83SXin Li
75*d0c94b83SXin Li ctl = plugin->controls + offset;
76*d0c94b83SXin Li id->numid = offset;
77*d0c94b83SXin Li id->iface = ctl->iface;
78*d0c94b83SXin Li
79*d0c94b83SXin Li strncpy((char *)id->name, (char *)ctl->name,
80*d0c94b83SXin Li sizeof(id->name));
81*d0c94b83SXin Li
82*d0c94b83SXin Li return 0;
83*d0c94b83SXin Li }
84*d0c94b83SXin Li
mixer_plug_info_enum(struct snd_control * ctl,struct snd_ctl_elem_info * einfo)85*d0c94b83SXin Li static int mixer_plug_info_enum(struct snd_control *ctl,
86*d0c94b83SXin Li struct snd_ctl_elem_info *einfo)
87*d0c94b83SXin Li {
88*d0c94b83SXin Li struct snd_value_enum *val = ctl->value;
89*d0c94b83SXin Li
90*d0c94b83SXin Li einfo->count = 1;
91*d0c94b83SXin Li einfo->value.enumerated.items = val->items;
92*d0c94b83SXin Li
93*d0c94b83SXin Li if (einfo->value.enumerated.item > val->items)
94*d0c94b83SXin Li return -EINVAL;
95*d0c94b83SXin Li
96*d0c94b83SXin Li strncpy(einfo->value.enumerated.name,
97*d0c94b83SXin Li val->texts[einfo->value.enumerated.item],
98*d0c94b83SXin Li sizeof(einfo->value.enumerated.name));
99*d0c94b83SXin Li
100*d0c94b83SXin Li return 0;
101*d0c94b83SXin Li }
102*d0c94b83SXin Li
mixer_plug_info_bytes(struct snd_control * ctl,struct snd_ctl_elem_info * einfo)103*d0c94b83SXin Li static int mixer_plug_info_bytes(struct snd_control *ctl,
104*d0c94b83SXin Li struct snd_ctl_elem_info *einfo)
105*d0c94b83SXin Li {
106*d0c94b83SXin Li struct snd_value_bytes *val;
107*d0c94b83SXin Li struct snd_value_tlv_bytes *val_tlv;
108*d0c94b83SXin Li
109*d0c94b83SXin Li if (ctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
110*d0c94b83SXin Li val_tlv = ctl->value;
111*d0c94b83SXin Li einfo->count = val_tlv->size;
112*d0c94b83SXin Li } else {
113*d0c94b83SXin Li val = ctl->value;
114*d0c94b83SXin Li einfo->count = val->size;
115*d0c94b83SXin Li }
116*d0c94b83SXin Li
117*d0c94b83SXin Li return 0;
118*d0c94b83SXin Li }
119*d0c94b83SXin Li
mixer_plug_info_integer(struct snd_control * ctl,struct snd_ctl_elem_info * einfo)120*d0c94b83SXin Li static int mixer_plug_info_integer(struct snd_control *ctl,
121*d0c94b83SXin Li struct snd_ctl_elem_info *einfo)
122*d0c94b83SXin Li {
123*d0c94b83SXin Li struct snd_value_int *val = ctl->value;
124*d0c94b83SXin Li
125*d0c94b83SXin Li einfo->count = val->count;
126*d0c94b83SXin Li einfo->value.integer.min = val->min;
127*d0c94b83SXin Li einfo->value.integer.max = val->max;
128*d0c94b83SXin Li einfo->value.integer.step = val->step;
129*d0c94b83SXin Li
130*d0c94b83SXin Li return 0;
131*d0c94b83SXin Li }
132*d0c94b83SXin Li
mixer_plug_notifier_cb(struct mixer_plugin * plugin)133*d0c94b83SXin Li void mixer_plug_notifier_cb(struct mixer_plugin *plugin)
134*d0c94b83SXin Li {
135*d0c94b83SXin Li pthread_mutex_lock(&plugin->mutex);
136*d0c94b83SXin Li plugin->event_cnt++;
137*d0c94b83SXin Li pthread_mutex_unlock(&plugin->mutex);
138*d0c94b83SXin Li eventfd_write(plugin->eventfd, 1);
139*d0c94b83SXin Li }
140*d0c94b83SXin Li
141*d0c94b83SXin Li /* In consume_event/read, do not call eventfd_read until all events are read from list.
142*d0c94b83SXin Li This will make poll getting unblocked until all events are read */
mixer_plug_read_event(void * data,struct snd_ctl_event * ev,size_t size)143*d0c94b83SXin Li static ssize_t mixer_plug_read_event(void *data, struct snd_ctl_event *ev, size_t size)
144*d0c94b83SXin Li {
145*d0c94b83SXin Li struct mixer_plug_data *plug_data = data;
146*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
147*d0c94b83SXin Li eventfd_t evfd;
148*d0c94b83SXin Li ssize_t result = 0;
149*d0c94b83SXin Li unsigned int i, read_cnt;
150*d0c94b83SXin Li
151*d0c94b83SXin Li result = plugin->ops->read_event(plugin, (struct ctl_event *)ev, size);
152*d0c94b83SXin Li
153*d0c94b83SXin Li if (result > 0) {
154*d0c94b83SXin Li read_cnt = result / sizeof(struct snd_ctl_event);
155*d0c94b83SXin Li pthread_mutex_lock(&plugin->mutex);
156*d0c94b83SXin Li plugin->event_cnt -= read_cnt;
157*d0c94b83SXin Li if (plugin->event_cnt < 0) {
158*d0c94b83SXin Li plugin->event_cnt = 0;
159*d0c94b83SXin Li }
160*d0c94b83SXin Li pthread_mutex_unlock(&plugin->mutex);
161*d0c94b83SXin Li
162*d0c94b83SXin Li for (i = 0; i < read_cnt; ++i) {
163*d0c94b83SXin Li eventfd_read(plugin->eventfd, &evfd);
164*d0c94b83SXin Li }
165*d0c94b83SXin Li }
166*d0c94b83SXin Li
167*d0c94b83SXin Li return result;
168*d0c94b83SXin Li }
169*d0c94b83SXin Li
mixer_plug_subscribe_events(struct mixer_plug_data * plug_data,int * subscribe)170*d0c94b83SXin Li static int mixer_plug_subscribe_events(struct mixer_plug_data *plug_data,
171*d0c94b83SXin Li int *subscribe)
172*d0c94b83SXin Li {
173*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
174*d0c94b83SXin Li eventfd_t evfd;
175*d0c94b83SXin Li unsigned int count;
176*d0c94b83SXin Li
177*d0c94b83SXin Li if (*subscribe < 0 || *subscribe > 1) {
178*d0c94b83SXin Li *subscribe = plugin->subscribed;
179*d0c94b83SXin Li return -EINVAL;
180*d0c94b83SXin Li }
181*d0c94b83SXin Li
182*d0c94b83SXin Li if (*subscribe && !plugin->subscribed) {
183*d0c94b83SXin Li plugin->ops->subscribe_events(plugin, &mixer_plug_notifier_cb);
184*d0c94b83SXin Li } else if (plugin->subscribed && !*subscribe) {
185*d0c94b83SXin Li plugin->ops->subscribe_events(plugin, NULL);
186*d0c94b83SXin Li
187*d0c94b83SXin Li pthread_mutex_lock(&plugin->mutex);
188*d0c94b83SXin Li count = plugin->event_cnt;
189*d0c94b83SXin Li plugin->event_cnt = 0;
190*d0c94b83SXin Li pthread_mutex_unlock(&plugin->mutex);
191*d0c94b83SXin Li for (int i = 0; i < count; ++i) {
192*d0c94b83SXin Li eventfd_read(plugin->eventfd, &evfd);
193*d0c94b83SXin Li }
194*d0c94b83SXin Li }
195*d0c94b83SXin Li
196*d0c94b83SXin Li plugin->subscribed = *subscribe;
197*d0c94b83SXin Li return 0;
198*d0c94b83SXin Li }
199*d0c94b83SXin Li
mixer_plug_get_poll_fd(void * data,struct pollfd * pfd,int count)200*d0c94b83SXin Li static int mixer_plug_get_poll_fd(void *data, struct pollfd *pfd, int count)
201*d0c94b83SXin Li {
202*d0c94b83SXin Li struct mixer_plug_data *plug_data = data;
203*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
204*d0c94b83SXin Li
205*d0c94b83SXin Li if (plugin->eventfd != -1) {
206*d0c94b83SXin Li pfd[count].fd = plugin->eventfd;
207*d0c94b83SXin Li return 0;
208*d0c94b83SXin Li }
209*d0c94b83SXin Li return -ENODEV;
210*d0c94b83SXin Li }
211*d0c94b83SXin Li
mixer_plug_tlv_write(struct mixer_plug_data * plug_data,struct snd_ctl_tlv * tlv)212*d0c94b83SXin Li static int mixer_plug_tlv_write(struct mixer_plug_data *plug_data,
213*d0c94b83SXin Li struct snd_ctl_tlv *tlv)
214*d0c94b83SXin Li {
215*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
216*d0c94b83SXin Li struct snd_control *ctl;
217*d0c94b83SXin Li struct snd_value_tlv_bytes *val_tlv;
218*d0c94b83SXin Li
219*d0c94b83SXin Li ctl = plugin->controls + tlv->numid;
220*d0c94b83SXin Li val_tlv = ctl->value;
221*d0c94b83SXin Li
222*d0c94b83SXin Li return val_tlv->put(plugin, ctl, tlv);
223*d0c94b83SXin Li }
224*d0c94b83SXin Li
mixer_plug_tlv_read(struct mixer_plug_data * plug_data,struct snd_ctl_tlv * tlv)225*d0c94b83SXin Li static int mixer_plug_tlv_read(struct mixer_plug_data *plug_data,
226*d0c94b83SXin Li struct snd_ctl_tlv *tlv)
227*d0c94b83SXin Li {
228*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
229*d0c94b83SXin Li struct snd_control *ctl;
230*d0c94b83SXin Li struct snd_value_tlv_bytes *val_tlv;
231*d0c94b83SXin Li
232*d0c94b83SXin Li ctl = plugin->controls + tlv->numid;
233*d0c94b83SXin Li val_tlv = ctl->value;
234*d0c94b83SXin Li
235*d0c94b83SXin Li return val_tlv->get(plugin, ctl, tlv);
236*d0c94b83SXin Li }
237*d0c94b83SXin Li
mixer_plug_elem_write(struct mixer_plug_data * plug_data,struct snd_ctl_elem_value * ev)238*d0c94b83SXin Li static int mixer_plug_elem_write(struct mixer_plug_data *plug_data,
239*d0c94b83SXin Li struct snd_ctl_elem_value *ev)
240*d0c94b83SXin Li {
241*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
242*d0c94b83SXin Li struct snd_control *ctl;
243*d0c94b83SXin Li int ret;
244*d0c94b83SXin Li
245*d0c94b83SXin Li ret = mixer_plug_get_elem_id(plug_data, &ev->id, ev->id.numid);
246*d0c94b83SXin Li if (ret < 0)
247*d0c94b83SXin Li return ret;
248*d0c94b83SXin Li
249*d0c94b83SXin Li ctl = plugin->controls + ev->id.numid;
250*d0c94b83SXin Li
251*d0c94b83SXin Li return ctl->put(plugin, ctl, ev);
252*d0c94b83SXin Li }
253*d0c94b83SXin Li
mixer_plug_elem_read(struct mixer_plug_data * plug_data,struct snd_ctl_elem_value * ev)254*d0c94b83SXin Li static int mixer_plug_elem_read(struct mixer_plug_data *plug_data,
255*d0c94b83SXin Li struct snd_ctl_elem_value *ev)
256*d0c94b83SXin Li {
257*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
258*d0c94b83SXin Li struct snd_control *ctl;
259*d0c94b83SXin Li int ret;
260*d0c94b83SXin Li
261*d0c94b83SXin Li ret = mixer_plug_get_elem_id(plug_data, &ev->id, ev->id.numid);
262*d0c94b83SXin Li if (ret < 0)
263*d0c94b83SXin Li return ret;
264*d0c94b83SXin Li
265*d0c94b83SXin Li ctl = plugin->controls + ev->id.numid;
266*d0c94b83SXin Li
267*d0c94b83SXin Li return ctl->get(plugin, ctl, ev);
268*d0c94b83SXin Li
269*d0c94b83SXin Li }
270*d0c94b83SXin Li
mixer_plug_get_elem_info(struct mixer_plug_data * plug_data,struct snd_ctl_elem_info * einfo)271*d0c94b83SXin Li static int mixer_plug_get_elem_info(struct mixer_plug_data *plug_data,
272*d0c94b83SXin Li struct snd_ctl_elem_info *einfo)
273*d0c94b83SXin Li {
274*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
275*d0c94b83SXin Li struct snd_control *ctl;
276*d0c94b83SXin Li int ret;
277*d0c94b83SXin Li
278*d0c94b83SXin Li ret = mixer_plug_get_elem_id(plug_data, &einfo->id,
279*d0c94b83SXin Li einfo->id.numid);
280*d0c94b83SXin Li if (ret < 0)
281*d0c94b83SXin Li return ret;
282*d0c94b83SXin Li
283*d0c94b83SXin Li ctl = plugin->controls + einfo->id.numid;
284*d0c94b83SXin Li einfo->type = ctl->type;
285*d0c94b83SXin Li einfo->access = ctl->access;
286*d0c94b83SXin Li
287*d0c94b83SXin Li switch (einfo->type) {
288*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
289*d0c94b83SXin Li ret = mixer_plug_info_enum(ctl, einfo);
290*d0c94b83SXin Li if (ret < 0)
291*d0c94b83SXin Li return ret;
292*d0c94b83SXin Li break;
293*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_BYTES:
294*d0c94b83SXin Li ret = mixer_plug_info_bytes(ctl, einfo);
295*d0c94b83SXin Li if (ret < 0)
296*d0c94b83SXin Li return ret;
297*d0c94b83SXin Li break;
298*d0c94b83SXin Li case SNDRV_CTL_ELEM_TYPE_INTEGER:
299*d0c94b83SXin Li ret = mixer_plug_info_integer(ctl, einfo);
300*d0c94b83SXin Li if (ret < 0)
301*d0c94b83SXin Li return ret;
302*d0c94b83SXin Li break;
303*d0c94b83SXin Li default:
304*d0c94b83SXin Li printf("%s: unknown type %d\n", __func__, einfo->type);
305*d0c94b83SXin Li return -EINVAL;
306*d0c94b83SXin Li }
307*d0c94b83SXin Li
308*d0c94b83SXin Li return 0;
309*d0c94b83SXin Li }
310*d0c94b83SXin Li
mixer_plug_get_elem_list(struct mixer_plug_data * plug_data,struct snd_ctl_elem_list * elist)311*d0c94b83SXin Li static int mixer_plug_get_elem_list(struct mixer_plug_data *plug_data,
312*d0c94b83SXin Li struct snd_ctl_elem_list *elist)
313*d0c94b83SXin Li {
314*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
315*d0c94b83SXin Li unsigned int avail;
316*d0c94b83SXin Li struct snd_ctl_elem_id *id;
317*d0c94b83SXin Li int ret;
318*d0c94b83SXin Li
319*d0c94b83SXin Li elist->count = plugin->num_controls;
320*d0c94b83SXin Li elist->used = 0;
321*d0c94b83SXin Li avail = elist->space;
322*d0c94b83SXin Li
323*d0c94b83SXin Li while (avail > 0) {
324*d0c94b83SXin Li id = elist->pids + elist->used;
325*d0c94b83SXin Li ret = mixer_plug_get_elem_id(plug_data, id, elist->used);
326*d0c94b83SXin Li if (ret < 0)
327*d0c94b83SXin Li return ret;
328*d0c94b83SXin Li
329*d0c94b83SXin Li avail--;
330*d0c94b83SXin Li elist->used++;
331*d0c94b83SXin Li }
332*d0c94b83SXin Li
333*d0c94b83SXin Li return 0;
334*d0c94b83SXin Li }
335*d0c94b83SXin Li
mixer_plug_get_card_info(struct mixer_plug_data * plug_data,struct snd_ctl_card_info * card_info)336*d0c94b83SXin Li static int mixer_plug_get_card_info(struct mixer_plug_data *plug_data,
337*d0c94b83SXin Li struct snd_ctl_card_info *card_info)
338*d0c94b83SXin Li {
339*d0c94b83SXin Li /*TODO: Fill card_info here from snd-card-def */
340*d0c94b83SXin Li memset(card_info, 0, sizeof(*card_info));
341*d0c94b83SXin Li card_info->card = plug_data->card;
342*d0c94b83SXin Li memcpy(card_info->id, "card_id", strlen("card_id") + 1);
343*d0c94b83SXin Li memcpy(card_info->driver, "mymixer-so-name", strlen("mymixer-so-name") + 1);
344*d0c94b83SXin Li memcpy(card_info->name, "card-name", strlen("card-name") + 1);
345*d0c94b83SXin Li memcpy(card_info->longname, "card-name", strlen("card-name") + 1);
346*d0c94b83SXin Li memcpy(card_info->mixername, "mixer-name", strlen("mixer-name") + 1);
347*d0c94b83SXin Li
348*d0c94b83SXin Li printf("%s: card = %d\n", __func__, plug_data->card);
349*d0c94b83SXin Li
350*d0c94b83SXin Li return 0;
351*d0c94b83SXin Li }
352*d0c94b83SXin Li
mixer_plug_close(void * data)353*d0c94b83SXin Li static void mixer_plug_close(void *data)
354*d0c94b83SXin Li {
355*d0c94b83SXin Li struct mixer_plug_data *plug_data = data;
356*d0c94b83SXin Li struct mixer_plugin *plugin = plug_data->plugin;
357*d0c94b83SXin Li eventfd_t evfd;
358*d0c94b83SXin Li unsigned int count;
359*d0c94b83SXin Li
360*d0c94b83SXin Li pthread_mutex_lock(&plugin->mutex);
361*d0c94b83SXin Li count = plugin->event_cnt;
362*d0c94b83SXin Li pthread_mutex_unlock(&plugin->mutex);
363*d0c94b83SXin Li pthread_mutex_destroy(&plugin->mutex);
364*d0c94b83SXin Li
365*d0c94b83SXin Li for (int i = 0; i < count; ++i) {
366*d0c94b83SXin Li eventfd_read(plugin->eventfd, &evfd);
367*d0c94b83SXin Li }
368*d0c94b83SXin Li
369*d0c94b83SXin Li plugin->ops->close(&plugin);
370*d0c94b83SXin Li dlclose(plug_data->dl_hdl);
371*d0c94b83SXin Li snd_utils_put_dev_node(plug_data->mixer_node);
372*d0c94b83SXin Li free(plug_data);
373*d0c94b83SXin Li plug_data = NULL;
374*d0c94b83SXin Li }
375*d0c94b83SXin Li
mixer_plug_ioctl(void * data,unsigned int cmd,...)376*d0c94b83SXin Li static int mixer_plug_ioctl(void *data, unsigned int cmd, ...)
377*d0c94b83SXin Li {
378*d0c94b83SXin Li struct mixer_plug_data *plug_data = data;
379*d0c94b83SXin Li int ret;
380*d0c94b83SXin Li va_list ap;
381*d0c94b83SXin Li void *arg;
382*d0c94b83SXin Li
383*d0c94b83SXin Li va_start(ap, cmd);
384*d0c94b83SXin Li arg = va_arg(ap, void *);
385*d0c94b83SXin Li va_end(ap);
386*d0c94b83SXin Li
387*d0c94b83SXin Li switch (cmd) {
388*d0c94b83SXin Li case SNDRV_CTL_IOCTL_CARD_INFO:
389*d0c94b83SXin Li ret = mixer_plug_get_card_info(plug_data, arg);
390*d0c94b83SXin Li break;
391*d0c94b83SXin Li case SNDRV_CTL_IOCTL_ELEM_LIST:
392*d0c94b83SXin Li ret = mixer_plug_get_elem_list(plug_data, arg);
393*d0c94b83SXin Li break;
394*d0c94b83SXin Li case SNDRV_CTL_IOCTL_ELEM_INFO:
395*d0c94b83SXin Li ret = mixer_plug_get_elem_info(plug_data, arg);
396*d0c94b83SXin Li break;
397*d0c94b83SXin Li case SNDRV_CTL_IOCTL_ELEM_READ:
398*d0c94b83SXin Li ret = mixer_plug_elem_read(plug_data, arg);
399*d0c94b83SXin Li break;
400*d0c94b83SXin Li case SNDRV_CTL_IOCTL_ELEM_WRITE:
401*d0c94b83SXin Li ret = mixer_plug_elem_write(plug_data, arg);
402*d0c94b83SXin Li break;
403*d0c94b83SXin Li case SNDRV_CTL_IOCTL_TLV_READ:
404*d0c94b83SXin Li ret = mixer_plug_tlv_read(plug_data, arg);
405*d0c94b83SXin Li break;
406*d0c94b83SXin Li case SNDRV_CTL_IOCTL_TLV_WRITE:
407*d0c94b83SXin Li ret = mixer_plug_tlv_write(plug_data, arg);
408*d0c94b83SXin Li break;
409*d0c94b83SXin Li case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
410*d0c94b83SXin Li ret = mixer_plug_subscribe_events(plug_data, arg);
411*d0c94b83SXin Li break;
412*d0c94b83SXin Li default:
413*d0c94b83SXin Li /* TODO: plugin should support ioctl */
414*d0c94b83SXin Li ret = -EFAULT;
415*d0c94b83SXin Li break;
416*d0c94b83SXin Li }
417*d0c94b83SXin Li
418*d0c94b83SXin Li return ret;
419*d0c94b83SXin Li }
420*d0c94b83SXin Li
421*d0c94b83SXin Li static struct mixer_ops mixer_plug_ops = {
422*d0c94b83SXin Li .close = mixer_plug_close,
423*d0c94b83SXin Li .get_poll_fd = mixer_plug_get_poll_fd,
424*d0c94b83SXin Li .read_event = mixer_plug_read_event,
425*d0c94b83SXin Li .ioctl = mixer_plug_ioctl,
426*d0c94b83SXin Li };
427*d0c94b83SXin Li
mixer_plugin_open(unsigned int card,void ** data,struct mixer_ops ** ops)428*d0c94b83SXin Li int mixer_plugin_open(unsigned int card, void **data,
429*d0c94b83SXin Li struct mixer_ops **ops)
430*d0c94b83SXin Li {
431*d0c94b83SXin Li struct mixer_plug_data *plug_data;
432*d0c94b83SXin Li struct mixer_plugin *plugin = NULL;
433*d0c94b83SXin Li void *dl_hdl;
434*d0c94b83SXin Li char *name, *so_name;
435*d0c94b83SXin Li char *open_fn_name, token[80], *token_saveptr;
436*d0c94b83SXin Li int ret;
437*d0c94b83SXin Li
438*d0c94b83SXin Li plug_data = calloc(1, sizeof(*plug_data));
439*d0c94b83SXin Li if (!plug_data)
440*d0c94b83SXin Li return -ENOMEM;
441*d0c94b83SXin Li
442*d0c94b83SXin Li /* mixer id is fixed to 1 in snd-card-def xml */
443*d0c94b83SXin Li plug_data->mixer_node = snd_utils_get_dev_node(card, 1, NODE_MIXER);
444*d0c94b83SXin Li if (!plug_data->mixer_node) {
445*d0c94b83SXin Li /* Do not print error here.
446*d0c94b83SXin Li * It is valid for card to not have virtual mixer node
447*d0c94b83SXin Li */
448*d0c94b83SXin Li goto err_free_plug_data;
449*d0c94b83SXin Li }
450*d0c94b83SXin Li
451*d0c94b83SXin Li ret = snd_utils_get_str(plug_data->mixer_node, "so-name",
452*d0c94b83SXin Li &so_name);
453*d0c94b83SXin Li if(ret) {
454*d0c94b83SXin Li fprintf(stderr, "%s: mixer so-name not found for card %u\n",
455*d0c94b83SXin Li __func__, card);
456*d0c94b83SXin Li goto err_put_dev_node;
457*d0c94b83SXin Li
458*d0c94b83SXin Li }
459*d0c94b83SXin Li
460*d0c94b83SXin Li dl_hdl = dlopen(so_name, RTLD_NOW);
461*d0c94b83SXin Li if (!dl_hdl) {
462*d0c94b83SXin Li fprintf(stderr, "%s: unable to open %s\n",
463*d0c94b83SXin Li __func__, so_name);
464*d0c94b83SXin Li goto err_put_dev_node;
465*d0c94b83SXin Li }
466*d0c94b83SXin Li
467*d0c94b83SXin Li sscanf(so_name, "lib%s", token);
468*d0c94b83SXin Li token_saveptr = token;
469*d0c94b83SXin Li name = strtok_r(token, ".", &token_saveptr);
470*d0c94b83SXin Li if (!name) {
471*d0c94b83SXin Li fprintf(stderr, "%s: invalid library name\n", __func__);
472*d0c94b83SXin Li goto err_dl_hdl;
473*d0c94b83SXin Li }
474*d0c94b83SXin Li
475*d0c94b83SXin Li open_fn_name = calloc(1, strlen(name) + strlen("_open") + 1);
476*d0c94b83SXin Li if (!open_fn_name) {
477*d0c94b83SXin Li ret = -ENOMEM;
478*d0c94b83SXin Li goto err_dl_hdl;
479*d0c94b83SXin Li }
480*d0c94b83SXin Li
481*d0c94b83SXin Li strncpy(open_fn_name, name, strlen(name) + 1);
482*d0c94b83SXin Li strcat(open_fn_name, "_open");
483*d0c94b83SXin Li
484*d0c94b83SXin Li printf("%s - %s\n", __func__, open_fn_name);
485*d0c94b83SXin Li
486*d0c94b83SXin Li plug_data->mixer_plugin_open_fn = dlsym(dl_hdl, open_fn_name);
487*d0c94b83SXin Li if (!plug_data->mixer_plugin_open_fn) {
488*d0c94b83SXin Li fprintf(stderr, "%s: dlsym open fn failed: %s\n",
489*d0c94b83SXin Li __func__, dlerror());
490*d0c94b83SXin Li goto err_open_fn_name;
491*d0c94b83SXin Li }
492*d0c94b83SXin Li ret = plug_data->mixer_plugin_open_fn(&plugin, card);
493*d0c94b83SXin Li if (ret) {
494*d0c94b83SXin Li fprintf(stderr, "%s: failed to open plugin, err: %d\n",
495*d0c94b83SXin Li __func__, ret);
496*d0c94b83SXin Li goto err_open_fn_name;
497*d0c94b83SXin Li }
498*d0c94b83SXin Li
499*d0c94b83SXin Li plug_data->plugin = plugin;
500*d0c94b83SXin Li plug_data->card = card;
501*d0c94b83SXin Li plug_data->dl_hdl = dl_hdl;
502*d0c94b83SXin Li plugin->eventfd = eventfd(0, EFD_SEMAPHORE);
503*d0c94b83SXin Li pthread_mutex_init(&plugin->mutex, NULL);
504*d0c94b83SXin Li
505*d0c94b83SXin Li *data = plug_data;
506*d0c94b83SXin Li *ops = &mixer_plug_ops;
507*d0c94b83SXin Li
508*d0c94b83SXin Li printf("%s: card = %d\n", __func__, plug_data->card);
509*d0c94b83SXin Li
510*d0c94b83SXin Li free(open_fn_name);
511*d0c94b83SXin Li return 0;
512*d0c94b83SXin Li
513*d0c94b83SXin Li err_open_fn_name:
514*d0c94b83SXin Li free(open_fn_name);
515*d0c94b83SXin Li
516*d0c94b83SXin Li err_dl_hdl:
517*d0c94b83SXin Li dlclose(dl_hdl);
518*d0c94b83SXin Li
519*d0c94b83SXin Li err_put_dev_node:
520*d0c94b83SXin Li snd_utils_put_dev_node(plug_data->mixer_node);
521*d0c94b83SXin Li
522*d0c94b83SXin Li err_free_plug_data:
523*d0c94b83SXin Li
524*d0c94b83SXin Li free(plug_data);
525*d0c94b83SXin Li return -1;
526*d0c94b83SXin Li }
527