blob: 6ea6c0de8dda66e995c5018ee0fa0b20ae3bd826 [file] [log] [blame]
Simon Wilsonedff7082011-06-06 15:33:34 -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 Zhang6cb14f22016-04-22 17:59:40 -070031#include <stdint.h>
Simon Wilsonedff7082011-06-06 15:33:34 -070032#include <string.h>
33#include <unistd.h>
34#include <fcntl.h>
35#include <errno.h>
36#include <ctype.h>
Pankaj Bharadiya415c0c32017-01-11 08:57:06 +053037#include <poll.h>
Simon Wilsonedff7082011-06-06 15:33:34 -070038
Simon Wilson85dc38f2012-05-15 17:37:19 -070039#include <sys/ioctl.h>
40
Simon Wilsonedff7082011-06-06 15:33:34 -070041#include <linux/ioctl.h>
42#define __force
43#define __bitwise
44#define __user
45#include <sound/asound.h>
46
47#include <tinyalsa/asoundlib.h>
48
49struct mixer_ctl {
50 struct mixer *mixer;
51 struct snd_ctl_elem_info *info;
52 char **ename;
53};
54
55struct mixer {
56 int fd;
Simon Wilson7e8a6562013-06-28 16:47:16 -070057 struct snd_ctl_card_info card_info;
58 struct snd_ctl_elem_info *elem_info;
Simon Wilsonedff7082011-06-06 15:33:34 -070059 struct mixer_ctl *ctl;
60 unsigned int count;
61};
62
63void mixer_close(struct mixer *mixer)
64{
65 unsigned int n,m;
66
67 if (!mixer)
68 return;
69
70 if (mixer->fd >= 0)
71 close(mixer->fd);
72
73 if (mixer->ctl) {
74 for (n = 0; n < mixer->count; n++) {
75 if (mixer->ctl[n].ename) {
76 unsigned int max = mixer->ctl[n].info->value.enumerated.items;
77 for (m = 0; m < max; m++)
78 free(mixer->ctl[n].ename[m]);
79 free(mixer->ctl[n].ename);
80 }
81 }
82 free(mixer->ctl);
83 }
84
Simon Wilson7e8a6562013-06-28 16:47:16 -070085 if (mixer->elem_info)
86 free(mixer->elem_info);
Simon Wilsonedff7082011-06-06 15:33:34 -070087
88 free(mixer);
89
90 /* TODO: verify frees */
91}
92
93struct mixer *mixer_open(unsigned int card)
94{
95 struct snd_ctl_elem_list elist;
96 struct snd_ctl_elem_info tmp;
97 struct snd_ctl_elem_id *eid = NULL;
98 struct mixer *mixer = NULL;
99 unsigned int n, m;
100 int fd;
101 char fn[256];
102
103 snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
104 fd = open(fn, O_RDWR);
105 if (fd < 0)
106 return 0;
107
108 memset(&elist, 0, sizeof(elist));
109 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
110 goto fail;
111
112 mixer = calloc(1, sizeof(*mixer));
113 if (!mixer)
114 goto fail;
115
116 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
Simon Wilson7e8a6562013-06-28 16:47:16 -0700117 mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
118 if (!mixer->ctl || !mixer->elem_info)
119 goto fail;
120
121 if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
Simon Wilsonedff7082011-06-06 15:33:34 -0700122 goto fail;
123
124 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
125 if (!eid)
126 goto fail;
127
128 mixer->count = elist.count;
129 mixer->fd = fd;
130 elist.space = mixer->count;
131 elist.pids = eid;
132 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
133 goto fail;
134
135 for (n = 0; n < mixer->count; n++) {
Simon Wilson7e8a6562013-06-28 16:47:16 -0700136 struct snd_ctl_elem_info *ei = mixer->elem_info + n;
Simon Wilsonedff7082011-06-06 15:33:34 -0700137 ei->id.numid = eid[n].numid;
138 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
139 goto fail;
140 mixer->ctl[n].info = ei;
141 mixer->ctl[n].mixer = mixer;
142 if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
143 char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
144 if (!enames)
145 goto fail;
146 mixer->ctl[n].ename = enames;
147 for (m = 0; m < ei->value.enumerated.items; m++) {
148 memset(&tmp, 0, sizeof(tmp));
149 tmp.id.numid = ei->id.numid;
150 tmp.value.enumerated.item = m;
151 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
152 goto fail;
153 enames[m] = strdup(tmp.value.enumerated.name);
154 if (!enames[m])
155 goto fail;
156 }
157 }
158 }
159
160 free(eid);
161 return mixer;
162
163fail:
164 /* TODO: verify frees in failure case */
165 if (eid)
166 free(eid);
167 if (mixer)
168 mixer_close(mixer);
169 else if (fd >= 0)
170 close(fd);
171 return 0;
172}
173
Simon Wilson7e8a6562013-06-28 16:47:16 -0700174const char *mixer_get_name(struct mixer *mixer)
175{
176 return (const char *)mixer->card_info.name;
177}
178
Simon Wilsonedff7082011-06-06 15:33:34 -0700179unsigned int mixer_get_num_ctls(struct mixer *mixer)
180{
181 if (!mixer)
182 return 0;
183
184 return mixer->count;
185}
186
187struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
188{
189 if (mixer && (id < mixer->count))
190 return mixer->ctl + id;
191
192 return NULL;
193}
194
195struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
196{
197 unsigned int n;
198
199 if (!mixer)
200 return NULL;
201
202 for (n = 0; n < mixer->count; n++)
Simon Wilson7e8a6562013-06-28 16:47:16 -0700203 if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
Simon Wilsonedff7082011-06-06 15:33:34 -0700204 return mixer->ctl + n;
205
206 return NULL;
207}
208
Simon Wilson7e8a6562013-06-28 16:47:16 -0700209void mixer_ctl_update(struct mixer_ctl *ctl)
210{
211 ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
212}
213
Simon Wilsone44e30a2012-03-08 10:45:31 -0800214const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
Simon Wilsonedff7082011-06-06 15:33:34 -0700215{
Simon Wilsone44e30a2012-03-08 10:45:31 -0800216 if (!ctl)
217 return NULL;
Simon Wilsonedff7082011-06-06 15:33:34 -0700218
Simon Wilsone44e30a2012-03-08 10:45:31 -0800219 return (const char *)ctl->info->id.name;
Simon Wilsonedff7082011-06-06 15:33:34 -0700220}
221
222enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
223{
224 if (!ctl)
225 return MIXER_CTL_TYPE_UNKNOWN;
226
227 switch (ctl->info->type) {
228 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
229 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
230 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
231 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
232 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
233 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
234 default: return MIXER_CTL_TYPE_UNKNOWN;
235 };
236}
237
238const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
239{
240 if (!ctl)
241 return "";
242
243 switch (ctl->info->type) {
244 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
245 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
246 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
247 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
248 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
249 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
250 default: return "Unknown";
251 };
252}
253
254unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
255{
256 if (!ctl)
257 return 0;
258
259 return ctl->info->count;
260}
261
262static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
263{
264 int range;
265
266 if (percent > 100)
267 percent = 100;
268 else if (percent < 0)
269 percent = 0;
270
271 range = (ei->value.integer.max - ei->value.integer.min);
272
273 return ei->value.integer.min + (range * percent) / 100;
274}
275
276static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
277{
278 int range = (ei->value.integer.max - ei->value.integer.min);
279
280 if (range == 0)
281 return 0;
282
283 return ((value - ei->value.integer.min) / range) * 100;
284}
285
286int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
287{
288 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
289 return -EINVAL;
290
291 return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
292}
293
294int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
295{
296 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
297 return -EINVAL;
298
299 return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
300}
301
302int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
303{
304 struct snd_ctl_elem_value ev;
305 int ret;
306
307 if (!ctl || (id >= ctl->info->count))
308 return -EINVAL;
309
310 memset(&ev, 0, sizeof(ev));
311 ev.id.numid = ctl->info->id.numid;
312 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
313 if (ret < 0)
314 return ret;
315
316 switch (ctl->info->type) {
317 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
318 return !!ev.value.integer.value[id];
319
320 case SNDRV_CTL_ELEM_TYPE_INTEGER:
321 return ev.value.integer.value[id];
322
323 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
324 return ev.value.enumerated.item[id];
325
Simon Wilson5aed71d2011-11-16 14:45:38 -0800326 case SNDRV_CTL_ELEM_TYPE_BYTES:
327 return ev.value.bytes.data[id];
328
Simon Wilsonedff7082011-06-06 15:33:34 -0700329 default:
330 return -EINVAL;
331 }
332
333 return 0;
334}
335
Pankaj Bharadiya10d4b8f2017-01-04 11:10:58 +0530336int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl)
337{
338 return (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
339}
340
Simon Wilson8813fe82013-06-24 15:40:34 -0700341int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800342{
343 struct snd_ctl_elem_value ev;
Mythri P K67b1df42014-08-18 15:39:36 +0530344 int ret = 0;
Simon Wilson8813fe82013-06-24 15:40:34 -0700345 size_t size;
346 void *source;
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530347 size_t total_count;
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800348
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530349 if ((!ctl) || !count || !array)
350 return -EINVAL;
351
352 total_count = ctl->info->count;
353
354 if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
Pankaj Bharadiya10d4b8f2017-01-04 11:10:58 +0530355 mixer_ctl_is_access_tlv_rw(ctl)) {
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530356 /* Additional two words is for the TLV header */
357 total_count += TLV_HEADER_SIZE;
358 }
359
360 if (count > total_count)
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800361 return -EINVAL;
362
363 memset(&ev, 0, sizeof(ev));
364 ev.id.numid = ctl->info->id.numid;
365
Simon Wilson8813fe82013-06-24 15:40:34 -0700366 switch (ctl->info->type) {
367 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
368 case SNDRV_CTL_ELEM_TYPE_INTEGER:
Mythri P K67b1df42014-08-18 15:39:36 +0530369 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
370 if (ret < 0)
371 return ret;
Simon Wilson8813fe82013-06-24 15:40:34 -0700372 size = sizeof(ev.value.integer.value[0]);
373 source = ev.value.integer.value;
374 break;
375
376 case SNDRV_CTL_ELEM_TYPE_BYTES:
Mythri P K67b1df42014-08-18 15:39:36 +0530377 /* check if this is new bytes TLV */
Pankaj Bharadiya10d4b8f2017-01-04 11:10:58 +0530378 if (mixer_ctl_is_access_tlv_rw(ctl)) {
Mythri P K67b1df42014-08-18 15:39:36 +0530379 struct snd_ctl_tlv *tlv;
380 int ret;
381
Ben Zhang6cb14f22016-04-22 17:59:40 -0700382 if (count > SIZE_MAX - sizeof(*tlv))
383 return -EINVAL;
Mythri P K67b1df42014-08-18 15:39:36 +0530384 tlv = calloc(1, sizeof(*tlv) + count);
Ben Zhang6cb14f22016-04-22 17:59:40 -0700385 if (!tlv)
386 return -ENOMEM;
Mythri P K67b1df42014-08-18 15:39:36 +0530387 tlv->numid = ctl->info->id.numid;
388 tlv->length = count;
389 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv);
390
391 source = tlv->tlv;
392 memcpy(array, source, count);
393
394 free(tlv);
395
396 return ret;
397 } else {
398 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
399 if (ret < 0)
400 return ret;
401 size = sizeof(ev.value.bytes.data[0]);
402 source = ev.value.bytes.data;
403 break;
404 }
Simon Wilson8813fe82013-06-24 15:40:34 -0700405
Yogesh Agrawal49a61372013-08-02 20:04:58 +0530406 case SNDRV_CTL_ELEM_TYPE_IEC958:
407 size = sizeof(ev.value.iec958);
408 source = &ev.value.iec958;
409 break;
410
Simon Wilson8813fe82013-06-24 15:40:34 -0700411 default:
412 return -EINVAL;
413 }
414
415 memcpy(array, source, size * count);
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800416
417 return 0;
418}
419
Simon Wilsonedff7082011-06-06 15:33:34 -0700420int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
421{
422 struct snd_ctl_elem_value ev;
423 int ret;
424
425 if (!ctl || (id >= ctl->info->count))
426 return -EINVAL;
427
428 memset(&ev, 0, sizeof(ev));
429 ev.id.numid = ctl->info->id.numid;
430 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
431 if (ret < 0)
432 return ret;
433
434 switch (ctl->info->type) {
435 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
436 ev.value.integer.value[id] = !!value;
437 break;
438
439 case SNDRV_CTL_ELEM_TYPE_INTEGER:
440 ev.value.integer.value[id] = value;
441 break;
442
443 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
444 ev.value.enumerated.item[id] = value;
445 break;
446
David.Coutheruta8481aa2013-06-11 16:22:57 +0200447 case SNDRV_CTL_ELEM_TYPE_BYTES:
448 ev.value.bytes.data[id] = value;
449 break;
450
Simon Wilsonedff7082011-06-06 15:33:34 -0700451 default:
452 return -EINVAL;
453 }
454
455 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
456}
457
Simon Wilson8813fe82013-06-24 15:40:34 -0700458int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800459{
460 struct snd_ctl_elem_value ev;
Simon Wilson8813fe82013-06-24 15:40:34 -0700461 size_t size;
462 void *dest;
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530463 size_t total_count;
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800464
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530465 if ((!ctl) || !count || !array)
466 return -EINVAL;
467
468 total_count = ctl->info->count;
469
470 if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
Pankaj Bharadiya10d4b8f2017-01-04 11:10:58 +0530471 mixer_ctl_is_access_tlv_rw(ctl)) {
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530472 /* Additional two words is for the TLV header */
473 total_count += TLV_HEADER_SIZE;
474 }
475
476 if (count > total_count)
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800477 return -EINVAL;
478
479 memset(&ev, 0, sizeof(ev));
480 ev.id.numid = ctl->info->id.numid;
481
Simon Wilson8813fe82013-06-24 15:40:34 -0700482 switch (ctl->info->type) {
483 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
484 case SNDRV_CTL_ELEM_TYPE_INTEGER:
485 size = sizeof(ev.value.integer.value[0]);
486 dest = ev.value.integer.value;
487 break;
488
489 case SNDRV_CTL_ELEM_TYPE_BYTES:
Mythri P K67b1df42014-08-18 15:39:36 +0530490 /* check if this is new bytes TLV */
Pankaj Bharadiya10d4b8f2017-01-04 11:10:58 +0530491 if (mixer_ctl_is_access_tlv_rw(ctl)) {
Mythri P K67b1df42014-08-18 15:39:36 +0530492 struct snd_ctl_tlv *tlv;
493 int ret = 0;
Ben Zhang6cb14f22016-04-22 17:59:40 -0700494 if (count > SIZE_MAX - sizeof(*tlv))
495 return -EINVAL;
Mythri P K67b1df42014-08-18 15:39:36 +0530496 tlv = calloc(1, sizeof(*tlv) + count);
Ben Zhang6cb14f22016-04-22 17:59:40 -0700497 if (!tlv)
498 return -ENOMEM;
Mythri P K67b1df42014-08-18 15:39:36 +0530499 tlv->numid = ctl->info->id.numid;
500 tlv->length = count;
501 memcpy(tlv->tlv, array, count);
502
503 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
504 free(tlv);
505
506 return ret;
507 } else {
508 size = sizeof(ev.value.bytes.data[0]);
509 dest = ev.value.bytes.data;
510 }
Simon Wilson8813fe82013-06-24 15:40:34 -0700511 break;
512
Yogesh Agrawal49a61372013-08-02 20:04:58 +0530513 case SNDRV_CTL_ELEM_TYPE_IEC958:
514 size = sizeof(ev.value.iec958);
515 dest = &ev.value.iec958;
516 break;
517
Simon Wilson8813fe82013-06-24 15:40:34 -0700518 default:
519 return -EINVAL;
520 }
521
522 memcpy(dest, array, size * count);
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800523
524 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
525}
526
Simon Wilsonedff7082011-06-06 15:33:34 -0700527int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
528{
Simon Wilsonedff7082011-06-06 15:33:34 -0700529 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
530 return -EINVAL;
531
Simon Wilsonedff7082011-06-06 15:33:34 -0700532 return ctl->info->value.integer.min;
533}
534
535int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
536{
Simon Wilsonedff7082011-06-06 15:33:34 -0700537 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
538 return -EINVAL;
539
Simon Wilsonedff7082011-06-06 15:33:34 -0700540 return ctl->info->value.integer.max;
541}
542
543unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
544{
545 if (!ctl)
546 return 0;
547
548 return ctl->info->value.enumerated.items;
549}
550
Simon Wilsone44e30a2012-03-08 10:45:31 -0800551const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
552 unsigned int enum_id)
Simon Wilsonedff7082011-06-06 15:33:34 -0700553{
Simon Wilsonedff7082011-06-06 15:33:34 -0700554 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
555 (enum_id >= ctl->info->value.enumerated.items))
Simon Wilsone44e30a2012-03-08 10:45:31 -0800556 return NULL;
Simon Wilsonedff7082011-06-06 15:33:34 -0700557
Simon Wilsone44e30a2012-03-08 10:45:31 -0800558 return (const char *)ctl->ename[enum_id];
Simon Wilsonedff7082011-06-06 15:33:34 -0700559}
560
561int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
562{
563 unsigned int i, num_enums;
564 struct snd_ctl_elem_value ev;
565 int ret;
566
567 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
568 return -EINVAL;
569
570 num_enums = ctl->info->value.enumerated.items;
571 for (i = 0; i < num_enums; i++) {
572 if (!strcmp(string, ctl->ename[i])) {
573 memset(&ev, 0, sizeof(ev));
574 ev.value.enumerated.item[0] = i;
575 ev.id.numid = ctl->info->id.numid;
576 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
577 if (ret < 0)
578 return ret;
579 return 0;
580 }
581 }
582
583 return -EINVAL;
584}
585
Pankaj Bharadiya415c0c32017-01-11 08:57:06 +0530586/** Subscribes for the mixer events.
587 * @param mixer A mixer handle.
588 * @param subscribe value indicating subscribe or unsubscribe for events
589 * @returns On success, zero.
590 * On failure, non-zero.
591 * @ingroup libtinyalsa-mixer
592 */
593int mixer_subscribe_events(struct mixer *mixer, int subscribe)
594{
595 if (ioctl(mixer->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
596 return -1;
597 }
598 return 0;
599}
600
601/** Wait for mixer events.
602 * @param mixer A mixer handle.
603 * @param timeout timout value
604 * @returns On success, 1.
605 * On failure, -errno.
606 * On timeout, 0
607 * @ingroup libtinyalsa-mixer
608 */
609int mixer_wait_event(struct mixer *mixer, int timeout)
610{
611 struct pollfd pfd;
612
613 pfd.fd = mixer->fd;
614 pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
615
616 for (;;) {
617 int err;
618 err = poll(&pfd, 1, timeout);
619 if (err < 0)
620 return -errno;
621 if (!err)
622 return 0;
623 if (pfd.revents & (POLLERR | POLLNVAL))
624 return -EIO;
625 if (pfd.revents & (POLLIN | POLLOUT))
626 return 1;
627 }
628}