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