xref: /aosp_15_r20/external/tinyalsa_new/examples/plugins/sample_mixer_plugin.c (revision 02e95f1a335b55495d41ca67eaf42361f13704fa)
1 /* sample_mixer_plugin.c
2 **
3 ** Copyright (c) 2021, The Linux Foundation. All rights reserved.
4 **
5 ** Redistribution and use in source and binary forms, with or without
6 ** modification, are permitted provided that the following conditions are
7 ** met:
8 **   * Redistributions of source code must retain the above copyright
9 **     notice, this list of conditions and the following disclaimer.
10 **   * Redistributions in binary form must reproduce the above
11 **     copyright notice, this list of conditions and the following
12 **     disclaimer in the documentation and/or other materials provided
13 **     with the distribution.
14 **   * Neither the name of The Linux Foundation nor the names of its
15 **     contributors may be used to endorse or promote products derived
16 **     from this software without specific prior written permission.
17 **
18 ** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
19 ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
21 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22 ** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 ** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 ** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
28 ** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 **/
30 
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdint.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include <sound/asound.h>
38 #include <tinyalsa/plugin.h>
39 #include <tinyalsa/asoundlib.h>
40 
41 #define SAMPLE_MIXER_PRIV_GET_CTL_PTR(p, idx) (p->ctls + idx)
42 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
43 
44 static const char *const sample_enum_text[] = {"One", "Two", "Three"};
45 
46 struct sample_mixer_priv {
47     struct snd_control *ctls;
48     int ctl_count;
49 
50     struct snd_value_enum sample_enum;
51 
52     mixer_event_callback event_cb;
53 };
54 
sample_mixer_int_ctl_get(struct mixer_plugin * plugin,struct snd_control * ctl,struct snd_ctl_elem_value * ev)55 static int sample_mixer_int_ctl_get(struct mixer_plugin *plugin,
56                 struct snd_control *ctl, struct snd_ctl_elem_value *ev)
57 {
58     return 0;
59 }
60 
sample_mixer_int_ctl_put(struct mixer_plugin * plugin,struct snd_control * ctl,struct snd_ctl_elem_value * ev)61 static int sample_mixer_int_ctl_put(struct mixer_plugin *plugin,
62                 struct snd_control *ctl, struct snd_ctl_elem_value *ev)
63 {
64 /*
65  *   Integer values can be retrieved using:
66  *   uint32_t val1 = (uint32_t)ev->value.integer.value[0];
67  *   uint32_t val2 = (uint32_t)ev->value.integer.value[1];
68  *   uint32_t val3 = (uint32_t)ev->value.integer.value[2];
69  */
70     return 0;
71 }
72 
sample_mixer_byte_array_ctl_get(struct mixer_plugin * plugin,struct snd_control * ctl,struct snd_ctl_elem_value * ev)73 static int sample_mixer_byte_array_ctl_get(struct mixer_plugin *plugin,
74                 struct snd_control *ctl, struct snd_ctl_elem_value *ev)
75 {
76     return 0;
77 }
78 
sample_mixer_byte_array_ctl_put(struct mixer_plugin * plugin,struct snd_control * ctl,struct snd_ctl_elem_value * ev)79 static int sample_mixer_byte_array_ctl_put(struct mixer_plugin *plugin,
80                 struct snd_control *ctl, struct snd_ctl_elem_value *ev)
81 {
82 /*
83  *   Byte array payload can be retrieved using:
84  *   void *payload = ev->value.bytes.data;
85  */
86 
87     return 0;
88 }
89 
sample_mixer_tlv_ctl_get(struct mixer_plugin * plugin,struct snd_control * ctl,struct snd_ctl_tlv * ev)90 static int sample_mixer_tlv_ctl_get(struct mixer_plugin *plugin,
91                 struct snd_control *ctl, struct snd_ctl_tlv *ev)
92 {
93     return 0;
94 }
95 
sample_mixer_tlv_ctl_put(struct mixer_plugin * plugin,struct snd_control * ctl,struct snd_ctl_tlv * tlv)96 static int sample_mixer_tlv_ctl_put(struct mixer_plugin *plugin,
97                 struct snd_control *ctl, struct snd_ctl_tlv *tlv)
98 {
99 /*
100  *   TLV payload and len can be retrieved using:
101  *   void *payload = &tlv->tlv[0];
102  *   size_t tlv_size = tlv->length;
103  */
104 
105     return 0;
106 }
107 
sample_mixer_enum_ctl_get(struct mixer_plugin * plugin,struct snd_control * ctl,struct snd_ctl_elem_value * ev)108 static int sample_mixer_enum_ctl_get(struct mixer_plugin *plugin,
109                 struct snd_control *ctl, struct snd_ctl_elem_value *ev)
110 {
111     return 0;
112 }
113 
sample_mixer_enum_ctl_put(struct mixer_plugin * plugin,struct snd_control * ctl,struct snd_ctl_elem_value * ev)114 static int sample_mixer_enum_ctl_put(struct mixer_plugin *plugin,
115                 struct snd_control *ctl, struct snd_ctl_elem_value *ev)
116 {
117 /*
118  *    Enum value can be retrieved using:
119  *    unsigned int val = ev->value.enumerated.item[0];
120  */
121     return 0;
122 }
123 
124 static struct snd_value_int sample_mixer_ctl_value_int =
125     SND_VALUE_INTEGER(3, 0, 1000, 100);
126 
127 /* 512 max bytes for non-tlv byte controls */
128 static struct snd_value_bytes byte_array_ctl_bytes =
129     SND_VALUE_BYTES(512);
130 
131 static struct snd_value_tlv_bytes sample_mixer_tlv_ctl_bytes =
132     SND_VALUE_TLV_BYTES(1024, sample_mixer_tlv_ctl_get, sample_mixer_tlv_ctl_put);
133 
create_integer_ctl(struct sample_mixer_priv * priv,int ctl_idx,int pval,void * pdata)134 static void create_integer_ctl(struct sample_mixer_priv *priv,
135                 int ctl_idx, int pval, void *pdata)
136 {
137     struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx);
138     char *ctl_name = strdup("Sample integer control");
139 
140     /* pval and pdata can be retrieved using snd_control during get()/put() */
141     INIT_SND_CONTROL_INTEGER(ctl, ctl_name, sample_mixer_int_ctl_get,
142                     sample_mixer_int_ctl_put, sample_mixer_ctl_value_int, pval, pdata);
143 }
144 
create_byte_array_ctl(struct sample_mixer_priv * priv,int ctl_idx,int pval,void * pdata)145 static void create_byte_array_ctl(struct sample_mixer_priv *priv,
146     int ctl_idx, int pval, void *pdata)
147 {
148     struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx);
149     char *ctl_name = strdup("Sample byte array control");
150 
151     INIT_SND_CONTROL_BYTES(ctl, ctl_name, sample_mixer_byte_array_ctl_get,
152             sample_mixer_byte_array_ctl_put, byte_array_ctl_bytes,
153             pval, pdata);
154 }
155 
create_tlv_ctl(struct sample_mixer_priv * priv,int ctl_idx,int pval,void * pdata)156 static void create_tlv_ctl(struct sample_mixer_priv *priv,
157                 int ctl_idx, int pval, void *pdata)
158 {
159     struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx);
160     char *ctl_name = strdup("Sample tlv control");
161 
162     INIT_SND_CONTROL_TLV_BYTES(ctl, ctl_name, sample_mixer_tlv_ctl_bytes,
163                     pval, pdata);
164 }
165 
create_enum_ctl(struct sample_mixer_priv * priv,int ctl_idx,struct snd_value_enum * e,int pval,void * pdata)166 static void create_enum_ctl(struct sample_mixer_priv *priv,
167             int ctl_idx, struct snd_value_enum *e,
168             int pval, void *pdata)
169 {
170     struct snd_control *ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, ctl_idx);
171     char *ctl_name = strdup("Sample enum control");
172 
173     INIT_SND_CONTROL_ENUM(ctl, ctl_name, sample_mixer_enum_ctl_get,
174                     sample_mixer_enum_ctl_put, e, pval, pdata);
175 }
176 
sample_mixer_form_ctls(struct sample_mixer_priv * priv,int ctl_idx)177 static int sample_mixer_form_ctls(struct sample_mixer_priv *priv, int ctl_idx)
178 {
179     create_integer_ctl(priv, ctl_idx, 0, NULL);
180     ctl_idx++;
181     create_byte_array_ctl(priv, ctl_idx, 0, NULL);
182     ctl_idx++;
183     create_tlv_ctl(priv, ctl_idx, 0, NULL);
184     ctl_idx++;
185     create_enum_ctl(priv, ctl_idx, &priv->sample_enum, 0, NULL);
186     ctl_idx++;
187 
188     return 0;
189 }
190 
sample_mixer_read_event(struct mixer_plugin * plugin,struct snd_ctl_event * ev,size_t size)191 static ssize_t sample_mixer_read_event(struct mixer_plugin *plugin,
192                               struct snd_ctl_event *ev, size_t size)
193 {
194     /* Fill snd_ctl_event *ev before sending.
195      * Return : sizeof(struct snd_ctl_event),
196      *          0 in case no event present.
197      */
198 
199     return 0;
200 }
201 
sample_mixer_subscribe_events(struct mixer_plugin * plugin,mixer_event_callback event_cb)202 static int sample_mixer_subscribe_events(struct mixer_plugin *plugin,
203                                   mixer_event_callback event_cb)
204 {
205     struct sample_mixer_priv *priv = plugin->priv;
206 
207     priv->event_cb = event_cb;
208    /* event_cb is the callback function which needs to be called
209     * when an event occurs. This will unblock poll() on mixer fd
210     * which is called from mixer_wait_event().
211     * Once poll is unblocked, clients can call mixer_read_event()
212     * During unsubscribe(), event_cb is NULL.
213     */
214     return 0;
215 }
216 
sample_mixer_alloc_ctls(struct sample_mixer_priv * priv)217 static int sample_mixer_alloc_ctls(struct sample_mixer_priv *priv)
218 {
219     int ret = 0, i;
220 
221     priv->ctls = calloc(priv->ctl_count, sizeof(*priv->ctls));
222     if (!priv->ctls) {
223         return -ENOMEM;
224     }
225 
226     priv->sample_enum.items = ARRAY_SIZE(sample_enum_text);
227     priv->sample_enum.texts = calloc(priv->sample_enum.items, sizeof(*priv->sample_enum.texts));
228 
229     for (i = 0; i < ARRAY_SIZE(sample_enum_text); i++)
230         priv->sample_enum.texts[i] = strdup(sample_enum_text[i]);
231 
232     return sample_mixer_form_ctls(priv, 0);
233 }
234 
sample_mixer_free_ctls(struct sample_mixer_priv * priv)235 static void sample_mixer_free_ctls(struct sample_mixer_priv *priv)
236 {
237     int num_enums, i;
238     struct snd_control *ctl = NULL;
239 
240     for (i = 0; i < priv->ctl_count; i++) {
241         ctl = SAMPLE_MIXER_PRIV_GET_CTL_PTR(priv, i);
242         if (ctl->name)
243             free((void *)ctl->name);
244     }
245 
246     num_enums = priv->sample_enum.items;
247 
248     for (i = 0; i < num_enums; i++)
249         free(priv->sample_enum.texts[i]);
250 
251     free(priv->sample_enum.texts);
252     priv->ctl_count = 0;
253 
254     if (priv->ctls) {
255         free(priv->ctls);
256         priv->ctls = NULL;
257     }
258 }
259 
sample_mixer_close(struct mixer_plugin ** plugin)260 static void sample_mixer_close(struct mixer_plugin **plugin)
261 {
262     struct mixer_plugin *mp = *plugin;
263     struct sample_mixer_priv *priv = mp->priv;
264 
265     /* unblock mixer event during close */
266     if (priv->event_cb)
267         priv->event_cb(mp);
268     sample_mixer_subscribe_events(mp, NULL);
269     sample_mixer_free_ctls(priv);
270     free(priv);
271     free(*plugin);
272     *plugin = NULL;
273 }
274 
sample_mixer_open(struct mixer_plugin ** plugin,unsigned int card)275 int sample_mixer_open(struct mixer_plugin **plugin, unsigned int card)
276 {
277     struct mixer_plugin *mp;
278     struct sample_mixer_priv *priv;
279     int i, ret = 0;
280     int ctl_cnt = 4;
281 
282     mp = calloc(1, sizeof(*mp));
283     if (!mp) {
284         return -ENOMEM;
285     }
286 
287     priv = calloc(1, sizeof(*priv));
288     if (!priv) {
289         ret = -ENOMEM;
290         goto err_priv_alloc;
291     }
292 
293     priv->ctl_count = ctl_cnt;
294     ret = sample_mixer_alloc_ctls(priv);
295     if (ret)
296         goto err_ctls_alloc;
297 
298     /* Register the controls */
299     mp->controls = priv->ctls;
300     mp->num_controls = priv->ctl_count;
301     mp->priv = priv;
302     *plugin = mp;
303 
304     return 0;
305 
306 err_ctls_alloc:
307     sample_mixer_free_ctls(priv);
308     free(priv);
309 
310 err_priv_alloc:
311     free(mp);
312     return ret;
313 }
314 
315 struct mixer_plugin_ops mixer_plugin_ops = {
316     .open = sample_mixer_open,
317     .close = sample_mixer_close,
318     .subscribe_events = sample_mixer_subscribe_events,
319     .read_event = sample_mixer_read_event,
320 };
321