• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 ** Copyright 2010, The Android Open-Source Project
3 **
4 ** Licensed under the Apache License, Version 2.0 (the "License");
5 ** you may not use this file except in compliance with the License.
6 ** You may obtain a copy of the License at
7 **
8 **     http://www.apache.org/licenses/LICENSE-2.0
9 **
10 ** Unless required by applicable law or agreed to in writing, software
11 ** distributed under the License is distributed on an "AS IS" BASIS,
12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 ** See the License for the specific language governing permissions and
14 ** limitations under the License.
15 */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <ctype.h>
24 
25 #include <linux/ioctl.h>
26 #define __force
27 #define __bitwise
28 #define __user
29 #include "asound.h"
30 
31 #include "alsa_audio.h"
32 
elem_iface_name(snd_ctl_elem_iface_t n)33 static const char *elem_iface_name(snd_ctl_elem_iface_t n)
34 {
35     switch (n) {
36     case SNDRV_CTL_ELEM_IFACE_CARD: return "CARD";
37     case SNDRV_CTL_ELEM_IFACE_HWDEP: return "HWDEP";
38     case SNDRV_CTL_ELEM_IFACE_MIXER: return "MIXER";
39     case SNDRV_CTL_ELEM_IFACE_PCM: return "PCM";
40     case SNDRV_CTL_ELEM_IFACE_RAWMIDI: return "MIDI";
41     case SNDRV_CTL_ELEM_IFACE_TIMER: return "TIMER";
42     case SNDRV_CTL_ELEM_IFACE_SEQUENCER: return "SEQ";
43     default: return "???";
44     }
45 }
46 
elem_type_name(snd_ctl_elem_type_t n)47 static const char *elem_type_name(snd_ctl_elem_type_t n)
48 {
49     switch (n) {
50     case SNDRV_CTL_ELEM_TYPE_NONE: return "NONE";
51     case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL";
52     case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT32";
53     case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM";
54     case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTES";
55     case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958";
56     case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64";
57     default: return "???";
58     }
59 }
60 
61 
62 struct mixer_ctl {
63     struct mixer *mixer;
64     struct snd_ctl_elem_info *info;
65     char **ename;
66 };
67 
68 struct mixer {
69     int fd;
70     struct snd_ctl_elem_info *info;
71     struct mixer_ctl *ctl;
72     unsigned count;
73 };
74 
mixer_close(struct mixer * mixer)75 void mixer_close(struct mixer *mixer)
76 {
77     unsigned n,m;
78 
79     if (mixer->fd >= 0)
80         close(mixer->fd);
81 
82     if (mixer->ctl) {
83         for (n = 0; n < mixer->count; n++) {
84             if (mixer->ctl[n].ename) {
85                 unsigned max = mixer->ctl[n].info->value.enumerated.items;
86                 for (m = 0; m < max; m++)
87                     free(mixer->ctl[n].ename[m]);
88                 free(mixer->ctl[n].ename);
89             }
90         }
91         free(mixer->ctl);
92     }
93 
94     if (mixer->info)
95         free(mixer->info);
96 
97     free(mixer);
98 }
99 
mixer_open(void)100 struct mixer *mixer_open(void)
101 {
102     struct snd_ctl_elem_list elist;
103     struct snd_ctl_elem_info tmp;
104     struct snd_ctl_elem_id *eid = NULL;
105     struct mixer *mixer = NULL;
106     unsigned n, m;
107     int fd;
108 
109     fd = open("/dev/snd/controlC0", 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->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));
123     if (!mixer->ctl || !mixer->info)
124         goto fail;
125 
126     eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));
127     if (!eid)
128         goto fail;
129 
130     mixer->count = elist.count;
131     mixer->fd = fd;
132     elist.space = mixer->count;
133     elist.pids = eid;
134     if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
135         goto fail;
136 
137     for (n = 0; n < mixer->count; n++) {
138         struct snd_ctl_elem_info *ei = mixer->info + n;
139         ei->id.numid = eid[n].numid;
140         if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)
141             goto fail;
142         mixer->ctl[n].info = ei;
143         mixer->ctl[n].mixer = mixer;
144         if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
145             char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
146             if (!enames)
147                 goto fail;
148             mixer->ctl[n].ename = enames;
149             for (m = 0; m < ei->value.enumerated.items; m++) {
150                 memset(&tmp, 0, sizeof(tmp));
151                 tmp.id.numid = ei->id.numid;
152                 tmp.value.enumerated.item = m;
153                 if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
154                     goto fail;
155                 enames[m] = strdup(tmp.value.enumerated.name);
156                 if (!enames[m])
157                     goto fail;
158             }
159         }
160     }
161 
162     free(eid);
163     return mixer;
164 
165 fail:
166     if (eid)
167         free(eid);
168     if (mixer)
169         mixer_close(mixer);
170     else if (fd >= 0)
171         close(fd);
172     return 0;
173 }
174 
mixer_dump(struct mixer * mixer)175 void mixer_dump(struct mixer *mixer)
176 {
177     unsigned n, m;
178 
179     printf("  id iface dev sub idx num perms     type   name\n");
180     for (n = 0; n < mixer->count; n++) {
181         struct snd_ctl_elem_info *ei = mixer->info + n;
182 
183         printf("%4d %5s %3d %3d %3d %3d %c%c%c%c%c%c%c%c%c %-6s %s",
184                ei->id.numid, elem_iface_name(ei->id.iface),
185                ei->id.device, ei->id.subdevice, ei->id.index,
186                ei->count,
187                (ei->access & SNDRV_CTL_ELEM_ACCESS_READ) ? 'r' : ' ',
188                (ei->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ? 'w' : ' ',
189                (ei->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE) ? 'V' : ' ',
190                (ei->access & SNDRV_CTL_ELEM_ACCESS_TIMESTAMP) ? 'T' : ' ',
191                (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) ? 'R' : ' ',
192                (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) ? 'W' : ' ',
193                (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) ? 'C' : ' ',
194                (ei->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) ? 'I' : ' ',
195                (ei->access & SNDRV_CTL_ELEM_ACCESS_LOCK) ? 'L' : ' ',
196                elem_type_name(ei->type),
197                ei->id.name);
198         switch (ei->type) {
199         case SNDRV_CTL_ELEM_TYPE_INTEGER:
200             printf(ei->value.integer.step ?
201                    " { %ld-%ld, %ld }\n" : " { %ld-%ld }",
202                    ei->value.integer.min,
203                    ei->value.integer.max,
204                    ei->value.integer.step);
205             break;
206         case SNDRV_CTL_ELEM_TYPE_INTEGER64:
207             printf(ei->value.integer64.step ?
208                    " { %lld-%lld, %lld }\n" : " { %lld-%lld }",
209                    ei->value.integer64.min,
210                    ei->value.integer64.max,
211                    ei->value.integer64.step);
212             break;
213         case SNDRV_CTL_ELEM_TYPE_ENUMERATED: {
214             unsigned m;
215             printf(" { %s=0", mixer->ctl[n].ename[0]);
216             for (m = 1; m < ei->value.enumerated.items; m++)
217                 printf(", %s=%d", mixer->ctl[n].ename[m],m);
218             printf(" }");
219             break;
220         }
221         }
222         printf("\n");
223     }
224 }
225 
mixer_get_control(struct mixer * mixer,const char * name,unsigned index)226 struct mixer_ctl *mixer_get_control(struct mixer *mixer,
227                                     const char *name, unsigned index)
228 {
229     unsigned n;
230     for (n = 0; n < mixer->count; n++) {
231         if (mixer->info[n].id.index == index) {
232             if (!strcmp(name, (char*) mixer->info[n].id.name)) {
233                 return mixer->ctl + n;
234             }
235         }
236     }
237     return 0;
238 }
239 
mixer_get_nth_control(struct mixer * mixer,unsigned n)240 struct mixer_ctl *mixer_get_nth_control(struct mixer *mixer, unsigned n)
241 {
242     if (n < mixer->count)
243         return mixer->ctl + n;
244     return 0;
245 }
246 
mixer_ctl_print(struct mixer_ctl * ctl)247 void mixer_ctl_print(struct mixer_ctl *ctl)
248 {
249     struct snd_ctl_elem_value ev;
250     unsigned n;
251 
252     memset(&ev, 0, sizeof(ev));
253     ev.id.numid = ctl->info->id.numid;
254     if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev))
255         return;
256     printf("%s:", ctl->info->id.name);
257 
258     switch (ctl->info->type) {
259     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
260         for (n = 0; n < ctl->info->count; n++)
261             printf(" %s", ev.value.integer.value[n] ? "ON" : "OFF");
262         break;
263     case SNDRV_CTL_ELEM_TYPE_INTEGER: {
264         for (n = 0; n < ctl->info->count; n++)
265             printf(" %ld", ev.value.integer.value[n]);
266         break;
267     }
268     case SNDRV_CTL_ELEM_TYPE_INTEGER64:
269         for (n = 0; n < ctl->info->count; n++)
270             printf(" %lld", ev.value.integer64.value[n]);
271         break;
272     case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
273         for (n = 0; n < ctl->info->count; n++) {
274             unsigned v = ev.value.enumerated.item[n];
275             printf(" %d (%s)", v,
276                    (v < ctl->info->value.enumerated.items) ? ctl->ename[v] : "???");
277         }
278         break;
279     default:
280         printf(" ???");
281     }
282     printf("\n");
283 }
284 
scale_int(struct snd_ctl_elem_info * ei,unsigned _percent)285 static long scale_int(struct snd_ctl_elem_info *ei, unsigned _percent)
286 {
287     long percent;
288     long range;
289 
290     if (_percent > 100)
291         percent = 100;
292     else
293         percent = (long) _percent;
294 
295     range = (ei->value.integer.max - ei->value.integer.min);
296 
297     return ei->value.integer.min + (range * percent) / 100LL;
298 }
299 
scale_int64(struct snd_ctl_elem_info * ei,unsigned _percent)300 static long long scale_int64(struct snd_ctl_elem_info *ei, unsigned _percent)
301 {
302     long long percent;
303     long long range;
304 
305     if (_percent > 100)
306         percent = 100;
307     else
308         percent = (long) _percent;
309 
310     range = (ei->value.integer.max - ei->value.integer.min) * 100LL;
311 
312     return ei->value.integer.min + (range / percent);
313 }
314 
mixer_ctl_set(struct mixer_ctl * ctl,unsigned percent)315 int mixer_ctl_set(struct mixer_ctl *ctl, unsigned percent)
316 {
317     struct snd_ctl_elem_value ev;
318     unsigned n;
319 
320     memset(&ev, 0, sizeof(ev));
321     ev.id.numid = ctl->info->id.numid;
322     switch (ctl->info->type) {
323     case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
324         for (n = 0; n < ctl->info->count; n++)
325             ev.value.integer.value[n] = !!percent;
326         break;
327     case SNDRV_CTL_ELEM_TYPE_INTEGER: {
328         long value = scale_int(ctl->info, percent);
329         for (n = 0; n < ctl->info->count; n++)
330             ev.value.integer.value[n] = value;
331         break;
332     }
333     case SNDRV_CTL_ELEM_TYPE_INTEGER64: {
334         long long value = scale_int64(ctl->info, percent);
335         for (n = 0; n < ctl->info->count; n++)
336             ev.value.integer64.value[n] = value;
337         break;
338     }
339     default:
340         errno = EINVAL;
341         return -1;
342     }
343 
344     return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
345 }
346 
mixer_ctl_select(struct mixer_ctl * ctl,const char * value)347 int mixer_ctl_select(struct mixer_ctl *ctl, const char *value)
348 {
349     unsigned n, max;
350     struct snd_ctl_elem_value ev;
351 
352     if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
353         errno = EINVAL;
354         return -1;
355     }
356 
357     max = ctl->info->value.enumerated.items;
358     for (n = 0; n < max; n++) {
359         if (!strcmp(value, ctl->ename[n])) {
360             memset(&ev, 0, sizeof(ev));
361             ev.value.enumerated.item[0] = n;
362             ev.id.numid = ctl->info->id.numid;
363             if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0)
364                 return -1;
365             return 0;
366         }
367     }
368 
369     errno = EINVAL;
370     return -1;
371 }
372