blob: 9c7e3b5baf33d5bd414bb4ea71ed74cbf504f6c1 [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{
239 struct snd_ctl_elem_value ev;
240
241 memset(&ev, 0, sizeof(ev));
242 ev.id.numid = ctl->info->id.numid;
243 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev))
244 return -1;
245
246 switch (ctl->info->type) {
247 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
248 return !!ev.value.integer.value[0]; /* TODO: handle multiple return values */
249 break;
250 case SNDRV_CTL_ELEM_TYPE_INTEGER: {
251 return int_to_percent(ctl->info, ev.value.integer.value[0]);
252 break;
253 }
254 default:
255 errno = EINVAL;
256 return -1;
257 }
258
259 return 0;
260}
261
262int mixer_ctl_set_percent(struct mixer_ctl *ctl, int percent)
263{
264 struct snd_ctl_elem_value ev;
265 unsigned int n;
266
267 memset(&ev, 0, sizeof(ev));
268 ev.id.numid = ctl->info->id.numid;
269 switch (ctl->info->type) {
270 case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
271 for (n = 0; n < ctl->info->count; n++)
272 ev.value.integer.value[n] = !!percent; /* TODO: handle multiple set values */
273 break;
274
275 case SNDRV_CTL_ELEM_TYPE_INTEGER: {
276 int value = percent_to_int(ctl->info, percent);
277 for (n = 0; n < ctl->info->count; n++)
278 ev.value.integer.value[n] = value;
279 break;
280 }
281
282 default:
283 errno = EINVAL;
284 return -1;
285 }
286
287 return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
288}
289
290int mixer_ctl_set_enum(struct mixer_ctl *ctl, unsigned int id)
291{
292 struct snd_ctl_elem_value ev;
293
294 if ((ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) ||
295 (id >= ctl->info->value.enumerated.items)) {
296 errno = EINVAL;
297 return -1;
298 }
299
300 memset(&ev, 0, sizeof(ev));
301 ev.value.enumerated.item[0] = id;
302 ev.id.numid = ctl->info->id.numid;
303 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0)
304 return -1;
305 return 0;
306}
307
308int mixer_ctl_set_enum_by_name(struct mixer_ctl *ctl, const char *string)
309{
310 unsigned int n, max;
311 struct snd_ctl_elem_value ev;
312
313 if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
314 errno = EINVAL;
315 return -1;
316 }
317
318 max = ctl->info->value.enumerated.items;
319 for (n = 0; n < max; n++) {
320 if (!strcmp(string, ctl->ename[n])) {
321 memset(&ev, 0, sizeof(ev));
322 ev.value.enumerated.item[0] = n;
323 ev.id.numid = ctl->info->id.numid;
324 if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0)
325 return -1;
326 return 0;
327 }
328 }
329
330 errno = EINVAL;
331 return -1;
332}