blob: 17d5d0f3d96bdd97c91340cdbe850eb9f0cffb77 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/* 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>
31#include <string.h>
32#include <unistd.h>
33#include <fcntl.h>
34#include <errno.h>
35#include <ctype.h>
36
37#include <sys/ioctl.h>
38
39#include <linux/ioctl.h>
40#define __force
41#define __bitwise
42#define __user
43#include <sound/asound.h>
44
45#include <tinyalsa/asoundlib.h>
46
47/* Mixer obtains "absolute values" to be written into Kernel-ALSA
48 * But default ALSA behavior is READ-modify-WRITE.
49 * From other hand the extended ALSA has new flag "write_force"
50 * causing write single element as WRITE-ONLY without read
51 */
52//#define MIXER_WRITE_FORCE_NO_READ
53
54struct mixer_ctl {
55 struct mixer *mixer;
56 struct snd_ctl_elem_info *info;
57 char **ename;
58};
59
60struct mixer {
61 int fd;
62 struct snd_ctl_card_info card_info;
63 struct snd_ctl_elem_info *elem_info;
64 struct mixer_ctl *ctl;
65 unsigned int count;
66};
67
68void mixer_close(struct mixer *mixer)
69{
70 unsigned int n,m;
71
72 if (!mixer)
73 return;
74
75 if (mixer->fd >= 0)
76 close(mixer->fd);
77
78 if (mixer->ctl) {
79 for (n = 0; n < mixer->count; n++) {
80 if (mixer->ctl[n].ename) {
81 unsigned int max = mixer->ctl[n].info->value.enumerated.items;
82 for (m = 0; m < max; m++)
83 free(mixer->ctl[n].ename[m]);
84 free(mixer->ctl[n].ename);
85 }
86 }
87 free(mixer->ctl);
88 }
89
90 if (mixer->elem_info)
91 free(mixer->elem_info);
92
93 free(mixer);
94
95 /* TODO: verify frees */
96}
97
98struct mixer *mixer_open(unsigned int card)
99{
100 struct snd_ctl_elem_list elist;
101 struct snd_ctl_elem_info tmp;
102 struct snd_ctl_elem_id *eid = NULL;
103 struct mixer *mixer = NULL;
104 unsigned int n, m;
105 int fd;
106 char fn[256];
107
108 snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
109 fd = open(fn, O_RDWR);
110 if (fd < 0)
111 return 0;
112
113 memset(&elist, 0, sizeof(elist));
114 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
115 goto fail;
116
117 mixer = calloc(1, sizeof(*mixer));
118 if (!mixer)
119 goto fail;
120
121 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;
125
126 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;
136 elist.pids = eid;
137 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
138 goto fail;
139
140 for (n = 0; n < mixer->count; n++) {
141 struct snd_ctl_elem_info *ei = mixer->elem_info + n;
142 ei->id.numid = eid[n].numid;
143 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
144 goto fail;
145 mixer->ctl[n].info = ei;
146 mixer->ctl[n].mixer = mixer;
147 if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
148 char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
149 if (!enames)
150 goto fail;
151 mixer->ctl[n].ename = enames;
152 for (m = 0; m < ei->value.enumerated.items; m++) {
153 memset(&tmp, 0, sizeof(tmp));
154 tmp.id.numid = ei->id.numid;
155 tmp.value.enumerated.item = m;
156 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
157 goto fail;
158 enames[m] = strdup(tmp.value.enumerated.name);
159 if (!enames[m])
160 goto fail;
161 }
162 }
163 }
164
165 free(eid);
166 return mixer;
167
168fail:
169 /* TODO: verify frees in failure case */
170 if (eid)
171 free(eid);
172 if (mixer)
173 mixer_close(mixer);
174 if (fd >= 0)
175 close(fd);
176 return 0;
177}
178
179const char *mixer_get_name(struct mixer *mixer)
180{
181 return (const char *)mixer->card_info.name;
182}
183
184unsigned int mixer_get_num_ctls(struct mixer *mixer)
185{
186 if (!mixer)
187 return 0;
188
189 return mixer->count;
190}
191
192struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
193{
194 if (mixer && (id < mixer->count))
195 return mixer->ctl + id;
196
197 return NULL;
198}
199
200struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
201{
202 unsigned int n;
203
204 if (!mixer)
205 return NULL;
206
207 for (n = 0; n < mixer->count; n++)
208 if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
209 return mixer->ctl + n;
210
211 return NULL;
212}
213
214void mixer_ctl_update(struct mixer_ctl *ctl)
215{
216 ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
217}
218
219const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
220{
221 if (!ctl)
222 return NULL;
223
224 return (const char *)ctl->info->id.name;
225}
226
227enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
228{
229 if (!ctl)
230 return MIXER_CTL_TYPE_UNKNOWN;
231
232 switch (ctl->info->type) {
233 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
234 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
235 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
236 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
237 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
238 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
239 default: return MIXER_CTL_TYPE_UNKNOWN;
240 };
241}
242
243const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
244{
245 if (!ctl)
246 return "";
247
248 switch (ctl->info->type) {
249 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
250 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
251 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
252 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
253 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
254 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
255 default: return "Unknown";
256 };
257}
258
259unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
260{
261 if (!ctl)
262 return 0;
263
264 return ctl->info->count;
265}
266
267static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
268{
269 int range;
270
271 if (percent > 100)
272 percent = 100;
273 else if (percent < 0)
274 percent = 0;
275
276 range = (ei->value.integer.max - ei->value.integer.min);
277
278 return ei->value.integer.min + (range * percent) / 100;
279}
280
281static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
282{
283 int range = (ei->value.integer.max - ei->value.integer.min);
284
285 if (range == 0)
286 return 0;
287
288 return ((value - ei->value.integer.min) / range) * 100;
289}
290
291int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
292{
293 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
294 return -EINVAL;
295
296 return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
297}
298
299int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
300{
301 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
302 return -EINVAL;
303
304 return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
305}
306
307int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
308{
309 struct snd_ctl_elem_value ev;
310 int ret;
311
312 if (!ctl || (id >= ctl->info->count))
313 return -EINVAL;
314
315 memset(&ev, 0, sizeof(ev));
316 ev.id.numid = ctl->info->id.numid;
317 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
318 if (ret < 0)
319 return ret;
320
321 switch (ctl->info->type) {
322 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
323 return !!ev.value.integer.value[id];
324
325 case SNDRV_CTL_ELEM_TYPE_INTEGER:
326 return ev.value.integer.value[id];
327
328 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
329 return ev.value.enumerated.item[id];
330
331 case SNDRV_CTL_ELEM_TYPE_BYTES:
332 return ev.value.bytes.data[id];
333
334 default:
335 return -EINVAL;
336 }
337
338 return 0;
339}
340
341int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
342{
343 struct snd_ctl_elem_value ev;
344 int ret;
345 size_t size;
346 void *source;
347
348 if (!ctl || (count > ctl->info->count) || !count || !array)
349 return -EINVAL;
350
351 memset(&ev, 0, sizeof(ev));
352 ev.id.numid = ctl->info->id.numid;
353
354 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
355 if (ret < 0)
356 return ret;
357
358 switch (ctl->info->type) {
359 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
360 case SNDRV_CTL_ELEM_TYPE_INTEGER:
361 size = sizeof(ev.value.integer.value[0]);
362 source = ev.value.integer.value;
363 break;
364
365 case SNDRV_CTL_ELEM_TYPE_BYTES:
366 size = sizeof(ev.value.bytes.data[0]);
367 source = ev.value.bytes.data;
368 break;
369
370 default:
371 return -EINVAL;
372 }
373
374 memcpy(array, source, size * count);
375
376 return 0;
377}
378
379int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
380{
381 struct snd_ctl_elem_value ev;
382 int ret;
383
384 if (!ctl || (id >= ctl->info->count))
385 return -EINVAL;
386
387 memset(&ev, 0, sizeof(ev));
388 ev.id.numid = ctl->info->id.numid;
389
390 // Don't read original value and write new data directly to
391 // optimize enable/disable path duration.
392 // this change will save about 0.4ms for each register writing
393// ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
394// if (ret < 0)
395// return ret;
396
397 switch (ctl->info->type) {
398 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
399 ev.value.integer.value[id] = !!value;
400 break;
401
402 case SNDRV_CTL_ELEM_TYPE_INTEGER:
403#ifdef MIXER_WRITE_FORCE_NO_READ
404 ev.write_force = 1;
405#endif
406 ev.value.integer.value[id] = value;
407 break;
408
409 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
410 ev.value.enumerated.item[id] = value;
411 break;
412
413 default:
414 return -EINVAL;
415 }
416
417 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
418}
419
420int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
421{
422 struct snd_ctl_elem_value ev;
423 size_t size;
424 void *dest;
425
426 if (!ctl || (count > ctl->info->count) || !count || !array)
427 return -EINVAL;
428
429 memset(&ev, 0, sizeof(ev));
430 ev.id.numid = ctl->info->id.numid;
431
432 switch (ctl->info->type) {
433 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
434 case SNDRV_CTL_ELEM_TYPE_INTEGER:
435 size = sizeof(ev.value.integer.value[0]);
436 dest = ev.value.integer.value;
437 break;
438
439 case SNDRV_CTL_ELEM_TYPE_BYTES:
440 size = sizeof(ev.value.bytes.data[0]);
441 dest = ev.value.bytes.data;
442 break;
443
444 default:
445 return -EINVAL;
446 }
447
448 memcpy(dest, array, size * count);
449
450 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
451}
452
453int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
454{
455 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
456 return -EINVAL;
457
458 return ctl->info->value.integer.min;
459}
460
461int mixer_ctl_get_range_max(struct mixer_ctl *ctl)
462{
463 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
464 return -EINVAL;
465
466 return ctl->info->value.integer.max;
467}
468
469unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
470{
471 if (!ctl)
472 return 0;
473
474 return ctl->info->value.enumerated.items;
475}
476
477const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
478 unsigned int enum_id)
479{
480 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
481 (enum_id >= ctl->info->value.enumerated.items))
482 return NULL;
483
484 return (const char *)ctl->ename[enum_id];
485}
486
487int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
488{
489 unsigned int i, num_enums;
490 struct snd_ctl_elem_value ev;
491 int ret;
492
493 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
494 return -EINVAL;
495
496 num_enums = ctl->info->value.enumerated.items;
497 for (i = 0; i < num_enums; i++) {
498 if (!strcmp(string, ctl->ename[i])) {
499 memset(&ev, 0, sizeof(ev));
500 ev.value.enumerated.item[0] = i;
501 ev.id.numid = ctl->info->id.numid;
502 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
503 if (ret < 0)
504 return ret;
505 return 0;
506 }
507 }
508
509 return -EINVAL;
510}