blob: dc01e9951a6e194fe4ca878fa4d62ea3d54837ad [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
114struct mixer *mixer_open(unsigned int device)
115{
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;
122
123 fd = open("/dev/snd/controlC0", O_RDWR);
124 if (fd < 0)
125 return 0;
126
127 memset(&elist, 0, sizeof(elist));
128 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
129 goto fail;
130
131 mixer = calloc(1, sizeof(*mixer));
132 if (!mixer)
133 goto fail;
134
135 mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
136 mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
137 if (!mixer->ctl || !mixer->info)
138 goto fail;
139
140 eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
141 if (!eid)
142 goto fail;
143
144 mixer->count = elist.count;
145 mixer->fd = fd;
146 elist.space = mixer->count;
147 elist.pids = eid;
148 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
149 goto fail;
150
151 for (n = 0; n < mixer->count; n++) {
152 struct snd_ctl_elem_info *ei = mixer->info + n;
153 ei->id.numid = eid[n].numid;
154 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
155 goto fail;
156 mixer->ctl[n].info = ei;
157 mixer->ctl[n].mixer = mixer;
158 if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
159 char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
160 if (!enames)
161 goto fail;
162 mixer->ctl[n].ename = enames;
163 for (m = 0; m < ei->value.enumerated.items; m++) {
164 memset(&tmp, 0, sizeof(tmp));
165 tmp.id.numid = ei->id.numid;
166 tmp.value.enumerated.item = m;
167 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
168 goto fail;
169 enames[m] = strdup(tmp.value.enumerated.name);
170 if (!enames[m])
171 goto fail;
172 }
173 }
174 }
175
176 free(eid);
177 return mixer;
178
179fail:
180 /* TODO: verify frees in failure case */
181 if (eid)
182 free(eid);
183 if (mixer)
184 mixer_close(mixer);
185 else if (fd >= 0)
186 close(fd);
187 return 0;
188}
189
190int mixer_get_num_ctls(struct mixer *mixer)
191{
192 return mixer->count;
193}
194
195struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id)
196{
197 if (id < mixer->count)
198 return mixer->ctl + id;
199
200 return NULL;
201}
202
203struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name)
204{
205 unsigned int n;
206 for (n = 0; n < mixer->count; n++)
207 if (!strcmp(name, (char*) mixer->info[n].id.name))
208 return mixer->ctl + n;
209
210 return NULL;
211}
212
213static int percent_to_int(struct snd_ctl_elem_info *ei, int percent)
214{
215 int range;
216
217 if (percent > 100)
218 percent = 100;
219 else if (percent < 0)
220 percent = 0;
221
222 range = (ei->value.integer.max - ei->value.integer.min);
223
224 return ei->value.integer.min + (range * percent) / 100;
225}
226
227static int int_to_percent(struct snd_ctl_elem_info *ei, int value)
228{
229 int range = (ei->value.integer.max - ei->value.integer.min);
230
231 if (range == 0)
232 return 0;
233
234 return ((value - ei->value.integer.min) / range) * 100;
235}
236
237int mixer_ctl_get_percent(struct mixer_ctl *ctl)
238{
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700239 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER) {
240 errno = EINVAL;
241 return -1;
242 }
243
244 return int_to_percent(ctl->info, mixer_ctl_get_int(ctl));
245}
246
247int mixer_ctl_set_percent(struct mixer_ctl *ctl, int percent)
248{
249 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_INTEGER) {
250 errno = EINVAL;
251 return -1;
252 }
253
254 return mixer_ctl_set_int(ctl, percent_to_int(ctl->info, percent));
255}
256
257int mixer_ctl_get_int(struct mixer_ctl *ctl)
258{
Simon Wilson79d39652011-05-25 13:44:23 -0700259 struct snd_ctl_elem_value ev;
260
261 memset(&ev, 0, sizeof(ev));
262 ev.id.numid = ctl->info->id.numid;
263 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev))
264 return -1;
265
266 switch (ctl->info->type) {
267 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
268 return !!ev.value.integer.value[0]; /* TODO: handle multiple return values */
269 break;
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700270
271 case SNDRV_CTL_ELEM_TYPE_INTEGER:
272 return ev.value.integer.value[0]; /* TODO: handle multiple return values */
Simon Wilson79d39652011-05-25 13:44:23 -0700273 break;
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700274
Simon Wilson79d39652011-05-25 13:44:23 -0700275 default:
276 errno = EINVAL;
277 return -1;
278 }
279
280 return 0;
281}
282
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700283int mixer_ctl_set_int(struct mixer_ctl *ctl, int value)
Simon Wilson79d39652011-05-25 13:44:23 -0700284{
285 struct snd_ctl_elem_value ev;
286 unsigned int n;
287
288 memset(&ev, 0, sizeof(ev));
289 ev.id.numid = ctl->info->id.numid;
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700290
Simon Wilson79d39652011-05-25 13:44:23 -0700291 switch (ctl->info->type) {
292 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
293 for (n = 0; n < ctl->info->count; n++)
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700294 ev.value.integer.value[n] = !!value;
Simon Wilson79d39652011-05-25 13:44:23 -0700295 break;
296
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700297 case SNDRV_CTL_ELEM_TYPE_INTEGER:
Simon Wilson79d39652011-05-25 13:44:23 -0700298 for (n = 0; n < ctl->info->count; n++)
299 ev.value.integer.value[n] = value;
300 break;
Simon Wilson79d39652011-05-25 13:44:23 -0700301
302 default:
303 errno = EINVAL;
304 return -1;
305 }
Simon Wilsona1bb1e02011-05-26 18:22:00 -0700306
Simon Wilson79d39652011-05-25 13:44:23 -0700307 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
308}
309
310int mixer_ctl_set_enum(struct mixer_ctl *ctl, unsigned int id)
311{
312 struct snd_ctl_elem_value ev;
313
314 if ((ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
315 (id >= ctl->info->value.enumerated.items)) {
316 errno = EINVAL;
317 return -1;
318 }
319
320 memset(&ev, 0, sizeof(ev));
321 ev.value.enumerated.item[0] = id;
322 ev.id.numid = ctl->info->id.numid;
323 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0)
324 return -1;
325 return 0;
326}
327
328int mixer_ctl_set_enum_by_name(struct mixer_ctl *ctl, const char *string)
329{
330 unsigned int n, max;
331 struct snd_ctl_elem_value ev;
332
333 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
334 errno = EINVAL;
335 return -1;
336 }
337
338 max = ctl->info->value.enumerated.items;
339 for (n = 0; n < max; n++) {
340 if (!strcmp(string, ctl->ename[n])) {
341 memset(&ev, 0, sizeof(ev));
342 ev.value.enumerated.item[0] = n;
343 ev.id.numid = ctl->info->id.numid;
344 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0)
345 return -1;
346 return 0;
347 }
348 }
349
350 errno = EINVAL;
351 return -1;
352}