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