blob: 97f4beb1501ff2c74c046d6cea519715042880ec [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>
31#include <string.h>
32#include <unistd.h>
33#include <fcntl.h>
34#include <errno.h>
35#include <ctype.h>
36
37#include <linux/ioctl.h>
38#define __force
39#define __bitwise
40#define __user
41#include <sound/asound.h>
42
43#include <tinyalsa/asoundlib.h>
44
45static const char *elem_iface_name(snd_ctl_elem_iface_t n)
46{
47 switch (n) {
48 case SNDRV_CTL_ELEM_IFACE_CARD: return "CARD";
49 case SNDRV_CTL_ELEM_IFACE_HWDEP: return "HWDEP";
50 case SNDRV_CTL_ELEM_IFACE_MIXER: return "MIXER";
51 case SNDRV_CTL_ELEM_IFACE_PCM: return "PCM";
52 case SNDRV_CTL_ELEM_IFACE_RAWMIDI: return "MIDI";
53 case SNDRV_CTL_ELEM_IFACE_TIMER: return "TIMER";
54 case SNDRV_CTL_ELEM_IFACE_SEQUENCER: return "SEQ";
55 default: return "???";
56 }
57}
58
59static const char *elem_type_name(snd_ctl_elem_type_t n)
60{
61 switch (n) {
62 case SNDRV_CTL_ELEM_TYPE_NONE: return "NONE";
63 case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
64 case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT32";
65 case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
66 case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTES";
67 case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
68 case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
69 default: return "???";
70 }
71}
72
73
74struct mixer_ctl {
75 struct mixer *mixer;
76 struct snd_ctl_elem_info *info;
77 char **ename;
78};
79
80struct mixer {
81 int fd;
82 struct snd_ctl_elem_info *info;
83 struct mixer_ctl *ctl;
84 unsigned int count;
85};
86
87void mixer_close(struct mixer *mixer)
88{
89 unsigned int n,m;
90
91 if (mixer->fd >= 0)
92 close(mixer->fd);
93
94 if (mixer->ctl) {
95 for (n = 0; n < mixer->count; n++) {
96 if (mixer->ctl[n].ename) {
97 unsigned int max = mixer->ctl[n].info->value.enumerated.items;
98 for (m = 0; m < max; m++)
99 free(mixer->ctl[n].ename[m]);
100 free(mixer->ctl[n].ename);
101 }
102 }
103 free(mixer->ctl);
104 }
105
106 if (mixer->info)
107 free(mixer->info);
108
109 free(mixer);
110
111 /* TODO: verify frees */
112}
113
Simon Wilson1bd580f2011-06-02 15:58:41 -0700114struct mixer *mixer_open(unsigned int card)
Simon Wilson79d39652011-05-25 13:44:23 -0700115{
116 struct snd_ctl_elem_list elist;
117 struct snd_ctl_elem_info tmp;
118 struct snd_ctl_elem_id *eid = NULL;
119 struct mixer *mixer = NULL;
120 unsigned int n, m;
121 int fd;
Simon Wilson1bd580f2011-06-02 15:58:41 -0700122 char fn[256];
Simon Wilson79d39652011-05-25 13:44:23 -0700123
Simon Wilson1bd580f2011-06-02 15:58:41 -0700124 snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
125 fd = open(fn, O_RDWR);
Simon Wilson79d39652011-05-25 13:44:23 -0700126 if (fd < 0)
127 return 0;
128
129 memset(&elist, 0, sizeof(elist));
130 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
131 goto fail;
132
133 mixer = calloc(1, sizeof(*mixer));
134 if (!mixer)
135 goto fail;
136
137 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
138 mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
139 if (!mixer->ctl || !mixer->info)
140 goto fail;
141
142 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
143 if (!eid)
144 goto fail;
145
146 mixer->count = elist.count;
147 mixer->fd = fd;
148 elist.space = mixer->count;
149 elist.pids = eid;
150 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
151 goto fail;
152
153 for (n = 0; n < mixer->count; n++) {
154 struct snd_ctl_elem_info *ei = mixer->info + n;
155 ei->id.numid = eid[n].numid;
156 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
157 goto fail;
158 mixer->ctl[n].info = ei;
159 mixer->ctl[n].mixer = mixer;
160 if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
161 char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
162 if (!enames)
163 goto fail;
164 mixer->ctl[n].ename = enames;
165 for (m = 0; m < ei->value.enumerated.items; m++) {
166 memset(&tmp, 0, sizeof(tmp));
167 tmp.id.numid = ei->id.numid;
168 tmp.value.enumerated.item = m;
169 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
170 goto fail;
171 enames[m] = strdup(tmp.value.enumerated.name);
172 if (!enames[m])
173 goto fail;
174 }
175 }
176 }
177
178 free(eid);
179 return mixer;
180
181fail:
182 /* TODO: verify frees in failure case */
183 if (eid)
184 free(eid);
185 if (mixer)
186 mixer_close(mixer);
187 else if (fd >= 0)
188 close(fd);
189 return 0;
190}
191
192int mixer_get_num_ctls(struct mixer *mixer)
193{
194 return mixer->count;
195}
196
197struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
198{
199 if (id < mixer->count)
200 return mixer->ctl + id;
201
202 return NULL;
203}
204
205struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
206{
207 unsigned int n;
208 for (n = 0; n < mixer->count; n++)
209 if (!strcmp(name, (char*) mixer->info[n].id.name))
210 return mixer->ctl + n;
211
212 return NULL;
213}
214
215static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
216{
217 int range;
218
219 if (percent > 100)
220 percent = 100;
221 else if (percent < 0)
222 percent = 0;
223
224 range = (ei->value.integer.max - ei->value.integer.min);
225
226 return ei->value.integer.min + (range * percent) / 100;
227}
228
229static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
230{
231 int range = (ei->value.integer.max - ei->value.integer.min);
232
233 if (range == 0)
234 return 0;
235
236 return ((value - ei->value.integer.min) / range) * 100;
237}
238
239int mixer_ctl_get_percent(struct mixer_ctl *ctl)
240{
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700241 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER) {
242 errno = EINVAL;
243 return -1;
244 }
245
246 return int_to_percent(ctl->info, mixer_ctl_get_int(ctl));
247}
248
249int mixer_ctl_set_percent(struct mixer_ctl *ctl, int percent)
250{
251 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER) {
252 errno = EINVAL;
253 return -1;
254 }
255
256 return mixer_ctl_set_int(ctl, percent_to_int(ctl->info, percent));
257}
258
259int mixer_ctl_get_int(struct mixer_ctl *ctl)
260{
Simon Wilson79d39652011-05-25 13:44:23 -0700261 struct snd_ctl_elem_value ev;
262
263 memset(&ev, 0, sizeof(ev));
264 ev.id.numid = ctl->info->id.numid;
265 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev))
266 return -1;
267
268 switch (ctl->info->type) {
269 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
270 return !!ev.value.integer.value[0]; /* TODO: handle multiple return values */
271 break;
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700272
273 case SNDRV_CTL_ELEM_TYPE_INTEGER:
274 return ev.value.integer.value[0]; /* TODO: handle multiple return values */
Simon Wilson79d39652011-05-25 13:44:23 -0700275 break;
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700276
Simon Wilson79d39652011-05-25 13:44:23 -0700277 default:
278 errno = EINVAL;
279 return -1;
280 }
281
282 return 0;
283}
284
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700285int mixer_ctl_set_int(struct mixer_ctl *ctl, int value)
Simon Wilson79d39652011-05-25 13:44:23 -0700286{
287 struct snd_ctl_elem_value ev;
288 unsigned int n;
289
290 memset(&ev, 0, sizeof(ev));
291 ev.id.numid = ctl->info->id.numid;
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700292
Simon Wilson79d39652011-05-25 13:44:23 -0700293 switch (ctl->info->type) {
294 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
295 for (n = 0; n < ctl->info->count; n++)
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700296 ev.value.integer.value[n] = !!value;
Simon Wilson79d39652011-05-25 13:44:23 -0700297 break;
298
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700299 case SNDRV_CTL_ELEM_TYPE_INTEGER:
Simon Wilson79d39652011-05-25 13:44:23 -0700300 for (n = 0; n < ctl->info->count; n++)
301 ev.value.integer.value[n] = value;
302 break;
Simon Wilson79d39652011-05-25 13:44:23 -0700303
304 default:
305 errno = EINVAL;
306 return -1;
307 }
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700308
Simon Wilson79d39652011-05-25 13:44:23 -0700309 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
310}
311
312int mixer_ctl_set_enum(struct mixer_ctl *ctl, unsigned int id)
313{
314 struct snd_ctl_elem_value ev;
315
316 if ((ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
317 (id >= ctl->info->value.enumerated.items)) {
318 errno = EINVAL;
319 return -1;
320 }
321
322 memset(&ev, 0, sizeof(ev));
323 ev.value.enumerated.item[0] = id;
324 ev.id.numid = ctl->info->id.numid;
325 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0)
326 return -1;
327 return 0;
328}
329
330int mixer_ctl_set_enum_by_name(struct mixer_ctl *ctl, const char *string)
331{
332 unsigned int n, max;
333 struct snd_ctl_elem_value ev;
334
335 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
336 errno = EINVAL;
337 return -1;
338 }
339
340 max = ctl->info->value.enumerated.items;
341 for (n = 0; n < max; n++) {
342 if (!strcmp(string, ctl->ename[n])) {
343 memset(&ev, 0, sizeof(ev));
344 ev.value.enumerated.item[0] = n;
345 ev.id.numid = ctl->info->id.numid;
346 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0)
347 return -1;
348 return 0;
349 }
350 }
351
352 errno = EINVAL;
353 return -1;
354}