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