summaryrefslogtreecommitdiff
path: root/firmware/target/hosted/ibasso/tinyalsa/mixer.c
diff options
context:
space:
mode:
authorUdo Schläpfer <rockbox-2014.10@desktopwarrior.net>2015-02-02 21:44:29 +0100
committerUdo Schläpfer <rockbox-2014.10@desktopwarrior.net>2015-02-02 21:57:55 +0100
commitdbabd0d9c34a33bc0c51243ec37f230d117db955 (patch)
tree46de348929ce739702a230a2587fdb5539585753 /firmware/target/hosted/ibasso/tinyalsa/mixer.c
parentcef17e3d59ad93f766e8ee23b1610540a33dfe5e (diff)
downloadrockbox-dbabd0d9c34a33bc0c51243ec37f230d117db955.tar.gz
rockbox-dbabd0d9c34a33bc0c51243ec37f230d117db955.zip
iBasso DX50/DX90: Major code cleanup and reorganization.
Reorganization - Separated iBasso devices from PLATFORM_ANDROID. These are now standlone hosted targets. Most device specific code is in the firmware/target/hosted/ibasso directory. - No dependency on Android SDK, only the Android NDK is needed. 32 bit Android NDK and Android API Level 16. - Separate implementation for each device where feasible. Code cleanup - Rewrite of existing code, from simple reformat to complete reimplementation. - New backlight interface, seperating backlight from touchscreen. - Rewrite of device button handler, removing unneeded code and fixing memory leaks. - New Debug messages interface logging to Android adb logcat (DEBUGF, panicf, logf). - Rewrite of lcd device handler, removing unneeded code and fixing memory leaks. - Rewrite of audiohw device handler/pcm interface, removing unneeded code and fixing memory leaks, enabling 44.1/48kHz pthreaded playback. - Rewrite of power and powermng, proper shutdown, using batterylog results (see http://gerrit.rockbox.org/r/#/c/1047/). - Rewrite of configure (Android NDK) and device specific config. - Rewrite of the Android NDK specific Makefile. Misc - All plugins/games/demos activated. - Update tinyalsa to latest from https://github.com/tinyalsa/tinyalsa. Includes - http://gerrit.rockbox.org/r/#/c/993/ - http://gerrit.rockbox.org/r/#/c/1010/ - http://gerrit.rockbox.org/r/#/c/1035/ Does not include http://gerrit.rockbox.org/r/#/c/1007/ due to new backlight interface and new option for hold switch, touchscreen, physical button interaction. Rockbox needs the iBasso DX50/DX90 loader for startup, see http://gerrit.rockbox.org/r/#/c/1099/ The loader expects Rockbox to be installed in /mnt/sdcard/.rockbox/. If /mnt/sdcard/ is accessed as USB mass storage device, Rockbox will exit gracefully and the loader will restart Rockbox on USB disconnect. Tested on iBasso DX50. Compiled (not tested) for iBasso DX90. Compiled (not tested) for PLATFORM_ANDROID. Change-Id: I5f5e22e68f5b4cf29c28e2b40b2c265f2beb7ab7
Diffstat (limited to 'firmware/target/hosted/ibasso/tinyalsa/mixer.c')
-rw-r--r--firmware/target/hosted/ibasso/tinyalsa/mixer.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/firmware/target/hosted/ibasso/tinyalsa/mixer.c b/firmware/target/hosted/ibasso/tinyalsa/mixer.c
new file mode 100644
index 0000000000..24e94f4f1d
--- /dev/null
+++ b/firmware/target/hosted/ibasso/tinyalsa/mixer.c
@@ -0,0 +1,502 @@
1/* 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
47struct mixer_ctl {
48 struct mixer *mixer;
49 struct snd_ctl_elem_info *info;
50 char **ename;
51};
52
53struct mixer {
54 int fd;
55 struct snd_ctl_card_info card_info;
56 struct snd_ctl_elem_info *elem_info;
57 struct mixer_ctl *ctl;
58 unsigned int count;
59};
60
61void mixer_close(struct mixer *mixer)
62{
63 unsigned int n,m;
64
65 if (!mixer)
66 return;
67
68 if (mixer->fd >= 0)
69 close(mixer->fd);
70
71 if (mixer->ctl) {
72 for (n = 0; n < mixer->count; n++) {
73 if (mixer->ctl[n].ename) {
74 unsigned int max = mixer->ctl[n].info->value.enumerated.items;
75 for (m = 0; m < max; m++)
76 free(mixer->ctl[n].ename[m]);
77 free(mixer->ctl[n].ename);
78 }
79 }
80 free(mixer->ctl);
81 }
82
83 if (mixer->elem_info)
84 free(mixer->elem_info);
85
86 free(mixer);
87
88 /* TODO: verify frees */
89}
90
91struct mixer *mixer_open(unsigned int card)
92{
93 struct snd_ctl_elem_list elist;
94 struct snd_ctl_elem_info tmp;
95 struct snd_ctl_elem_id *eid = NULL;
96 struct mixer *mixer = NULL;
97 unsigned int n, m;
98 int fd;
99 char fn[256];
100
101 snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
102 fd = open(fn, O_RDWR);
103 if (fd < 0)
104 return 0;
105
106 memset(&elist, 0, sizeof(elist));
107 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
108 goto fail;
109
110 mixer = calloc(1, sizeof(*mixer));
111 if (!mixer)
112 goto fail;
113
114 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
115 mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
116 if (!mixer->ctl || !mixer->elem_info)
117 goto fail;
118
119 if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
120 goto fail;
121
122 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
123 if (!eid)
124 goto fail;
125
126 mixer->count = elist.count;
127 mixer->fd = fd;
128 elist.space = mixer->count;
129 elist.pids = eid;
130 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
131 goto fail;
132
133 for (n = 0; n < mixer->count; n++) {
134 struct snd_ctl_elem_info *ei = mixer->elem_info + n;
135 ei->id.numid = eid[n].numid;
136 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
137 goto fail;
138 mixer->ctl[n].info = ei;
139 mixer->ctl[n].mixer = mixer;
140 if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
141 char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
142 if (!enames)
143 goto fail;
144 mixer->ctl[n].ename = enames;
145 for (m = 0; m < ei->value.enumerated.items; m++) {
146 memset(&tmp, 0, sizeof(tmp));
147 tmp.id.numid = ei->id.numid;
148 tmp.value.enumerated.item = m;
149 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
150 goto fail;
151 enames[m] = strdup(tmp.value.enumerated.name);
152 if (!enames[m])
153 goto fail;
154 }
155 }
156 }
157
158 free(eid);
159 return mixer;
160
161fail:
162 /* TODO: verify frees in failure case */
163 if (eid)
164 free(eid);
165 if (mixer)
166 mixer_close(mixer);
167 else if (fd >= 0)
168 close(fd);
169 return 0;
170}
171
172const char *mixer_get_name(struct mixer *mixer)
173{
174 return (const char *)mixer->card_info.name;
175}
176
177unsigned int mixer_get_num_ctls(struct mixer *mixer)
178{
179 if (!mixer)
180 return 0;
181
182 return mixer->count;
183}
184
185struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
186{
187 if (mixer && (id < mixer->count))
188 return mixer->ctl + id;
189
190 return NULL;
191}
192
193struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
194{
195 unsigned int n;
196
197 if (!mixer)
198 return NULL;
199
200 for (n = 0; n < mixer->count; n++)
201 if (!strcmp(name, (char*) mixer->elem_info[n].id.name))
202 return mixer->ctl + n;
203
204 return NULL;
205}
206
207void mixer_ctl_update(struct mixer_ctl *ctl)
208{
209 ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_INFO, ctl->info);
210}
211
212const char *mixer_ctl_get_name(struct mixer_ctl *ctl)
213{
214 if (!ctl)
215 return NULL;
216
217 return (const char *)ctl->info->id.name;
218}
219
220enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl)
221{
222 if (!ctl)
223 return MIXER_CTL_TYPE_UNKNOWN;
224
225 switch (ctl->info->type) {
226 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return MIXER_CTL_TYPE_BOOL;
227 case SNDRV_CTL_ELEM_TYPE_INTEGER: return MIXER_CTL_TYPE_INT;
228 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return MIXER_CTL_TYPE_ENUM;
229 case SNDRV_CTL_ELEM_TYPE_BYTES: return MIXER_CTL_TYPE_BYTE;
230 case SNDRV_CTL_ELEM_TYPE_IEC958: return MIXER_CTL_TYPE_IEC958;
231 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return MIXER_CTL_TYPE_INT64;
232 default: return MIXER_CTL_TYPE_UNKNOWN;
233 };
234}
235
236const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl)
237{
238 if (!ctl)
239 return "";
240
241 switch (ctl->info->type) {
242 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
243 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT";
244 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
245 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTE";
246 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
247 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
248 default: return "Unknown";
249 };
250}
251
252unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl)
253{
254 if (!ctl)
255 return 0;
256
257 return ctl->info->count;
258}
259
260static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
261{
262 if ((percent > 100) || (percent < 0)) {
263 return -EINVAL;
264 }
265
266 int range = (ei->value.integer.max - ei->value.integer.min);
267
268 return ei->value.integer.min + (range * percent) / 100;
269}
270
271static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
272{
273 int range = (ei->value.integer.max - ei->value.integer.min);
274
275 if (range == 0)
276 return 0;
277
278 return ((value - ei->value.integer.min) / range) * 100;
279}
280
281int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id)
282{
283 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
284 return -EINVAL;
285
286 return int_to_percent(ctl->info, mixer_ctl_get_value(ctl, id));
287}
288
289int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent)
290{
291 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
292 return -EINVAL;
293
294 return mixer_ctl_set_value(ctl, id, percent_to_int(ctl->info, percent));
295}
296
297int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id)
298{
299 struct snd_ctl_elem_value ev;
300 int ret;
301
302 if (!ctl || (id >= ctl->info->count))
303 return -EINVAL;
304
305 memset(&ev, 0, sizeof(ev));
306 ev.id.numid = ctl->info->id.numid;
307 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
308 if (ret < 0)
309 return ret;
310
311 switch (ctl->info->type) {
312 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
313 return !!ev.value.integer.value[id];
314
315 case SNDRV_CTL_ELEM_TYPE_INTEGER:
316 return ev.value.integer.value[id];
317
318 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
319 return ev.value.enumerated.item[id];
320
321 case SNDRV_CTL_ELEM_TYPE_BYTES:
322 return ev.value.bytes.data[id];
323
324 default:
325 return -EINVAL;
326 }
327
328 return 0;
329}
330
331int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count)
332{
333 struct snd_ctl_elem_value ev;
334 int ret;
335 size_t size;
336 void *source;
337
338 if (!ctl || (count > ctl->info->count) || !count || !array)
339 return -EINVAL;
340
341 memset(&ev, 0, sizeof(ev));
342 ev.id.numid = ctl->info->id.numid;
343
344 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
345 if (ret < 0)
346 return ret;
347
348 switch (ctl->info->type) {
349 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
350 case SNDRV_CTL_ELEM_TYPE_INTEGER:
351 size = sizeof(ev.value.integer.value[0]);
352 source = ev.value.integer.value;
353 break;
354
355 case SNDRV_CTL_ELEM_TYPE_BYTES:
356 size = sizeof(ev.value.bytes.data[0]);
357 source = ev.value.bytes.data;
358 break;
359
360 default:
361 return -EINVAL;
362 }
363
364 memcpy(array, source, size * count);
365
366 return 0;
367}
368
369int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
370{
371 struct snd_ctl_elem_value ev;
372 int ret;
373
374 if (!ctl || (id >= ctl->info->count))
375 return -EINVAL;
376
377 memset(&ev, 0, sizeof(ev));
378 ev.id.numid = ctl->info->id.numid;
379 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);
380 if (ret < 0)
381 return ret;
382
383 switch (ctl->info->type) {
384 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
385 ev.value.integer.value[id] = !!value;
386 break;
387
388 case SNDRV_CTL_ELEM_TYPE_INTEGER:
389 if ((value < mixer_ctl_get_range_min(ctl)) ||
390 (value > mixer_ctl_get_range_max(ctl))) {
391 return -EINVAL;
392 }
393
394 ev.value.integer.value[id] = value;
395 break;
396
397 case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
398 ev.value.enumerated.item[id] = value;
399 break;
400
401 case SNDRV_CTL_ELEM_TYPE_BYTES:
402 ev.value.bytes.data[id] = value;
403 break;
404
405 default:
406 return -EINVAL;
407 }
408
409 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
410}
411
412int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count)
413{
414 struct snd_ctl_elem_value ev;
415 size_t size;
416 void *dest;
417
418 if (!ctl || (count > ctl->info->count) || !count || !array)
419 return -EINVAL;
420
421 memset(&ev, 0, sizeof(ev));
422 ev.id.numid = ctl->info->id.numid;
423
424 switch (ctl->info->type) {
425 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
426 case SNDRV_CTL_ELEM_TYPE_INTEGER:
427 size = sizeof(ev.value.integer.value[0]);
428 dest = ev.value.integer.value;
429 break;
430
431 case SNDRV_CTL_ELEM_TYPE_BYTES:
432 size = sizeof(ev.value.bytes.data[0]);
433 dest = ev.value.bytes.data;
434 break;
435
436 default:
437 return -EINVAL;
438 }
439
440 memcpy(dest, array, size * count);
441
442 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
443}
444
445int mixer_ctl_get_range_min(struct mixer_ctl *ctl)
446{
447 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER))
448 return -EINVAL;
449
450 return ctl->info->value.integer.min;
451}
452
453int mixer_ctl_get_range_max(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.max;
459}
460
461unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl)
462{
463 if (!ctl)
464 return 0;
465
466 return ctl->info->value.enumerated.items;
467}
468
469const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl,
470 unsigned int enum_id)
471{
472 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
473 (enum_id >= ctl->info->value.enumerated.items))
474 return NULL;
475
476 return (const char *)ctl->ename[enum_id];
477}
478
479int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
480{
481 unsigned int i, num_enums;
482 struct snd_ctl_elem_value ev;
483 int ret;
484
485 if (!ctl || (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED))
486 return -EINVAL;
487
488 num_enums = ctl->info->value.enumerated.items;
489 for (i = 0; i < num_enums; i++) {
490 if (!strcmp(string, ctl->ename[i])) {
491 memset(&ev, 0, sizeof(ev));
492 ev.value.enumerated.item[0] = i;
493 ev.id.numid = ctl->info->id.numid;
494 ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
495 if (ret < 0)
496 return ret;
497 return 0;
498 }
499 }
500
501 return -EINVAL;
502}