blob: f3fdb62e589c6f4482ba675b3dbcdb58adc1efa3 [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
John Muir22bca932017-10-23 10:58:12 -070029#include <stdbool.h>
Simon Wilsonedff7082011-06-06 15:33:34 -070030#include <stdio.h>
31#include <stdlib.h>
Ben Zhang6cb14f22016-04-22 17:59:40 -070032#include <stdint.h>
Simon Wilsonedff7082011-06-06 15:33:34 -070033#include <string.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <errno.h>
37#include <ctype.h>
Pankaj Bharadiya415c0c32017-01-11 08:57:06 +053038#include <poll.h>
Simon Wilsonedff7082011-06-06 15:33:34 -070039
Simon Wilson85dc38f2012-05-15 17:37:19 -070040#include <sys/ioctl.h>
41
Simon Wilsonedff7082011-06-06 15:33:34 -070042#include <linux/ioctl.h>
43#define __force
44#define __bitwise
45#define __user
46#include <sound/asound.h>
47
John Muir22bca932017-10-23 10:58:12 -070048#ifndef SNDRV_CTL_ELEM_ID_NAME_MAXLEN
49#define SNDRV_CTL_ELEM_ID_NAME_MAXLEN 44
50#endif
51
Simon Wilsonedff7082011-06-06 15:33:34 -070052#include <tinyalsa/asoundlib.h>
53
54struct mixer_ctl {
55 struct mixer *mixer;
56 struct snd_ctl_elem_info *info;
57 char **ename;
John Muir22bca932017-10-23 10:58:12 -070058 bool info_retrieved;
Simon Wilsonedff7082011-06-06 15:33:34 -070059};
60
Bhalchandra Gajare4268f1b2019-08-21 15:09:43 -070061struct mixer {
62 int fd;
63 struct snd_ctl_card_info card_info;
Ashish Jaine8257c52020-03-31 09:53:16 +053064 struct snd_ctl_elem_info *elem_info;
65 struct mixer_ctl *ctl;
66 unsigned int count;
Bhalchandra Gajare4268f1b2019-08-21 15:09:43 -070067};
68
Simon Wilsonedff7082011-06-06 15:33:34 -070069void mixer_close(struct mixer *mixer)
70{
Ashish Jaine8257c52020-03-31 09:53:16 +053071 unsigned int n,m;
72
Simon Wilsonedff7082011-06-06 15:33:34 -070073 if (!mixer)
74 return;
75
Ashish Jaine8257c52020-03-31 09:53:16 +053076 if (mixer->fd >= 0)
77 close(mixer->fd);
Simon Wilsonedff7082011-06-06 15:33:34 -070078
Ashish Jaine8257c52020-03-31 09:53:16 +053079 if (mixer->ctl) {
80 for (n = 0; n < mixer->count; n++) {
81 if (mixer->ctl[n].ename) {
82 unsigned int max = mixer->ctl[n].info->value.enumerated.items;
83 for (m = 0; m < max; m++)
84 free(mixer->ctl[n].ename[m]);
85 free(mixer->ctl[n].ename);
86 }
87 }
88 free(mixer->ctl);
89 }
90
91 if (mixer->elem_info)
92 free(mixer->elem_info);
Simon Wilsonedff7082011-06-06 15:33:34 -070093
94 free(mixer);
95
96 /* TODO: verify frees */
97}
98
Ashish Jaine8257c52020-03-31 09:53:16 +053099struct mixer *mixer_open(unsigned int card)
Simon Wilsonedff7082011-06-06 15:33:34 -0700100{
101 struct snd_ctl_elem_list elist;
Simon Wilsonedff7082011-06-06 15:33:34 -0700102 struct snd_ctl_elem_id *eid = NULL;
Ashish Jaine8257c52020-03-31 09:53:16 +0530103 struct mixer *mixer = NULL;
John Muir22bca932017-10-23 10:58:12 -0700104 unsigned int n;
Ashish Jaine8257c52020-03-31 09:53:16 +0530105 int fd;
106 char fn[256];
Simon Wilsonedff7082011-06-06 15:33:34 -0700107
Ashish Jaine8257c52020-03-31 09:53:16 +0530108 snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
109 fd = open(fn, O_RDWR);
110 if (fd < 0)
111 return 0;
Simon Wilsonedff7082011-06-06 15:33:34 -0700112
113 memset(&elist, 0, sizeof(elist));
Ashish Jaine8257c52020-03-31 09:53:16 +0530114 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
115 goto fail;
Simon Wilsonedff7082011-06-06 15:33:34 -0700116
Ashish Jaine8257c52020-03-31 09:53:16 +0530117 mixer = calloc(1, sizeof(*mixer));
118 if (!mixer)
119 goto fail;
Simon Wilsonedff7082011-06-06 15:33:34 -0700120
Ashish Jaine8257c52020-03-31 09:53:16 +0530121 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
122 mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
123 if (!mixer->ctl || !mixer->elem_info)
124 goto fail;
Simon Wilson7e8a6562013-06-28 16:47:16 -0700125
Ashish Jaine8257c52020-03-31 09:53:16 +0530126 if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
127 goto fail;
128
129 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
130 if (!eid)
131 goto fail;
132
133 mixer->count = elist.count;
134 mixer->fd = fd;
135 elist.space = mixer->count;
Simon Wilsonedff7082011-06-06 15:33:34 -0700136 elist.pids = eid;
Ashish Jaine8257c52020-03-31 09:53:16 +0530137 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
138 goto fail;
Simon Wilsonedff7082011-06-06 15:33:34 -0700139
Ashish Jaine8257c52020-03-31 09:53:16 +0530140 for (n = 0; n < mixer->count; n++) {
141 struct mixer_ctl *ctl = mixer->ctl + n;
John Muir22bca932017-10-23 10:58:12 -0700142
143 ctl->mixer = mixer;
Ashish Jaine8257c52020-03-31 09:53:16 +0530144 ctl->info = mixer->elem_info + n;
John Muir22bca932017-10-23 10:58:12 -0700145 ctl->info->id.numid = eid[n].numid;
146 strncpy((char *)ctl->info->id.name, (char *)eid[n].name,
147 SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
148 ctl->info->id.name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1] = 0;
Simon Wilsonedff7082011-06-06 15:33:34 -0700149 }
150
151 free(eid);
152 return mixer;
153
154fail:
Ashish Jaine8257c52020-03-31 09:53:16 +0530155 /* TODO: verify frees in failure case */
156 if (eid)
157 free(eid);
Simon Wilsonedff7082011-06-06 15:33:34 -0700158 if (mixer)
159 mixer_close(mixer);
Ashish Jaine8257c52020-03-31 09:53:16 +0530160 else if (fd >= 0)
161 close(fd);
Simon Wilsonedff7082011-06-06 15:33:34 -0700162 return 0;
163}
164
John Muir22bca932017-10-23 10:58:12 -0700165static bool mixer_ctl_get_elem_info(struct mixer_ctl* ctl)
166{
167 if (!ctl->info_retrieved) {
Ashish Jaine8257c52020-03-31 09:53:16 +0530168 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info) < 0)
John Muir22bca932017-10-23 10:58:12 -0700169 return false;
170 ctl->info_retrieved = true;
171 }
172
173 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED || ctl->ename)
174 return true;
175
176 struct snd_ctl_elem_info tmp;
177 char** enames = calloc(ctl->info->value.enumerated.items, sizeof(char*));
178 if (!enames)
179 return false;
180
Ashish Jaine8257c52020-03-31 09:53:16 +0530181 for (unsigned int i = 0; i < ctl->info->value.enumerated.items; i++) {
John Muir22bca932017-10-23 10:58:12 -0700182 memset(&tmp, 0, sizeof(tmp));
183 tmp.id.numid = ctl->info->id.numid;
184 tmp.value.enumerated.item = i;
Ashish Jaine8257c52020-03-31 09:53:16 +0530185 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
John Muir22bca932017-10-23 10:58:12 -0700186 goto fail;
187 enames[i] = strdup(tmp.value.enumerated.name);
188 if (!enames[i])
189 goto fail;
190 }
191 ctl->ename = enames;
192 return true;
193
194fail:
195 free(enames);
196 return false;
197}
198
Simon Wilson7e8a6562013-06-28 16:47:16 -0700199const char *mixer_get_name(struct mixer *mixer)
200{
201 return (const char *)mixer->card_info.name;
202}
203
Simon Wilsonedff7082011-06-06 15:33:34 -0700204unsigned int mixer_get_num_ctls(struct mixer *mixer)
205{
206 if (!mixer)
207 return 0;
208
Ashish Jaine8257c52020-03-31 09:53:16 +0530209 return mixer->count;
Simon Wilsonedff7082011-06-06 15:33:34 -0700210}
211
212struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
213{
John Muir22bca932017-10-23 10:58:12 -0700214 struct mixer_ctl *ctl;
Simon Wilsonedff7082011-06-06 15:33:34 -0700215
Ashish Jaine8257c52020-03-31 09:53:16 +0530216 if (!mixer || (id >= mixer->count))
John Muir22bca932017-10-23 10:58:12 -0700217 return NULL;
218
Ashish Jaine8257c52020-03-31 09:53:16 +0530219 ctl = mixer->ctl + id;
John Muir22bca932017-10-23 10:58:12 -0700220 if (!mixer_ctl_get_elem_info(ctl))
221 return NULL;
222
223 return ctl;
Simon Wilsonedff7082011-06-06 15:33:34 -0700224}
225
226struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
227{
228 unsigned int n;
229
230 if (!mixer)
231 return NULL;
232
Ashish Jaine8257c52020-03-31 09:53:16 +0530233 for (n = 0; n < mixer->count; n++)
234 if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
Andrew Chantb5701062018-02-05 15:16:41 -0800235 return mixer_get_ctl(mixer, n);
Simon Wilsonedff7082011-06-06 15:33:34 -0700236
Andrew Chantb5701062018-02-05 15:16:41 -0800237 return NULL;
Simon Wilsonedff7082011-06-06 15:33:34 -0700238}
239
Simon Wilson7e8a6562013-06-28 16:47:16 -0700240void mixer_ctl_update(struct mixer_ctl *ctl)
241{
Ashish Jaine8257c52020-03-31 09:53:16 +0530242 ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
Simon Wilson7e8a6562013-06-28 16:47:16 -0700243}
244
Simon Wilsone44e30a2012-03-08 10:45:31 -0800245const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
Simon Wilsonedff7082011-06-06 15:33:34 -0700246{
Simon Wilsone44e30a2012-03-08 10:45:31 -0800247 if (!ctl)
248 return NULL;
Simon Wilsonedff7082011-06-06 15:33:34 -0700249
Simon Wilsone44e30a2012-03-08 10:45:31 -0800250 return (const char *)ctl->info->id.name;
Simon Wilsonedff7082011-06-06 15:33:34 -0700251}
252
253enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
254{
255 if (!ctl)
256 return MIXER_CTL_TYPE_UNKNOWN;
257
258 switch (ctl->info->type) {
259 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
260 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
261 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
262 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
263 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
264 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
265 default: return MIXER_CTL_TYPE_UNKNOWN;
266 };
267}
268
269const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
270{
271 if (!ctl)
272 return "";
273
274 switch (ctl->info->type) {
275 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
276 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
277 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
278 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
279 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
280 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
281 default: return "Unknown";
282 };
283}
284
285unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
286{
287 if (!ctl)
288 return 0;
289
290 return ctl->info->count;
291}
292
293static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
294{
295 int range;
296
297 if (percent > 100)
298 percent = 100;
299 else if (percent < 0)
300 percent = 0;
301
302 range = (ei->value.integer.max - ei->value.integer.min);
303
304 return ei->value.integer.min + (range * percent) / 100;
305}
306
307static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
308{
309 int range = (ei->value.integer.max - ei->value.integer.min);
310
311 if (range == 0)
312 return 0;
313
314 return ((value - ei->value.integer.min) / range) * 100;
315}
316
317int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
318{
319 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
320 return -EINVAL;
321
322 return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
323}
324
325int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
326{
327 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
328 return -EINVAL;
329
330 return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
331}
332
333int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
334{
335 struct snd_ctl_elem_value ev;
336 int ret;
337
338 if (!ctl || (id >= ctl->info->count))
339 return -EINVAL;
340
341 memset(&ev, 0, sizeof(ev));
342 ev.id.numid = ctl->info->id.numid;
Ashish Jaine8257c52020-03-31 09:53:16 +0530343 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
Simon Wilsonedff7082011-06-06 15:33:34 -0700344 if (ret < 0)
345 return ret;
346
347 switch (ctl->info->type) {
348 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
349 return !!ev.value.integer.value[id];
350
351 case SNDRV_CTL_ELEM_TYPE_INTEGER:
352 return ev.value.integer.value[id];
353
354 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
355 return ev.value.enumerated.item[id];
356
Simon Wilson5aed71d2011-11-16 14:45:38 -0800357 case SNDRV_CTL_ELEM_TYPE_BYTES:
358 return ev.value.bytes.data[id];
359
Simon Wilsonedff7082011-06-06 15:33:34 -0700360 default:
361 return -EINVAL;
362 }
363
364 return 0;
365}
366
Pankaj Bharadiya10d4b8f2017-01-04 11:10:58 +0530367int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl)
368{
369 return (ctl->info->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
370}
371
Simon Wilson8813fe82013-06-24 15:40:34 -0700372int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800373{
374 struct snd_ctl_elem_value ev;
Mythri P K67b1df42014-08-18 15:39:36 +0530375 int ret = 0;
Simon Wilson8813fe82013-06-24 15:40:34 -0700376 size_t size;
377 void *source;
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530378 size_t total_count;
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800379
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530380 if ((!ctl) || !count || !array)
381 return -EINVAL;
382
383 total_count = ctl->info->count;
384
385 if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
Pankaj Bharadiya10d4b8f2017-01-04 11:10:58 +0530386 mixer_ctl_is_access_tlv_rw(ctl)) {
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530387 /* Additional two words is for the TLV header */
388 total_count += TLV_HEADER_SIZE;
389 }
390
391 if (count > total_count)
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800392 return -EINVAL;
393
394 memset(&ev, 0, sizeof(ev));
395 ev.id.numid = ctl->info->id.numid;
396
Simon Wilson8813fe82013-06-24 15:40:34 -0700397 switch (ctl->info->type) {
398 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
399 case SNDRV_CTL_ELEM_TYPE_INTEGER:
Ashish Jaine8257c52020-03-31 09:53:16 +0530400 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
Mythri P K67b1df42014-08-18 15:39:36 +0530401 if (ret < 0)
402 return ret;
Simon Wilson8813fe82013-06-24 15:40:34 -0700403 size = sizeof(ev.value.integer.value[0]);
404 source = ev.value.integer.value;
405 break;
406
407 case SNDRV_CTL_ELEM_TYPE_BYTES:
Mythri P K67b1df42014-08-18 15:39:36 +0530408 /* check if this is new bytes TLV */
Pankaj Bharadiya10d4b8f2017-01-04 11:10:58 +0530409 if (mixer_ctl_is_access_tlv_rw(ctl)) {
Mythri P K67b1df42014-08-18 15:39:36 +0530410 struct snd_ctl_tlv *tlv;
411 int ret;
412
Ben Zhang6cb14f22016-04-22 17:59:40 -0700413 if (count > SIZE_MAX - sizeof(*tlv))
414 return -EINVAL;
Mythri P K67b1df42014-08-18 15:39:36 +0530415 tlv = calloc(1, sizeof(*tlv) + count);
Ben Zhang6cb14f22016-04-22 17:59:40 -0700416 if (!tlv)
417 return -ENOMEM;
Mythri P K67b1df42014-08-18 15:39:36 +0530418 tlv->numid = ctl->info->id.numid;
419 tlv->length = count;
Ashish Jaine8257c52020-03-31 09:53:16 +0530420 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_READ, tlv);
Mythri P K67b1df42014-08-18 15:39:36 +0530421
422 source = tlv->tlv;
423 memcpy(array, source, count);
424
425 free(tlv);
426
427 return ret;
428 } else {
Ashish Jaine8257c52020-03-31 09:53:16 +0530429 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
Mythri P K67b1df42014-08-18 15:39:36 +0530430 if (ret < 0)
431 return ret;
432 size = sizeof(ev.value.bytes.data[0]);
433 source = ev.value.bytes.data;
434 break;
435 }
Simon Wilson8813fe82013-06-24 15:40:34 -0700436
Yogesh Agrawal49a61372013-08-02 20:04:58 +0530437 case SNDRV_CTL_ELEM_TYPE_IEC958:
438 size = sizeof(ev.value.iec958);
439 source = &ev.value.iec958;
440 break;
441
Simon Wilson8813fe82013-06-24 15:40:34 -0700442 default:
443 return -EINVAL;
444 }
445
446 memcpy(array, source, size * count);
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800447
448 return 0;
449}
450
Simon Wilsonedff7082011-06-06 15:33:34 -0700451int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
452{
453 struct snd_ctl_elem_value ev;
454 int ret;
455
456 if (!ctl || (id >= ctl->info->count))
457 return -EINVAL;
458
459 memset(&ev, 0, sizeof(ev));
460 ev.id.numid = ctl->info->id.numid;
Ashish Jaine8257c52020-03-31 09:53:16 +0530461 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
Simon Wilsonedff7082011-06-06 15:33:34 -0700462 if (ret < 0)
463 return ret;
464
465 switch (ctl->info->type) {
466 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
467 ev.value.integer.value[id] = !!value;
468 break;
469
470 case SNDRV_CTL_ELEM_TYPE_INTEGER:
471 ev.value.integer.value[id] = value;
472 break;
473
474 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
475 ev.value.enumerated.item[id] = value;
476 break;
477
David.Coutheruta8481aa2013-06-11 16:22:57 +0200478 case SNDRV_CTL_ELEM_TYPE_BYTES:
479 ev.value.bytes.data[id] = value;
480 break;
481
Simon Wilsonedff7082011-06-06 15:33:34 -0700482 default:
483 return -EINVAL;
484 }
485
Ashish Jaine8257c52020-03-31 09:53:16 +0530486 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
Simon Wilsonedff7082011-06-06 15:33:34 -0700487}
488
Simon Wilson8813fe82013-06-24 15:40:34 -0700489int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800490{
491 struct snd_ctl_elem_value ev;
Simon Wilson8813fe82013-06-24 15:40:34 -0700492 size_t size;
493 void *dest;
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530494 size_t total_count;
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800495
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530496 if ((!ctl) || !count || !array)
497 return -EINVAL;
498
499 total_count = ctl->info->count;
500
501 if ((ctl->info->type == SNDRV_CTL_ELEM_TYPE_BYTES) &&
Pankaj Bharadiya10d4b8f2017-01-04 11:10:58 +0530502 mixer_ctl_is_access_tlv_rw(ctl)) {
Pawse, GuruprasadXb4e8d0d2016-02-02 17:52:29 +0530503 /* Additional two words is for the TLV header */
504 total_count += TLV_HEADER_SIZE;
505 }
506
507 if (count > total_count)
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800508 return -EINVAL;
509
510 memset(&ev, 0, sizeof(ev));
511 ev.id.numid = ctl->info->id.numid;
512
Simon Wilson8813fe82013-06-24 15:40:34 -0700513 switch (ctl->info->type) {
514 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
515 case SNDRV_CTL_ELEM_TYPE_INTEGER:
516 size = sizeof(ev.value.integer.value[0]);
517 dest = ev.value.integer.value;
518 break;
519
520 case SNDRV_CTL_ELEM_TYPE_BYTES:
Mythri P K67b1df42014-08-18 15:39:36 +0530521 /* check if this is new bytes TLV */
Pankaj Bharadiya10d4b8f2017-01-04 11:10:58 +0530522 if (mixer_ctl_is_access_tlv_rw(ctl)) {
Mythri P K67b1df42014-08-18 15:39:36 +0530523 struct snd_ctl_tlv *tlv;
524 int ret = 0;
Ben Zhang6cb14f22016-04-22 17:59:40 -0700525 if (count > SIZE_MAX - sizeof(*tlv))
526 return -EINVAL;
Mythri P K67b1df42014-08-18 15:39:36 +0530527 tlv = calloc(1, sizeof(*tlv) + count);
Ben Zhang6cb14f22016-04-22 17:59:40 -0700528 if (!tlv)
529 return -ENOMEM;
Mythri P K67b1df42014-08-18 15:39:36 +0530530 tlv->numid = ctl->info->id.numid;
531 tlv->length = count;
532 memcpy(tlv->tlv, array, count);
533
Ashish Jaine8257c52020-03-31 09:53:16 +0530534 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_TLV_WRITE, tlv);
Mythri P K67b1df42014-08-18 15:39:36 +0530535 free(tlv);
536
537 return ret;
538 } else {
539 size = sizeof(ev.value.bytes.data[0]);
540 dest = ev.value.bytes.data;
541 }
Simon Wilson8813fe82013-06-24 15:40:34 -0700542 break;
543
Yogesh Agrawal49a61372013-08-02 20:04:58 +0530544 case SNDRV_CTL_ELEM_TYPE_IEC958:
545 size = sizeof(ev.value.iec958);
546 dest = &ev.value.iec958;
547 break;
548
Simon Wilson8813fe82013-06-24 15:40:34 -0700549 default:
550 return -EINVAL;
551 }
552
553 memcpy(dest, array, size * count);
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800554
Ashish Jaine8257c52020-03-31 09:53:16 +0530555 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
Simon Wilsonda39e0b2012-11-09 15:16:47 -0800556}
557
Simon Wilsonedff7082011-06-06 15:33:34 -0700558int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
559{
Simon Wilsonedff7082011-06-06 15:33:34 -0700560 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
561 return -EINVAL;
562
Simon Wilsonedff7082011-06-06 15:33:34 -0700563 return ctl->info->value.integer.min;
564}
565
566int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
567{
Simon Wilsonedff7082011-06-06 15:33:34 -0700568 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
569 return -EINVAL;
570
Simon Wilsonedff7082011-06-06 15:33:34 -0700571 return ctl->info->value.integer.max;
572}
573
574unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
575{
576 if (!ctl)
577 return 0;
578
579 return ctl->info->value.enumerated.items;
580}
581
Simon Wilsone44e30a2012-03-08 10:45:31 -0800582const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
583 unsigned int enum_id)
Simon Wilsonedff7082011-06-06 15:33:34 -0700584{
Simon Wilsonedff7082011-06-06 15:33:34 -0700585 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
586 (enum_id >= ctl->info->value.enumerated.items))
Simon Wilsone44e30a2012-03-08 10:45:31 -0800587 return NULL;
Simon Wilsonedff7082011-06-06 15:33:34 -0700588
Simon Wilsone44e30a2012-03-08 10:45:31 -0800589 return (const char *)ctl->ename[enum_id];
Simon Wilsonedff7082011-06-06 15:33:34 -0700590}
591
592int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
593{
594 unsigned int i, num_enums;
595 struct snd_ctl_elem_value ev;
596 int ret;
597
598 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
599 return -EINVAL;
600
601 num_enums = ctl->info->value.enumerated.items;
602 for (i = 0; i < num_enums; i++) {
603 if (!strcmp(string, ctl->ename[i])) {
604 memset(&ev, 0, sizeof(ev));
605 ev.value.enumerated.item[0] = i;
606 ev.id.numid = ctl->info->id.numid;
Ashish Jaine8257c52020-03-31 09:53:16 +0530607 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
Simon Wilsonedff7082011-06-06 15:33:34 -0700608 if (ret < 0)
609 return ret;
610 return 0;
611 }
612 }
613
614 return -EINVAL;
615}
616
Pankaj Bharadiya415c0c32017-01-11 08:57:06 +0530617/** Subscribes for the mixer events.
618 * @param mixer A mixer handle.
619 * @param subscribe value indicating subscribe or unsubscribe for events
620 * @returns On success, zero.
621 * On failure, non-zero.
622 * @ingroup libtinyalsa-mixer
623 */
624int mixer_subscribe_events(struct mixer *mixer, int subscribe)
625{
Ashish Jaine8257c52020-03-31 09:53:16 +0530626 if (ioctl(mixer->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
627 return -1;
Pankaj Bharadiya415c0c32017-01-11 08:57:06 +0530628 }
629 return 0;
630}
631
632/** Wait for mixer events.
633 * @param mixer A mixer handle.
634 * @param timeout timout value
635 * @returns On success, 1.
636 * On failure, -errno.
637 * On timeout, 0
638 * @ingroup libtinyalsa-mixer
639 */
640int mixer_wait_event(struct mixer *mixer, int timeout)
641{
Ashish Jaine8257c52020-03-31 09:53:16 +0530642 struct pollfd pfd;
Pankaj Bharadiya415c0c32017-01-11 08:57:06 +0530643
Ashish Jaine8257c52020-03-31 09:53:16 +0530644 pfd.fd = mixer->fd;
645 pfd.events = POLLIN | POLLOUT | POLLERR | POLLNVAL;
Pankaj Bharadiya415c0c32017-01-11 08:57:06 +0530646
647 for (;;) {
648 int err;
Ashish Jaine8257c52020-03-31 09:53:16 +0530649 err = poll(&pfd, 1, timeout);
Pankaj Bharadiya415c0c32017-01-11 08:57:06 +0530650 if (err < 0)
651 return -errno;
652 if (!err)
653 return 0;
Ashish Jaine8257c52020-03-31 09:53:16 +0530654 if (pfd.revents & (POLLERR | POLLNVAL))
655 return -EIO;
656 if (pfd.revents & (POLLIN | POLLOUT))
657 return 1;
Pankaj Bharadiya415c0c32017-01-11 08:57:06 +0530658 }
659}
Andrew Chantb5701062018-02-05 15:16:41 -0800660
661/** Consume a mixer event.
662 * If mixer_subscribe_events has been called,
663 * mixer_wait_event will identify when a control value has changed.
664 * This function will clear a single event from the mixer so that
665 * further events can be alerted.
666 *
667 * @param mixer A mixer handle.
668 * @returns 0 on success. -errno on failure.
669 * @ingroup libtinyalsa-mixer
670 */
Ashish Jaine8257c52020-03-31 09:53:16 +0530671int mixer_consume_event(struct mixer *mixer) {
Andrew Chantb5701062018-02-05 15:16:41 -0800672 struct snd_ctl_event ev;
Ashish Jaine8257c52020-03-31 09:53:16 +0530673 ssize_t count = read(mixer->fd, &ev, sizeof(ev));
Andrew Chantb5701062018-02-05 15:16:41 -0800674 // Exporting the actual event would require exposing snd_ctl_event
675 // via the header file, and all associated structs.
676 // The events generally tell you exactly which value changed,
677 // but reading values you're interested isn't hard and simplifies
678 // the interface greatly.
679 return (count >= 0) ? 0 : -errno;
680}