xref: /aosp_15_r20/external/tinyalsa_new/src/mixer_plugin.c (revision 02e95f1a335b55495d41ca67eaf42361f13704fa)
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