blob: afbc015c27d13aea7c43248ac039a9ffc59d4cf4 [file] [log] [blame]
Simon Wilson79d39652011-05-25 13:44:23 -07001/* mixer.c
2**
3** Copyright 2011, The Android Open Source Project
4**
5** Redistribution and use in source and binary forms, with or without
6** modification, are permitted provided that the following conditions are met:
7** * Redistributions of source code must retain the above copyright
8** notice, this list of conditions and the following disclaimer.
9** * Redistributions in binary form must reproduce the above copyright
10** notice, this list of conditions and the following disclaimer in the
11** documentation and/or other materials provided with the distribution.
12** * Neither the name of The Android Open Source Project nor the names of
13** its contributors may be used to endorse or promote products derived
14** from this software without specific prior written permission.
15**
16** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26** DAMAGE.
27*/
28
29#include <stdio.h>
30#include <stdlib.h>
Ben Zhang7ed2ffb2016-04-22 17:59:40 -070031#include <stdint.h>
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -070032#include <stdbool.h>
Simon Wilson79d39652011-05-25 13:44:23 -070033#include <string.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <errno.h>
37#include <ctype.h>
Richard Fitzgerald57a87742014-09-09 16:54:12 +010038#include <limits.h>
Dima Krasner696c4482016-03-05 19:50:02 +020039#include <time.h>
Pankaj Bharadiya010121a2017-01-11 08:57:06 +053040#include <poll.h>
Simon Wilson79d39652011-05-25 13:44:23 -070041
Simon Wilson6a52f2c2012-05-04 16:32:10 -070042#include <sys/ioctl.h>
43
Simon Wilson79d39652011-05-25 13:44:23 -070044#include <linux/ioctl.h>
Taylor Holberton4c5a11d2018-11-28 14:29:52 -050045
46#ifndef __force
Simon Wilson79d39652011-05-25 13:44:23 -070047#define __force
Taylor Holberton4c5a11d2018-11-28 14:29:52 -050048#endif
49
50#ifndef __bitwise
Simon Wilson79d39652011-05-25 13:44:23 -070051#define __bitwise
Taylor Holberton4c5a11d2018-11-28 14:29:52 -050052#endif
53
54#ifndef __user
Simon Wilson79d39652011-05-25 13:44:23 -070055#define __user
Taylor Holberton4c5a11d2018-11-28 14:29:52 -050056#endif
57
Simon Wilson79d39652011-05-25 13:44:23 -070058#include <sound/asound.h>
59
Ricardo Biehl Pasquali04952ee2016-10-05 20:32:09 -030060#include <tinyalsa/mixer.h>
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -070061#include <tinyalsa/plugin.h>
62
63#include "mixer_io.h"
Simon Wilson79d39652011-05-25 13:44:23 -070064
Taylor Holberton7c8b20a2016-10-01 19:25:19 -040065/** A mixer control.
Taylor Holberton8ae5d112016-11-19 10:35:25 -080066 * @ingroup libtinyalsa-mixer
Taylor Holberton7c8b20a2016-10-01 19:25:19 -040067 */
Simon Wilson79d39652011-05-25 13:44:23 -070068struct mixer_ctl {
Taylor Holberton7c8b20a2016-10-01 19:25:19 -040069 /** The mixer that the mixer control belongs to */
Simon Wilson79d39652011-05-25 13:44:23 -070070 struct mixer *mixer;
Taylor Holbertonad4d7d72016-11-23 13:34:18 -080071 /** Information on the control's value (i.e. type, number of values) */
Richard Fitzgerald899cece2014-09-09 17:03:21 +010072 struct snd_ctl_elem_info info;
Taylor Holbertonad4d7d72016-11-23 13:34:18 -080073 /** A list of string representations of enumerated values (only valid for enumerated controls) */
Simon Wilson79d39652011-05-25 13:44:23 -070074 char **ename;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -070075 /** Pointer to the group that the control belongs to */
76 struct mixer_ctl_group *grp;
77};
78
79struct mixer_ctl_group {
80 /** A continuous array of mixer controls */
81 struct mixer_ctl *ctl;
82 /** The number of mixer controls */
83 unsigned int count;
84 /** The number of events associated with this group */
85 unsigned int event_cnt;
86 /** The operations corresponding to this group */
87 const struct mixer_ops *ops;
88 /** Private data for storing group specific data */
89 void *data;
Simon Wilson79d39652011-05-25 13:44:23 -070090};
91
Taylor Holberton7c8b20a2016-10-01 19:25:19 -040092/** A mixer handle.
Taylor Holberton8ae5d112016-11-19 10:35:25 -080093 * @ingroup libtinyalsa-mixer
Taylor Holberton7c8b20a2016-10-01 19:25:19 -040094 */
Simon Wilson79d39652011-05-25 13:44:23 -070095struct mixer {
Taylor Holberton7c8b20a2016-10-01 19:25:19 -040096 /** File descriptor for the card */
Simon Wilson79d39652011-05-25 13:44:23 -070097 int fd;
Taylor Holberton7c8b20a2016-10-01 19:25:19 -040098 /** Card information */
Simon Wilsonec281392013-06-28 16:21:31 -070099 struct snd_ctl_card_info card_info;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700100 /* Hardware (kernel interface) mixer control group */
101 struct mixer_ctl_group *h_grp;
102 /* Virtual (Plugin interface) mixer control group */
103 struct mixer_ctl_group *v_grp;
104 /* Total count of mixer controls from both groups */
105 unsigned int total_count;
106 /* Flag to track if card information is already retrieved */
107 bool is_card_info_retrieved;
108
Simon Wilson79d39652011-05-25 13:44:23 -0700109};
110
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100111static void mixer_cleanup_control(struct mixer_ctl *ctl)
112{
113 unsigned int m;
114
115 if (ctl->ename) {
116 unsigned int max = ctl->info.value.enumerated.items;
117 for (m = 0; m < max; m++)
118 free(ctl->ename[m]);
119 free(ctl->ename);
120 }
121}
122
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700123static void mixer_grp_close(struct mixer *mixer, struct mixer_ctl_group *grp)
124{
125 unsigned int n;
126
127 if (!grp)
128 return;
129
130 if (grp->ctl) {
131 for (n = 0; n < grp->count; n++)
132 mixer_cleanup_control(&grp->ctl[n]);
133 free(grp->ctl);
134 }
135
Taylor Holbertonab224a02020-06-03 09:47:50 -0400136 free(grp);
137
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700138 mixer->is_card_info_retrieved = false;
139}
140
Taylor Holberton7c8b20a2016-10-01 19:25:19 -0400141/** Closes a mixer returned by @ref mixer_open.
142 * @param mixer A mixer handle.
Taylor Holberton8ae5d112016-11-19 10:35:25 -0800143 * @ingroup libtinyalsa-mixer
Taylor Holberton7c8b20a2016-10-01 19:25:19 -0400144 */
Simon Wilson79d39652011-05-25 13:44:23 -0700145void mixer_close(struct mixer *mixer)
146{
Simon Wilson193b1c32011-06-07 23:42:38 -0700147 if (!mixer)
Simon Wilsond2cb5032011-06-04 00:57:17 -0700148 return;
Simon Wilsond2cb5032011-06-04 00:57:17 -0700149
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700150 if (mixer->fd >= 0 && mixer->h_grp)
151 mixer->h_grp->ops->close(mixer->h_grp->data);
152 mixer_grp_close(mixer, mixer->h_grp);
Simon Wilson79d39652011-05-25 13:44:23 -0700153
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700154#ifdef TINYALSA_USES_PLUGINS
155 if (mixer->v_grp)
156 mixer->v_grp->ops->close(mixer->v_grp->data);
157 mixer_grp_close(mixer, mixer->v_grp);
158#endif
Simon Wilson79d39652011-05-25 13:44:23 -0700159
Simon Wilson79d39652011-05-25 13:44:23 -0700160 free(mixer);
161
162 /* TODO: verify frees */
163}
164
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100165static void *mixer_realloc_z(void *ptr, size_t curnum, size_t newnum, size_t size)
166{
167 int8_t *newp;
168
169 newp = realloc(ptr, size * newnum);
170 if (!newp)
171 return NULL;
172
173 memset(newp + (curnum * size), 0, (newnum - curnum) * size);
174 return newp;
175}
176
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700177static int add_controls(struct mixer *mixer, struct mixer_ctl_group *grp)
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100178{
179 struct snd_ctl_elem_list elist;
180 struct snd_ctl_elem_id *eid = NULL;
181 struct mixer_ctl *ctl;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700182 const unsigned int old_count = grp->count;
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100183 unsigned int new_count;
184 unsigned int n;
185
186 memset(&elist, 0, sizeof(elist));
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700187 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100188 goto fail;
189
190 if (old_count == elist.count)
191 return 0; /* no new controls return unchanged */
192
193 if (old_count > elist.count)
194 return -1; /* driver has removed controls - this is bad */
195
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700196 ctl = mixer_realloc_z(grp->ctl, old_count, elist.count,
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100197 sizeof(struct mixer_ctl));
198 if (!ctl)
199 goto fail;
200
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700201 grp->ctl = ctl;
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100202
203 /* ALSA drivers are not supposed to remove or re-order controls that
204 * have already been created so we know that any new controls must
205 * be after the ones we have already collected
206 */
207 new_count = elist.count;
208 elist.space = new_count - old_count; /* controls we haven't seen before */
209 elist.offset = old_count; /* first control we haven't seen */
210
211 eid = calloc(elist.space, sizeof(struct snd_ctl_elem_id));
212 if (!eid)
213 goto fail;
214
215 elist.pids = eid;
216
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700217 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100218 goto fail;
219
220 for (n = old_count; n < new_count; n++) {
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700221 struct snd_ctl_elem_info *ei = &grp->ctl[n].info;
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100222 ei->id.numid = eid[n - old_count].numid;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700223 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100224 goto fail_extend;
225 ctl[n].mixer = mixer;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700226 ctl[n].grp = grp;
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100227 }
228
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700229 grp->count = new_count;
230 mixer->total_count += (new_count - old_count);
231
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100232 free(eid);
233 return 0;
234
235fail_extend:
236 /* cleanup the control we failed on but leave the ones that were already
237 * added. Also no advantage to shrinking the resized memory block, we
238 * might want to extend the controls again later
239 */
240 mixer_cleanup_control(&ctl[n]);
241
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700242 grp->count = n; /* keep controls we successfully added */
243 mixer->total_count += (n - old_count);
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100244 /* fall through... */
245fail:
246 free(eid);
247 return -1;
248}
249
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700250static int mixer_grp_open(struct mixer *mixer, unsigned int card, bool is_hw)
251{
252 struct mixer_ctl_group *grp = NULL;
253 const struct mixer_ops *ops = NULL;
254 void *data = NULL;
255 int fd, ret;
256
257 grp = calloc(1, sizeof(*grp));
258 if (!grp)
259 return -ENOMEM;
260
261 if (is_hw) {
262 mixer->fd = -1;
263 fd = mixer_hw_open(card, &data, &ops);
264 if (fd < 0) {
265 ret = fd;
266 goto err_open;
267 }
268 mixer->fd = fd;
269 mixer->h_grp = grp;
270 }
271#ifdef TINYALSA_USES_PLUGINS
272 else {
273 ret = mixer_plugin_open(card, &data, &ops);
274 if (ret < 0)
275 goto err_open;
276 mixer->v_grp = grp;
277 }
278#endif
279 grp->ops = ops;
280 grp->data = data;
281
282 if (!mixer->is_card_info_retrieved) {
283 ret = grp->ops->ioctl(data, SNDRV_CTL_IOCTL_CARD_INFO,
284 &mixer->card_info);
285 if (ret < 0)
286 goto err_card_info;
287 mixer->is_card_info_retrieved = true;
288 }
289
290 ret = add_controls(mixer, grp);
291 if (ret < 0)
292 goto err_card_info;
293
294 return 0;
295
296err_card_info:
297 grp->ops->close(grp->data);
298
299err_open:
300 free(grp);
301 return ret;
302
303}
304
Taylor Holberton7c8b20a2016-10-01 19:25:19 -0400305/** Opens a mixer for a given card.
306 * @param card The card to open the mixer for.
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500307 * @returns An initialized mixer handle.
Taylor Holberton8ae5d112016-11-19 10:35:25 -0800308 * @ingroup libtinyalsa-mixer
Taylor Holberton7c8b20a2016-10-01 19:25:19 -0400309 */
Simon Wilson1bd580f2011-06-02 15:58:41 -0700310struct mixer *mixer_open(unsigned int card)
Simon Wilson79d39652011-05-25 13:44:23 -0700311{
Simon Wilson79d39652011-05-25 13:44:23 -0700312 struct mixer *mixer = NULL;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700313 int h_status, v_status = -1;
Simon Wilson79d39652011-05-25 13:44:23 -0700314
Simon Wilson79d39652011-05-25 13:44:23 -0700315 mixer = calloc(1, sizeof(*mixer));
316 if (!mixer)
317 goto fail;
318
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700319 h_status = mixer_grp_open(mixer, card, true);
Simon Wilson79d39652011-05-25 13:44:23 -0700320
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700321#ifdef TINYALSA_USES_PLUGINS
322 v_status = mixer_grp_open(mixer, card, false);
323#endif
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100324
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700325 /* both hw and virtual should fail for mixer_open to fail */
326 if (h_status < 0 && v_status < 0)
Simon Wilson79d39652011-05-25 13:44:23 -0700327 goto fail;
328
Simon Wilson79d39652011-05-25 13:44:23 -0700329 return mixer;
330
331fail:
Simon Wilson79d39652011-05-25 13:44:23 -0700332 if (mixer)
333 mixer_close(mixer);
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700334
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100335 return NULL;
336}
337
338/** Some controls may not be present at boot time, e.g. controls from runtime
339 * loadable DSP firmware. This function adds any new controls that have appeared
340 * since mixer_open() or the last call to this function. This assumes a well-
341 * behaved codec driver that does not delete controls that already exists, so
342 * any added controls must be after the last one we already saw. Scanning only
343 * the new controls is much faster than calling mixer_close() then mixer_open()
344 * to re-scan all controls.
345 *
346 * NOTE: this invalidates any struct mixer_ctl pointers previously obtained
347 * from mixer_get_ctl() and mixer_get_ctl_by_name(). Either refresh all your
348 * stored pointers after calling mixer_update_ctls(), or (better) do not
349 * store struct mixer_ctl pointers, instead lookup the control by name or
350 * id only when you are about to use it. The overhead of lookup by id
351 * using mixer_get_ctl() is negligible.
352 * @param mixer An initialized mixer handle.
353 * @returns 0 on success, -1 on failure
354 */
355int mixer_add_new_ctls(struct mixer *mixer)
356{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700357 int rc1 = 0, rc2 = 0;
358
Richard Fitzgeraldfd329032014-09-09 17:14:09 +0100359 if (!mixer)
360 return 0;
361
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700362 /* add the h_grp controls */
363 if (mixer->h_grp)
364 rc1 = add_controls(mixer, mixer->h_grp);
365
366#ifdef TINYALSA_USES_PLUGINS
367 /* add the v_grp controls */
368 if (mixer->v_grp)
369 rc2 = add_controls(mixer, mixer->v_grp);
370#endif
371
372 if (rc1 < 0)
373 return rc1;
374 if (rc2 < 0)
375 return rc2;
376
377 return 0;
Simon Wilson79d39652011-05-25 13:44:23 -0700378}
379
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500380/** Gets the name of the mixer's card.
381 * @param mixer An initialized mixer handle.
382 * @returns The name of the mixer's card.
383 * @ingroup libtinyalsa-mixer
384 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800385const char *mixer_get_name(const struct mixer *mixer)
Simon Wilsonec281392013-06-28 16:21:31 -0700386{
dvdli75f4a602021-05-19 19:23:03 +0800387 if (!mixer) {
388 return NULL;
389 }
390
Simon Wilsonec281392013-06-28 16:21:31 -0700391 return (const char *)mixer->card_info.name;
392}
393
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500394/** Gets the number of mixer controls for a given mixer.
395 * @param mixer An initialized mixer handle.
396 * @returns The number of mixer controls for the given mixer.
397 * @ingroup libtinyalsa-mixer
398 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800399unsigned int mixer_get_num_ctls(const struct mixer *mixer)
Simon Wilson79d39652011-05-25 13:44:23 -0700400{
Simon Wilson193b1c32011-06-07 23:42:38 -0700401 if (!mixer)
Simon Wilson98c1f162011-06-07 16:12:32 -0700402 return 0;
Simon Wilsond2cb5032011-06-04 00:57:17 -0700403
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700404 return mixer->total_count;
Simon Wilson79d39652011-05-25 13:44:23 -0700405}
406
Taylor Holberton94c7c832016-12-01 18:35:24 -0800407/** Gets the number of mixer controls, that go by a specified name, for a given mixer.
408 * @param mixer An initialized mixer handle.
409 * @param name The name of the mixer control
410 * @returns The number of mixer controls, specified by @p name, for the given mixer.
411 * @ingroup libtinyalsa-mixer
412 */
413unsigned int mixer_get_num_ctls_by_name(const struct mixer *mixer, const char *name)
414{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700415 struct mixer_ctl_group *grp;
Taylor Holberton94c7c832016-12-01 18:35:24 -0800416 unsigned int n;
417 unsigned int count = 0;
418 struct mixer_ctl *ctl;
419
dvdli75f4a602021-05-19 19:23:03 +0800420 if (!mixer || !name) {
Taylor Holberton94c7c832016-12-01 18:35:24 -0800421 return 0;
dvdli75f4a602021-05-19 19:23:03 +0800422 }
Taylor Holberton94c7c832016-12-01 18:35:24 -0800423
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700424 if (mixer->h_grp) {
425 grp = mixer->h_grp;
426 ctl = grp->ctl;
Taylor Holberton94c7c832016-12-01 18:35:24 -0800427
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700428 for (n = 0; n < grp->count; n++)
429 if (!strcmp(name, (char*) ctl[n].info.id.name))
430 count++;
431 }
432#ifdef TINYALSA_USES_PLUGINS
433 if (mixer->v_grp) {
434 grp = mixer->v_grp;
435 ctl = grp->ctl;
436
437 for (n = 0; n < grp->count; n++)
438 if (!strcmp(name, (char*) ctl[n].info.id.name))
439 count++;
440 }
441#endif
Taylor Holberton94c7c832016-12-01 18:35:24 -0800442
443 return count;
444}
445
Pankaj Bharadiya010121a2017-01-11 08:57:06 +0530446/** Subscribes for the mixer events.
447 * @param mixer A mixer handle.
448 * @param subscribe value indicating subscribe or unsubscribe for events
449 * @returns On success, zero.
450 * On failure, non-zero.
451 * @ingroup libtinyalsa-mixer
452 */
453int mixer_subscribe_events(struct mixer *mixer, int subscribe)
454{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700455 struct mixer_ctl_group *grp;
456
dvdli75f4a602021-05-19 19:23:03 +0800457 if (!mixer) {
458 return -EINVAL;
459 }
460
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700461 if (mixer->h_grp) {
462 grp = mixer->h_grp;
463 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
464 return -1;
Pankaj Bharadiya010121a2017-01-11 08:57:06 +0530465 }
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700466
467#ifdef TINYALSA_USES_PLUGINS
468 if (mixer->v_grp) {
469 grp = mixer->v_grp;
470 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0)
471 return -1;
472 }
473#endif
Pankaj Bharadiya010121a2017-01-11 08:57:06 +0530474 return 0;
475}
476
477/** Wait for mixer events.
478 * @param mixer A mixer handle.
479 * @param timeout timout value
Pankaj Bharadiya95c79d82017-01-11 11:28:02 +0530480 * @returns On success, 1.
481 * On failure, -errno.
482 * On timeout, 0
Pankaj Bharadiya010121a2017-01-11 08:57:06 +0530483 * @ingroup libtinyalsa-mixer
484 */
485int mixer_wait_event(struct mixer *mixer, int timeout)
486{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700487 struct pollfd *pfd;
488 struct mixer_ctl_group *grp;
Rohit kumardf855e82020-06-02 11:41:13 +0530489 int count = 0, num_fds = 0, i, ret = 0;
Pankaj Bharadiya010121a2017-01-11 08:57:06 +0530490
dvdli75f4a602021-05-19 19:23:03 +0800491 if (!mixer) {
492 return -EINVAL;
493 }
494
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700495 if (mixer->fd >= 0)
496 num_fds++;
497
498#ifdef TINYALSA_USES_PLUGINS
499 if (mixer->v_grp)
500 num_fds++;
501#endif
502
Rohit kumardf855e82020-06-02 11:41:13 +0530503 pfd = (struct pollfd *)calloc(num_fds, sizeof(struct pollfd));
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700504 if (!pfd)
505 return -ENOMEM;
506
507 if (mixer->fd >= 0) {
508 pfd[count].fd = mixer->fd;
509 pfd[count].events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
510 count++;
511 }
512
513#ifdef TINYALSA_USES_PLUGINS
514 if (mixer->v_grp) {
515 grp = mixer->v_grp;
516 if (!grp->ops->get_poll_fd(grp->data, pfd, count)) {
517 pfd[count].events = POLLIN | POLLERR | POLLNVAL;
518 count++;
519 }
520 }
521#endif
522
523 if (!count)
Rohit kumardf855e82020-06-02 11:41:13 +0530524 goto exit;
Pankaj Bharadiya010121a2017-01-11 08:57:06 +0530525
526 for (;;) {
527 int err;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700528 err = poll(pfd, count, timeout);
Rohit kumardf855e82020-06-02 11:41:13 +0530529 if (err < 0) {
530 ret = -errno;
531 goto exit;
532 }
Pankaj Bharadiya010121a2017-01-11 08:57:06 +0530533 if (!err)
Rohit kumardf855e82020-06-02 11:41:13 +0530534 goto exit;
535
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700536 for (i = 0; i < count; i++) {
Rohit kumardf855e82020-06-02 11:41:13 +0530537 if (pfd[i].revents & (POLLERR | POLLNVAL)) {
538 ret = -EIO;
539 goto exit;
540 }
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700541 if (pfd[i].revents & (POLLIN | POLLOUT)) {
542 if ((i == 0) && mixer->fd >= 0) {
543 grp = mixer->h_grp;
544 grp->event_cnt++;
545 }
546#ifdef TINYALSA_USES_PLUGINS
Rohit kumardf855e82020-06-02 11:41:13 +0530547 else {
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700548 grp = mixer->v_grp;
549 grp->event_cnt++;
550 }
551#endif
Rohit kumardf855e82020-06-02 11:41:13 +0530552 ret = 1;
553 goto exit;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700554 }
555 }
Pankaj Bharadiya010121a2017-01-11 08:57:06 +0530556 }
Rohit kumardf855e82020-06-02 11:41:13 +0530557exit:
558 free(pfd);
559 return ret;
Pankaj Bharadiya010121a2017-01-11 08:57:06 +0530560}
561
Andrew Chantbdc1fcf2018-02-05 15:16:41 -0800562/** Consume a mixer event.
563 * If mixer_subscribe_events has been called,
564 * mixer_wait_event will identify when a control value has changed.
565 * This function will clear a single event from the mixer so that
566 * further events can be alerted.
567 *
568 * @param mixer A mixer handle.
David Lic0d68b12020-12-02 07:46:19 +0000569 * @returns 1 on success. 0, if no pending event. -errno on failure.
Andrew Chantbdc1fcf2018-02-05 15:16:41 -0800570 * @ingroup libtinyalsa-mixer
571 */
Rohit kumarf38405c2020-06-02 11:14:38 +0530572int mixer_consume_event(struct mixer *mixer)
573{
David Lic0d68b12020-12-02 07:46:19 +0000574 struct mixer_ctl_event ev;
dvdli75f4a602021-05-19 19:23:03 +0800575 if (!mixer) {
576 return -EINVAL;
577 }
Rohit kumarf38405c2020-06-02 11:14:38 +0530578
579 return mixer_read_event(mixer, &ev);
580}
581
David Lic0d68b12020-12-02 07:46:19 +0000582/** Read a mixer control event.
583 * Try to read an control event from mixer.
584 *
585 * @param mixer A mixer handle.
586 * @param event Output parameter. If there is an event read form the mixer, this function will fill
587 * the event data into it.
588 * @returns 1 on success. 0, if no pending event. -errno on failure.
589 * @ingroup libtinyalsa-mixer
590 */
591int mixer_read_event(struct mixer *mixer, struct mixer_ctl_event *event)
Rohit kumarf38405c2020-06-02 11:14:38 +0530592{
David Lic0d68b12020-12-02 07:46:19 +0000593 struct mixer_ctl_group *grp = NULL;
594 struct snd_ctl_event ev;
595 ssize_t bytes = 0;
596
597 if (!mixer || !event) {
598 return -EINVAL;
599 }
Rohit kumarf38405c2020-06-02 11:14:38 +0530600
601 if (mixer->h_grp) {
David Lic0d68b12020-12-02 07:46:19 +0000602 if (mixer->h_grp->event_cnt > 0) {
603 grp = mixer->h_grp;
Rohit kumarf38405c2020-06-02 11:14:38 +0530604 }
605 }
606#ifdef TINYALSA_USES_PLUGINS
607 if (mixer->v_grp) {
David Lic0d68b12020-12-02 07:46:19 +0000608 if (mixer->v_grp->event_cnt > 0) {
609 grp = mixer->v_grp;
Rohit kumarf38405c2020-06-02 11:14:38 +0530610 }
611 }
612#endif
David Lic0d68b12020-12-02 07:46:19 +0000613 if (grp) {
614 grp->event_cnt--;
615 bytes = grp->ops->read_event(grp->data, &ev, sizeof(ev));
616
617 if (bytes < 0) {
618 return -errno;
619 }
620
621 if (bytes == sizeof(*event)) {
622 memcpy(event, &ev, sizeof(*event));
623 return 1;
624 }
625 }
626
Rohit kumarf38405c2020-06-02 11:14:38 +0530627 return 0;
Andrew Chantbdc1fcf2018-02-05 15:16:41 -0800628}
629
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700630static unsigned int mixer_grp_get_count(struct mixer_ctl_group *grp)
631{
632 if (!grp)
633 return 0;
634
635 return grp->count;
636}
637
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500638/** Gets a mixer control handle, by the mixer control's id.
Taylor Holbertona94295b2016-12-01 18:23:16 -0800639 * For non-const access, see @ref mixer_get_ctl
640 * @param mixer An initialized mixer handle.
641 * @param id The control's id in the given mixer.
642 * @returns A handle to the mixer control.
643 * @ingroup libtinyalsa-mixer
644 */
645const struct mixer_ctl *mixer_get_ctl_const(const struct mixer *mixer, unsigned int id)
646{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700647 unsigned int h_count;
648
649 if (!mixer || (id >= mixer->total_count))
650 return NULL;
651
652 h_count = mixer_grp_get_count(mixer->h_grp);
653
654 if (id < h_count)
655 return mixer->h_grp->ctl + id;
656#ifdef TINYALSA_USES_PLUGINS
657 else {
658 unsigned int v_count = mixer_grp_get_count(mixer->v_grp);
659 if ((id - h_count) < v_count)
660 return mixer->v_grp->ctl + (id - h_count);
661 }
662#endif
Taylor Holbertona94295b2016-12-01 18:23:16 -0800663
664 return NULL;
665}
666
667/** Gets a mixer control handle, by the mixer control's id.
668 * For const access, see @ref mixer_get_ctl_const
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500669 * @param mixer An initialized mixer handle.
670 * @param id The control's id in the given mixer.
671 * @returns A handle to the mixer control.
672 * @ingroup libtinyalsa-mixer
673 */
Simon Wilson79d39652011-05-25 13:44:23 -0700674struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
675{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700676 unsigned int h_count;
Simon Wilson79d39652011-05-25 13:44:23 -0700677
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700678 if (!mixer || (id >= mixer->total_count))
679 return NULL;
680
681 h_count = mixer_grp_get_count(mixer->h_grp);
682
683 if (id < h_count)
684 return mixer->h_grp->ctl + id;
685#ifdef TINYALSA_USES_PLUGINS
686 else {
687 unsigned int v_count = mixer_grp_get_count(mixer->v_grp);
688 if ((id - h_count) < v_count)
689 return mixer->v_grp->ctl + (id - h_count);
690 }
691#endif
Simon Wilson79d39652011-05-25 13:44:23 -0700692 return NULL;
693}
694
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500695/** Gets the first instance of mixer control handle, by the mixer control's name.
696 * @param mixer An initialized mixer handle.
697 * @param name The control's name in the given mixer.
698 * @returns A handle to the mixer control.
699 * @ingroup libtinyalsa-mixer
700 */
Simon Wilson79d39652011-05-25 13:44:23 -0700701struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
702{
dvdli75f4a602021-05-19 19:23:03 +0800703 if (!mixer || !name) {
704 return NULL;
705 }
706
Frédéric Boisnard9e2c2402013-09-17 22:43:18 +0200707 return mixer_get_ctl_by_name_and_index(mixer, name, 0);
708}
709
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500710/** Gets an instance of mixer control handle, by the mixer control's name and index.
711 * For instance, if two controls have the name of 'Volume', then and index of 1 would return the second control.
712 * @param mixer An initialized mixer handle.
713 * @param name The control's name in the given mixer.
714 * @param index The control's index.
715 * @returns A handle to the mixer control.
716 * @ingroup libtinyalsa-mixer
717 */
Frédéric Boisnard9e2c2402013-09-17 22:43:18 +0200718struct mixer_ctl *mixer_get_ctl_by_name_and_index(struct mixer *mixer,
719 const char *name,
720 unsigned int index)
721{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700722 struct mixer_ctl_group *grp;
Simon Wilson79d39652011-05-25 13:44:23 -0700723 unsigned int n;
Svyatoslav Mishync7328362016-01-24 19:43:38 +0200724 struct mixer_ctl *ctl;
Simon Wilsond2cb5032011-06-04 00:57:17 -0700725
dvdli75f4a602021-05-19 19:23:03 +0800726 if (!mixer || !name) {
Simon Wilson193b1c32011-06-07 23:42:38 -0700727 return NULL;
dvdli75f4a602021-05-19 19:23:03 +0800728 }
Simon Wilsond2cb5032011-06-04 00:57:17 -0700729
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700730 if (mixer->h_grp) {
731 grp = mixer->h_grp;
732 ctl = grp->ctl;
Svyatoslav Mishync7328362016-01-24 19:43:38 +0200733
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700734 for (n = 0; n < grp->count; n++)
Caleb Connollyb49414e2021-11-02 18:56:21 +0000735 if (!strcmp(name, (char*) ctl[n].info.id.name)) {
736 if (index == 0) {
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700737 return ctl + n;
Caleb Connollyb49414e2021-11-02 18:56:21 +0000738 } else {
739 index--;
740 }
741 }
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700742 }
Simon Wilson79d39652011-05-25 13:44:23 -0700743
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700744#ifdef TINYALSA_USES_PLUGINS
745 if (mixer->v_grp) {
746 grp = mixer->v_grp;
747 ctl = grp->ctl;
748
749 for (n = 0; n < grp->count; n++)
Caleb Connollyb49414e2021-11-02 18:56:21 +0000750 if (!strcmp(name, (char*) ctl[n].info.id.name)) {
751 if (index == 0) {
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700752 return ctl + n;
Caleb Connollyb49414e2021-11-02 18:56:21 +0000753 } else {
754 index--;
755 }
756 }
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700757 }
758#endif
Simon Wilson79d39652011-05-25 13:44:23 -0700759 return NULL;
760}
761
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500762/** Updates the control's info.
763 * This is useful for a program that may be idle for a period of time.
764 * @param ctl An initialized control handle.
765 * @ingroup libtinyalsa-mixer
766 */
Simon Wilson710df882013-06-28 16:17:50 -0700767void mixer_ctl_update(struct mixer_ctl *ctl)
768{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700769 struct mixer_ctl_group *grp;
770
771 if (!ctl)
772 return;
773
774 grp = ctl->grp;
775 grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &ctl->info);
Simon Wilson710df882013-06-28 16:17:50 -0700776}
777
Pankaj Bharadiya9698d032017-01-09 12:23:14 +0530778/** Checks the control for TLV Read/Write access.
779 * @param ctl An initialized control handle.
780 * @returns On success, non-zero.
781 * On failure, zero.
782 * @ingroup libtinyalsa-mixer
783 */
784int mixer_ctl_is_access_tlv_rw(const struct mixer_ctl *ctl)
785{
dvdli75f4a602021-05-19 19:23:03 +0800786 if (!ctl) {
787 return 0;
788 }
789
Pankaj Bharadiya9698d032017-01-09 12:23:14 +0530790 return (ctl->info.access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
791}
792
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500793/** Gets the control's ID.
794 * @param ctl An initialized control handle.
795 * @returns On success, the control's ID is returned.
796 * On error, UINT_MAX is returned instead.
797 * @ingroup libtinyalsa-mixer
798 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800799unsigned int mixer_ctl_get_id(const struct mixer_ctl *ctl)
Richard Fitzgerald57a87742014-09-09 16:54:12 +0100800{
801 if (!ctl)
802 return UINT_MAX;
803
804 /* numid values start at 1, return a 0-base value that
805 * can be passed to mixer_get_ctl()
806 */
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100807 return ctl->info.id.numid - 1;
Richard Fitzgerald57a87742014-09-09 16:54:12 +0100808}
809
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500810/** Gets the name of the control.
811 * @param ctl An initialized control handle.
812 * @returns On success, the name of the control.
813 * On error, NULL.
814 * @ingroup libtinyalsa-mixer
815 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800816const char *mixer_ctl_get_name(const struct mixer_ctl *ctl)
Simon Wilsond2cb5032011-06-04 00:57:17 -0700817{
Simon Wilsonb29ac1a2012-03-08 10:15:08 -0800818 if (!ctl)
819 return NULL;
Simon Wilsond2cb5032011-06-04 00:57:17 -0700820
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100821 return (const char *)ctl->info.id.name;
Simon Wilsond2cb5032011-06-04 00:57:17 -0700822}
823
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500824/** Gets the value type of the control.
825 * @param ctl An initialized control handle
826 * @returns On success, the type of mixer control.
827 * On failure, it returns @ref MIXER_CTL_TYPE_UNKNOWN
828 * @ingroup libtinyalsa-mixer
829 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800830enum mixer_ctl_type mixer_ctl_get_type(const struct mixer_ctl *ctl)
Simon Wilsond2cb5032011-06-04 00:57:17 -0700831{
Simon Wilson193b1c32011-06-07 23:42:38 -0700832 if (!ctl)
Simon Wilsond2cb5032011-06-04 00:57:17 -0700833 return MIXER_CTL_TYPE_UNKNOWN;
Simon Wilsond2cb5032011-06-04 00:57:17 -0700834
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100835 switch (ctl->info.type) {
Simon Wilsond2cb5032011-06-04 00:57:17 -0700836 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
837 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
838 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
839 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
840 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
841 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
842 default: return MIXER_CTL_TYPE_UNKNOWN;
843 };
844}
845
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500846/** Gets the string that describes the value type of the control.
847 * @param ctl An initialized control handle
848 * @returns On success, a string describing type of mixer control.
849 * @ingroup libtinyalsa-mixer
850 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800851const char *mixer_ctl_get_type_string(const struct mixer_ctl *ctl)
Simon Wilsond2cb5032011-06-04 00:57:17 -0700852{
Simon Wilson193b1c32011-06-07 23:42:38 -0700853 if (!ctl)
Simon Wilsond2cb5032011-06-04 00:57:17 -0700854 return "";
Simon Wilsond2cb5032011-06-04 00:57:17 -0700855
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100856 switch (ctl->info.type) {
Simon Wilsond2cb5032011-06-04 00:57:17 -0700857 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
858 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
859 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
860 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
861 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
862 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
863 default: return "Unknown";
864 };
865}
866
Taylor Holbertonb7a28572016-11-19 23:45:00 -0500867/** Gets the number of values in the control.
868 * @param ctl An initialized control handle
869 * @returns The number of values in the mixer control
870 * @ingroup libtinyalsa-mixer
871 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800872unsigned int mixer_ctl_get_num_values(const struct mixer_ctl *ctl)
Simon Wilsond2cb5032011-06-04 00:57:17 -0700873{
Simon Wilson193b1c32011-06-07 23:42:38 -0700874 if (!ctl)
Simon Wilsond2cb5032011-06-04 00:57:17 -0700875 return 0;
Simon Wilsond2cb5032011-06-04 00:57:17 -0700876
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100877 return ctl->info.count;
Simon Wilsond2cb5032011-06-04 00:57:17 -0700878}
879
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800880static int percent_to_int(const struct snd_ctl_elem_info *ei, int percent)
Simon Wilson79d39652011-05-25 13:44:23 -0700881{
Baptiste Robert4a484e12013-09-12 15:37:53 +0200882 if ((percent > 100) || (percent < 0)) {
883 return -EINVAL;
884 }
Simon Wilson98c1f162011-06-07 16:12:32 -0700885
Baptiste Robert4a484e12013-09-12 15:37:53 +0200886 int range = (ei->value.integer.max - ei->value.integer.min);
Simon Wilson98c1f162011-06-07 16:12:32 -0700887
Simon Wilson79d39652011-05-25 13:44:23 -0700888 return ei->value.integer.min + (range * percent) / 100;
889}
890
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800891static int int_to_percent(const struct snd_ctl_elem_info *ei, int value)
Simon Wilson79d39652011-05-25 13:44:23 -0700892{
893 int range = (ei->value.integer.max - ei->value.integer.min);
Simon Wilson98c1f162011-06-07 16:12:32 -0700894
Simon Wilson79d39652011-05-25 13:44:23 -0700895 if (range == 0)
896 return 0;
Simon Wilson98c1f162011-06-07 16:12:32 -0700897
Daniela-Marinela Bistrean9962e712019-04-12 00:49:20 +0300898 return ((value - ei->value.integer.min) * 100) / range;
Simon Wilson79d39652011-05-25 13:44:23 -0700899}
900
Taylor Holberton4e557192016-11-23 11:48:06 -0800901/** Gets a percentage representation of a specified control value.
902 * @param ctl An initialized control handle.
903 * @param id The index of the value within the control.
904 * @returns On success, the percentage representation of the control value.
905 * On failure, -EINVAL is returned.
906 * @ingroup libtinyalsa-mixer
907 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800908int mixer_ctl_get_percent(const struct mixer_ctl *ctl, unsigned int id)
Simon Wilson79d39652011-05-25 13:44:23 -0700909{
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100910 if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
Simon Wilson193b1c32011-06-07 23:42:38 -0700911 return -EINVAL;
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700912
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100913 return int_to_percent(&ctl->info, mixer_ctl_get_value(ctl, id));
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700914}
915
Taylor Holberton4e557192016-11-23 11:48:06 -0800916/** Sets the value of a control by percent, specified by the value index.
917 * @param ctl An initialized control handle.
918 * @param id The index of the value to set
919 * @param percent A percentage value between 0 and 100.
920 * @returns On success, zero is returned.
921 * On failure, non-zero is returned.
922 * @ingroup libtinyalsa-mixer
923 */
Simon Wilsond2cb5032011-06-04 00:57:17 -0700924int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700925{
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100926 if (!ctl || (ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER))
Simon Wilson193b1c32011-06-07 23:42:38 -0700927 return -EINVAL;
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700928
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100929 return mixer_ctl_set_value(ctl, id, percent_to_int(&ctl->info, percent));
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700930}
931
Taylor Holberton4e557192016-11-23 11:48:06 -0800932/** Gets the value of a control.
933 * @param ctl An initialized control handle.
934 * @param id The index of the control value.
935 * @returns On success, the specified value is returned.
936 * On failure, -EINVAL is returned.
937 * @ingroup libtinyalsa-mixer
938 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800939int mixer_ctl_get_value(const struct mixer_ctl *ctl, unsigned int id)
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700940{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700941 struct mixer_ctl_group *grp;
Simon Wilson79d39652011-05-25 13:44:23 -0700942 struct snd_ctl_elem_value ev;
Simon Wilson193b1c32011-06-07 23:42:38 -0700943 int ret;
Simon Wilson79d39652011-05-25 13:44:23 -0700944
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100945 if (!ctl || (id >= ctl->info.count))
Simon Wilson193b1c32011-06-07 23:42:38 -0700946 return -EINVAL;
Simon Wilsond2cb5032011-06-04 00:57:17 -0700947
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700948 grp = ctl->grp;
Simon Wilson79d39652011-05-25 13:44:23 -0700949 memset(&ev, 0, sizeof(ev));
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100950 ev.id.numid = ctl->info.id.numid;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700951 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
Simon Wilson193b1c32011-06-07 23:42:38 -0700952 if (ret < 0)
953 return ret;
Simon Wilson79d39652011-05-25 13:44:23 -0700954
Richard Fitzgerald899cece2014-09-09 17:03:21 +0100955 switch (ctl->info.type) {
Simon Wilson79d39652011-05-25 13:44:23 -0700956 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
Simon Wilsond2cb5032011-06-04 00:57:17 -0700957 return !!ev.value.integer.value[id];
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700958
959 case SNDRV_CTL_ELEM_TYPE_INTEGER:
Simon Wilsond2cb5032011-06-04 00:57:17 -0700960 return ev.value.integer.value[id];
Simon Wilson066c9f62011-06-05 18:23:05 -0700961
962 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
963 return ev.value.enumerated.item[id];
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700964
Pierre-Louis Bossart52510162011-10-24 15:00:17 -0500965 case SNDRV_CTL_ELEM_TYPE_BYTES:
966 return ev.value.bytes.data[id];
967
Simon Wilson79d39652011-05-25 13:44:23 -0700968 default:
Simon Wilson193b1c32011-06-07 23:42:38 -0700969 return -EINVAL;
Simon Wilson79d39652011-05-25 13:44:23 -0700970 }
971
972 return 0;
973}
974
Taylor Holberton4e557192016-11-23 11:48:06 -0800975/** Gets the contents of a control's value array.
976 * @param ctl An initialized control handle.
977 * @param array A pointer to write the array data to.
978 * The size of this array must be equal to the number of items in the array
979 * multiplied by the size of each item.
980 * @param count The number of items in the array.
981 * This parameter must match the number of items in the control.
982 * The number of items in the control may be accessed via @ref mixer_ctl_get_num_values
983 * @returns On success, zero.
984 * On failure, non-zero.
985 * @ingroup libtinyalsa-mixer
986 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -0800987int mixer_ctl_get_array(const struct mixer_ctl *ctl, void *array, size_t count)
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +0100988{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700989 struct mixer_ctl_group *grp;
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +0100990 struct snd_ctl_elem_value ev;
Mythri P K45b2d042014-08-18 15:39:36 +0530991 int ret = 0;
Simon Wilson38f87f32012-10-23 15:05:23 -0700992 size_t size;
993 void *source;
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +0100994
dvdli75f4a602021-05-19 19:23:03 +0800995 if (!ctl || !array || count == 0) {
Pankaj Bharadiya3f813e42017-01-09 13:42:14 +0530996 return -EINVAL;
dvdli75f4a602021-05-19 19:23:03 +0800997 }
Pankaj Bharadiya3f813e42017-01-09 13:42:14 +0530998
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -0700999 grp = ctl->grp;
Pankaj Bharadiya3f813e42017-01-09 13:42:14 +05301000
Rohit kumarf4ee4532020-08-21 20:27:25 +05301001 if (count > ctl->info.count)
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +01001002 return -EINVAL;
1003
1004 memset(&ev, 0, sizeof(ev));
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001005 ev.id.numid = ctl->info.id.numid;
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +01001006
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001007 switch (ctl->info.type) {
Simon Wilson38f87f32012-10-23 15:05:23 -07001008 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1009 case SNDRV_CTL_ELEM_TYPE_INTEGER:
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001010 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
Mythri P K45b2d042014-08-18 15:39:36 +05301011 if (ret < 0)
1012 return ret;
Simon Wilson38f87f32012-10-23 15:05:23 -07001013 size = sizeof(ev.value.integer.value[0]);
1014 source = ev.value.integer.value;
1015 break;
1016
1017 case SNDRV_CTL_ELEM_TYPE_BYTES:
Mythri P K45b2d042014-08-18 15:39:36 +05301018 /* check if this is new bytes TLV */
Pankaj Bharadiya9698d032017-01-09 12:23:14 +05301019 if (mixer_ctl_is_access_tlv_rw(ctl)) {
Mythri P K45b2d042014-08-18 15:39:36 +05301020 struct snd_ctl_tlv *tlv;
1021 int ret;
1022
Ben Zhang7ed2ffb2016-04-22 17:59:40 -07001023 if (count > SIZE_MAX - sizeof(*tlv))
1024 return -EINVAL;
Rohit kumarf4ee4532020-08-21 20:27:25 +05301025
Mythri P K45b2d042014-08-18 15:39:36 +05301026 tlv = calloc(1, sizeof(*tlv) + count);
Ben Zhang7ed2ffb2016-04-22 17:59:40 -07001027 if (!tlv)
1028 return -ENOMEM;
Rohit kumarf4ee4532020-08-21 20:27:25 +05301029
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001030 tlv->numid = ctl->info.id.numid;
Mythri P K45b2d042014-08-18 15:39:36 +05301031 tlv->length = count;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001032 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_READ, tlv);
Mythri P K45b2d042014-08-18 15:39:36 +05301033
1034 source = tlv->tlv;
1035 memcpy(array, source, count);
1036
1037 free(tlv);
1038
1039 return ret;
1040 } else {
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001041 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
Mythri P K45b2d042014-08-18 15:39:36 +05301042 if (ret < 0)
1043 return ret;
1044 size = sizeof(ev.value.bytes.data[0]);
1045 source = ev.value.bytes.data;
1046 break;
1047 }
Simon Wilson38f87f32012-10-23 15:05:23 -07001048
dvdlied51bc42020-10-28 22:09:09 +08001049 case SNDRV_CTL_ELEM_TYPE_IEC958:
dvdli25096d32021-12-15 21:21:29 +08001050 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
1051 if (ret < 0)
1052 return ret;
dvdlied51bc42020-10-28 22:09:09 +08001053 size = sizeof(ev.value.iec958);
1054 source = &ev.value.iec958;
1055 break;
1056
Simon Wilson38f87f32012-10-23 15:05:23 -07001057 default:
1058 return -EINVAL;
1059 }
1060
1061 memcpy(array, source, size * count);
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +01001062
1063 return 0;
1064}
1065
Taylor Holberton4e557192016-11-23 11:48:06 -08001066/** Sets the value of a control, specified by the value index.
1067 * @param ctl An initialized control handle.
1068 * @param id The index of the value within the control.
1069 * @param value The value to set.
1070 * This must be in a range specified by @ref mixer_ctl_get_range_min
1071 * and @ref mixer_ctl_get_range_max.
1072 * @returns On success, zero is returned.
1073 * On failure, non-zero is returned.
1074 * @ingroup libtinyalsa-mixer
1075 */
Simon Wilson066c9f62011-06-05 18:23:05 -07001076int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
Simon Wilson79d39652011-05-25 13:44:23 -07001077{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001078 struct mixer_ctl_group *grp;
Simon Wilson79d39652011-05-25 13:44:23 -07001079 struct snd_ctl_elem_value ev;
Simon Wilson193b1c32011-06-07 23:42:38 -07001080 int ret;
Simon Wilsond2cb5032011-06-04 00:57:17 -07001081
dvdli75f4a602021-05-19 19:23:03 +08001082 if (!ctl || id >= ctl->info.count) {
Simon Wilson193b1c32011-06-07 23:42:38 -07001083 return -EINVAL;
dvdli75f4a602021-05-19 19:23:03 +08001084 }
Simon Wilson79d39652011-05-25 13:44:23 -07001085
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001086 grp = ctl->grp;
Simon Wilson79d39652011-05-25 13:44:23 -07001087 memset(&ev, 0, sizeof(ev));
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001088 ev.id.numid = ctl->info.id.numid;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001089 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
Simon Wilson193b1c32011-06-07 23:42:38 -07001090 if (ret < 0)
1091 return ret;
Simon Wilsona1bb1e02011-05-26 18:22:00 -07001092
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001093 switch (ctl->info.type) {
Simon Wilson79d39652011-05-25 13:44:23 -07001094 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
Simon Wilsond2cb5032011-06-04 00:57:17 -07001095 ev.value.integer.value[id] = !!value;
Simon Wilson79d39652011-05-25 13:44:23 -07001096 break;
1097
Simon Wilsona1bb1e02011-05-26 18:22:00 -07001098 case SNDRV_CTL_ELEM_TYPE_INTEGER:
Simon Wilsond2cb5032011-06-04 00:57:17 -07001099 ev.value.integer.value[id] = value;
Simon Wilson79d39652011-05-25 13:44:23 -07001100 break;
Simon Wilson79d39652011-05-25 13:44:23 -07001101
Simon Wilson066c9f62011-06-05 18:23:05 -07001102 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
1103 ev.value.enumerated.item[id] = value;
1104 break;
1105
David.Coutherutce2b6342013-06-11 16:22:57 +02001106 case SNDRV_CTL_ELEM_TYPE_BYTES:
1107 ev.value.bytes.data[id] = value;
1108 break;
1109
Simon Wilson79d39652011-05-25 13:44:23 -07001110 default:
Simon Wilson193b1c32011-06-07 23:42:38 -07001111 return -EINVAL;
Simon Wilson79d39652011-05-25 13:44:23 -07001112 }
Simon Wilsona1bb1e02011-05-26 18:22:00 -07001113
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001114 return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
Simon Wilson79d39652011-05-25 13:44:23 -07001115}
1116
Taylor Holberton4e557192016-11-23 11:48:06 -08001117/** Sets the contents of a control's value array.
1118 * @param ctl An initialized control handle.
1119 * @param array The array containing control values.
1120 * @param count The number of values in the array.
1121 * This must match the number of values in the control.
1122 * The number of values in a control may be accessed via @ref mixer_ctl_get_num_values
1123 * @returns On success, zero.
1124 * On failure, non-zero.
1125 * @ingroup libtinyalsa-mixer
1126 */
Simon Wilson38f87f32012-10-23 15:05:23 -07001127int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +01001128{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001129 struct mixer_ctl_group *grp;
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +01001130 struct snd_ctl_elem_value ev;
Simon Wilson38f87f32012-10-23 15:05:23 -07001131 size_t size;
1132 void *dest;
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +01001133
dvdli75f4a602021-05-19 19:23:03 +08001134 if (!ctl || !array || count == 0) {
Pankaj Bharadiya3f813e42017-01-09 13:42:14 +05301135 return -EINVAL;
dvdli75f4a602021-05-19 19:23:03 +08001136 }
Pankaj Bharadiya3f813e42017-01-09 13:42:14 +05301137
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001138 grp = ctl->grp;
Pankaj Bharadiya3f813e42017-01-09 13:42:14 +05301139
Rohit kumarf4ee4532020-08-21 20:27:25 +05301140 if (count > ctl->info.count)
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +01001141 return -EINVAL;
1142
1143 memset(&ev, 0, sizeof(ev));
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001144 ev.id.numid = ctl->info.id.numid;
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +01001145
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001146 switch (ctl->info.type) {
Simon Wilson38f87f32012-10-23 15:05:23 -07001147 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1148 case SNDRV_CTL_ELEM_TYPE_INTEGER:
1149 size = sizeof(ev.value.integer.value[0]);
1150 dest = ev.value.integer.value;
1151 break;
1152
1153 case SNDRV_CTL_ELEM_TYPE_BYTES:
Mythri P K45b2d042014-08-18 15:39:36 +05301154 /* check if this is new bytes TLV */
Pankaj Bharadiya9698d032017-01-09 12:23:14 +05301155 if (mixer_ctl_is_access_tlv_rw(ctl)) {
Mythri P K45b2d042014-08-18 15:39:36 +05301156 struct snd_ctl_tlv *tlv;
1157 int ret = 0;
Rohit kumarf4ee4532020-08-21 20:27:25 +05301158
Ben Zhang7ed2ffb2016-04-22 17:59:40 -07001159 if (count > SIZE_MAX - sizeof(*tlv))
1160 return -EINVAL;
Rohit kumarf4ee4532020-08-21 20:27:25 +05301161
Mythri P K45b2d042014-08-18 15:39:36 +05301162 tlv = calloc(1, sizeof(*tlv) + count);
Ben Zhang7ed2ffb2016-04-22 17:59:40 -07001163 if (!tlv)
1164 return -ENOMEM;
Rohit kumarf4ee4532020-08-21 20:27:25 +05301165
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001166 tlv->numid = ctl->info.id.numid;
Mythri P K45b2d042014-08-18 15:39:36 +05301167 tlv->length = count;
1168 memcpy(tlv->tlv, array, count);
1169
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001170 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
Mythri P K45b2d042014-08-18 15:39:36 +05301171 free(tlv);
1172
1173 return ret;
1174 } else {
1175 size = sizeof(ev.value.bytes.data[0]);
1176 dest = ev.value.bytes.data;
1177 }
Simon Wilson38f87f32012-10-23 15:05:23 -07001178 break;
1179
dvdlied51bc42020-10-28 22:09:09 +08001180 case SNDRV_CTL_ELEM_TYPE_IEC958:
1181 size = sizeof(ev.value.iec958);
1182 dest = &ev.value.iec958;
1183 break;
1184
Simon Wilson38f87f32012-10-23 15:05:23 -07001185 default:
1186 return -EINVAL;
1187 }
1188
1189 memcpy(dest, array, size * count);
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +01001190
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001191 return grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
Dimitris Papastamosf51c05b2012-05-28 13:40:33 +01001192}
1193
Taylor Holberton4e557192016-11-23 11:48:06 -08001194/** Gets the minimum value of an control.
1195 * The control must have an integer type.
1196 * The type of the control can be checked with @ref mixer_ctl_get_type.
1197 * @param ctl An initialized control handle.
1198 * @returns On success, the minimum value of the control.
1199 * On failure, -EINVAL.
1200 * @ingroup libtinyalsa-mixer
1201 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -08001202int mixer_ctl_get_range_min(const struct mixer_ctl *ctl)
Simon Wilsonb9d4f6b2011-06-06 14:41:02 -07001203{
dvdli75f4a602021-05-19 19:23:03 +08001204 if (!ctl || ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER) {
Simon Wilson193b1c32011-06-07 23:42:38 -07001205 return -EINVAL;
dvdli75f4a602021-05-19 19:23:03 +08001206 }
Simon Wilsonb9d4f6b2011-06-06 14:41:02 -07001207
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001208 return ctl->info.value.integer.min;
Simon Wilsonb9d4f6b2011-06-06 14:41:02 -07001209}
1210
Taylor Holberton4e557192016-11-23 11:48:06 -08001211/** Gets the maximum value of an control.
1212 * The control must have an integer type.
1213 * The type of the control can be checked with @ref mixer_ctl_get_type.
1214 * @param ctl An initialized control handle.
1215 * @returns On success, the maximum value of the control.
1216 * On failure, -EINVAL.
1217 * @ingroup libtinyalsa-mixer
1218 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -08001219int mixer_ctl_get_range_max(const struct mixer_ctl *ctl)
Simon Wilsonb9d4f6b2011-06-06 14:41:02 -07001220{
dvdli75f4a602021-05-19 19:23:03 +08001221 if (!ctl || ctl->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER) {
Simon Wilson193b1c32011-06-07 23:42:38 -07001222 return -EINVAL;
dvdli75f4a602021-05-19 19:23:03 +08001223 }
Simon Wilsonb9d4f6b2011-06-06 14:41:02 -07001224
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001225 return ctl->info.value.integer.max;
Simon Wilsonb9d4f6b2011-06-06 14:41:02 -07001226}
1227
Taylor Holberton4e557192016-11-23 11:48:06 -08001228/** Get the number of enumerated items in the control.
1229 * @param ctl An initialized control handle.
1230 * @returns The number of enumerated items in the control.
1231 * @ingroup libtinyalsa-mixer
1232 */
Taylor Holbertoncac43a22016-12-01 18:11:24 -08001233unsigned int mixer_ctl_get_num_enums(const struct mixer_ctl *ctl)
Simon Wilson066c9f62011-06-05 18:23:05 -07001234{
dvdli75f4a602021-05-19 19:23:03 +08001235 if (!ctl) {
Simon Wilson066c9f62011-06-05 18:23:05 -07001236 return 0;
dvdli75f4a602021-05-19 19:23:03 +08001237 }
Simon Wilson066c9f62011-06-05 18:23:05 -07001238
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001239 return ctl->info.value.enumerated.items;
Simon Wilson066c9f62011-06-05 18:23:05 -07001240}
1241
dvdli75f4a602021-05-19 19:23:03 +08001242static int mixer_ctl_fill_enum_string(struct mixer_ctl *ctl)
David.Coutherut9b423962013-06-03 13:45:51 +02001243{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001244 struct mixer_ctl_group *grp = ctl->grp;
David.Coutherut9b423962013-06-03 13:45:51 +02001245 struct snd_ctl_elem_info tmp;
1246 unsigned int m;
1247 char **enames;
1248
1249 if (ctl->ename) {
1250 return 0;
1251 }
1252
Taylor Holberton6a38d5f2016-10-03 21:07:39 -04001253 enames = calloc(ctl->info.value.enumerated.items, sizeof(char*));
David.Coutherut9b423962013-06-03 13:45:51 +02001254 if (!enames)
1255 goto fail;
Taylor Holberton6a38d5f2016-10-03 21:07:39 -04001256 for (m = 0; m < ctl->info.value.enumerated.items; m++) {
David.Coutherut9b423962013-06-03 13:45:51 +02001257 memset(&tmp, 0, sizeof(tmp));
Taylor Holberton6a38d5f2016-10-03 21:07:39 -04001258 tmp.id.numid = ctl->info.id.numid;
David.Coutherut9b423962013-06-03 13:45:51 +02001259 tmp.value.enumerated.item = m;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001260 if (grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
David.Coutherut9b423962013-06-03 13:45:51 +02001261 goto fail;
1262 enames[m] = strdup(tmp.value.enumerated.name);
1263 if (!enames[m])
1264 goto fail;
1265 }
1266 ctl->ename = enames;
1267 return 0;
1268
1269fail:
1270 if (enames) {
Taylor Holberton6a38d5f2016-10-03 21:07:39 -04001271 for (m = 0; m < ctl->info.value.enumerated.items; m++) {
David.Coutherut9b423962013-06-03 13:45:51 +02001272 if (enames[m]) {
1273 free(enames[m]);
1274 }
1275 }
1276 free(enames);
1277 }
1278 return -1;
1279}
1280
Taylor Holberton4e557192016-11-23 11:48:06 -08001281/** Gets the string representation of an enumerated item.
1282 * @param ctl An initialized control handle.
1283 * @param enum_id The index of the enumerated value.
1284 * @returns A string representation of the enumerated item.
1285 * @ingroup libtinyalsa-mixer
1286 */
Simon Wilsonb29ac1a2012-03-08 10:15:08 -08001287const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
1288 unsigned int enum_id)
Simon Wilson79d39652011-05-25 13:44:23 -07001289{
dvdli75f4a602021-05-19 19:23:03 +08001290 if (!ctl || ctl->info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED ||
1291 enum_id >= ctl->info.value.enumerated.items) {
Simon Wilsonb29ac1a2012-03-08 10:15:08 -08001292 return NULL;
dvdli75f4a602021-05-19 19:23:03 +08001293 }
1294
1295 if (mixer_ctl_fill_enum_string(ctl) < 0) {
1296 return NULL;
1297 }
Simon Wilson79d39652011-05-25 13:44:23 -07001298
Simon Wilsonb29ac1a2012-03-08 10:15:08 -08001299 return (const char *)ctl->ename[enum_id];
Simon Wilson79d39652011-05-25 13:44:23 -07001300}
1301
Taylor Holberton4e557192016-11-23 11:48:06 -08001302/** Set an enumeration value by string value.
1303 * @param ctl An enumerated mixer control.
1304 * @param string The string representation of an enumeration.
1305 * @returns On success, zero.
1306 * On failure, zero.
1307 * @ingroup libtinyalsa-mixer
1308 */
Simon Wilsonf0a20ee2011-06-05 21:18:52 -07001309int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
1310{
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001311 struct mixer_ctl_group *grp;
Simon Wilsonf0a20ee2011-06-05 21:18:52 -07001312 unsigned int i, num_enums;
1313 struct snd_ctl_elem_value ev;
Simon Wilson193b1c32011-06-07 23:42:38 -07001314 int ret;
Simon Wilsonf0a20ee2011-06-05 21:18:52 -07001315
dvdli75f4a602021-05-19 19:23:03 +08001316 if (!ctl || !string || ctl->info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
Simon Wilson193b1c32011-06-07 23:42:38 -07001317 return -EINVAL;
dvdli75f4a602021-05-19 19:23:03 +08001318 }
1319
1320 if (mixer_ctl_fill_enum_string(ctl) < 0) {
1321 return -EINVAL;
1322 }
Simon Wilsonf0a20ee2011-06-05 21:18:52 -07001323
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001324 grp = ctl->grp;
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001325 num_enums = ctl->info.value.enumerated.items;
Simon Wilsonf0a20ee2011-06-05 21:18:52 -07001326 for (i = 0; i < num_enums; i++) {
1327 if (!strcmp(string, ctl->ename[i])) {
1328 memset(&ev, 0, sizeof(ev));
1329 ev.value.enumerated.item[0] = i;
Richard Fitzgerald899cece2014-09-09 17:03:21 +01001330 ev.id.numid = ctl->info.id.numid;
Bhalchandra Gajaree7c627d2019-06-19 15:30:42 -07001331 ret = grp->ops->ioctl(grp->data, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
Simon Wilson193b1c32011-06-07 23:42:38 -07001332 if (ret < 0)
1333 return ret;
Simon Wilsonf0a20ee2011-06-05 21:18:52 -07001334 return 0;
1335 }
1336 }
1337
Simon Wilson193b1c32011-06-07 23:42:38 -07001338 return -EINVAL;
Simon Wilsonf0a20ee2011-06-05 21:18:52 -07001339}