OSDN Git Service

Changed Jaroslav Kysela's e-mail from perex@suse.cz to perex@perex.cz
[android-x86/external-alsa-lib.git] / src / control / setup.c
1 /**
2  * \file control/setup.c
3  * \brief Routines to setup control primitives from configuration
4  * \author Abramo Bagnara <abramo@alsa-project.org>
5  * \author Jaroslav Kysela <perex@perex.cz>
6  * \date 2001
7  *
8  * Routines to setup control primitives from configuration
9  */
10 /*
11  *  Control Interface - routines for setup from configuration
12  *  Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
13  *                        Jaroslav Kysela <perex@perex.cz>
14  *
15  *
16  *   This library is free software; you can redistribute it and/or modify
17  *   it under the terms of the GNU Lesser General Public License as
18  *   published by the Free Software Foundation; either version 2.1 of
19  *   the License, or (at your option) any later version.
20  *
21  *   This program is distributed in the hope that it will be useful,
22  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
23  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  *   GNU Lesser General Public License for more details.
25  *
26  *   You should have received a copy of the GNU Lesser General Public
27  *   License along with this library; if not, write to the Free Software
28  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
29  *
30  */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include "local.h"
39
40 #ifndef DOC_HIDDEN
41 typedef struct {
42         unsigned int lock: 1;
43         unsigned int preserve: 1;
44         snd_ctl_elem_id_t *id;
45         snd_ctl_elem_info_t *info;
46         snd_ctl_elem_value_t *val;
47         snd_ctl_elem_value_t *mask;
48         snd_ctl_elem_value_t *old;
49         struct list_head list;
50 } snd_sctl_elem_t;
51
52 struct _snd_sctl {
53         int mode;
54         snd_ctl_t *ctl;
55         struct list_head elems;
56 };
57 #endif /* DOC_HIDDEN */
58
59 static int free_elems(snd_sctl_t *h)
60 {
61         int err = 0;
62         while (!list_empty(&h->elems)) {
63                 snd_sctl_elem_t *elem = list_entry(h->elems.next, snd_sctl_elem_t, list);
64                 snd_ctl_elem_id_free(elem->id);
65                 snd_ctl_elem_info_free(elem->info);
66                 snd_ctl_elem_value_free(elem->val);
67                 snd_ctl_elem_value_free(elem->mask);
68                 snd_ctl_elem_value_free(elem->old);
69                 list_del(&elem->list);
70                 free(elem);
71         }
72         if ((h->mode & SND_SCTL_NOFREE) == 0)
73                 err = snd_ctl_close(h->ctl);
74         free(h);
75         return err;
76 }
77
78 /**
79  * \brief Install given values to control elements
80  * \param h Setup control handle
81  * \result zero if success, otherwise a negative error code
82  */
83 int snd_sctl_install(snd_sctl_t *h)
84 {
85         struct list_head *pos;
86         int err;
87         unsigned int k;
88         assert(h);
89         list_for_each(pos, &h->elems) {
90                 snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list);
91                 unsigned int count;
92                 snd_ctl_elem_type_t type;
93                 if (elem->lock) {
94                         err = snd_ctl_elem_lock(h->ctl, elem->id);
95                         if (err < 0) {
96                                 SNDERR("Cannot lock ctl elem");
97                                 return err;
98                         }
99                 }
100                 err = snd_ctl_elem_read(h->ctl, elem->old);
101                 if (err < 0) {
102                         SNDERR("Cannot read ctl elem");
103                         return err;
104                 }
105                 count = snd_ctl_elem_info_get_count(elem->info);
106                 type = snd_ctl_elem_info_get_type(elem->info);
107                 switch (type) {
108                 case SND_CTL_ELEM_TYPE_BOOLEAN:
109                         for (k = 0; k < count; ++k) {
110                                 int old, val, mask;
111                                 old = snd_ctl_elem_value_get_boolean(elem->old, k);
112                                 mask = snd_ctl_elem_value_get_boolean(elem->mask, k);
113                                 old &= ~mask;
114                                 if (old) {
115                                         val = snd_ctl_elem_value_get_boolean(elem->val, k);
116                                         val |= old;
117                                         snd_ctl_elem_value_set_boolean(elem->val, k, val);
118                                 }
119                         }
120                         break;
121                 case SND_CTL_ELEM_TYPE_INTEGER:
122                         for (k = 0; k < count; ++k) {
123                                 long old, val, mask;
124                                 old = snd_ctl_elem_value_get_integer(elem->old, k);
125                                 mask = snd_ctl_elem_value_get_integer(elem->mask, k);
126                                 old &= ~mask;
127                                 if (old) {
128                                         val = snd_ctl_elem_value_get_integer(elem->val, k);
129                                         val |= old;
130                                         snd_ctl_elem_value_set_integer(elem->val, k, val);
131                                 }
132                         }
133                         break;
134                 case SND_CTL_ELEM_TYPE_ENUMERATED:
135                         for (k = 0; k < count; ++k) {
136                                 unsigned int old, val, mask;
137                                 old = snd_ctl_elem_value_get_enumerated(elem->old, k);
138                                 mask = snd_ctl_elem_value_get_enumerated(elem->mask, k);
139                                 old &= ~mask;
140                                 if (old) {
141                                         val = snd_ctl_elem_value_get_enumerated(elem->val, k);
142                                         val |= old;
143                                         snd_ctl_elem_value_set_enumerated(elem->val, k, val);
144                                 }
145                         }
146                         break;
147                 case SND_CTL_ELEM_TYPE_IEC958:
148                         count = sizeof(snd_aes_iec958_t);
149                         /* Fall through */
150                 case SND_CTL_ELEM_TYPE_BYTES:
151                         for (k = 0; k < count; ++k) {
152                                 unsigned char old, val, mask;
153                                 old = snd_ctl_elem_value_get_byte(elem->old, k);
154                                 mask = snd_ctl_elem_value_get_byte(elem->mask, k);
155                                 old &= ~mask;
156                                 if (old) {
157                                         val = snd_ctl_elem_value_get_byte(elem->val, k);
158                                         val |= old;
159                                         snd_ctl_elem_value_set_byte(elem->val, k, val);
160                                 }
161                         }
162                         break;
163                 default:
164                         assert(0);
165                         break;
166                 }
167                 err = snd_ctl_elem_write(h->ctl, elem->val);
168                 if (err < 0) {
169                         SNDERR("Cannot write ctl elem");
170                         return err;
171                 }
172         }
173         return 0;
174 }
175
176 /**
177  * \brief Remove (restore) previous values from control elements
178  * \param h Setup control handle
179  * \result zero if success, otherwise a negative error code
180  */
181 int snd_sctl_remove(snd_sctl_t *h)
182 {
183         struct list_head *pos;
184         int err;
185         assert(h);
186         list_for_each(pos, &h->elems) {
187                 snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list);
188                 if (elem->lock) {
189                         err = snd_ctl_elem_unlock(h->ctl, elem->id);
190                         if (err < 0) {
191                                 SNDERR("Cannot unlock ctl elem");
192                                 return err;
193                         }
194                 }
195                 if (elem->preserve) {
196                         err = snd_ctl_elem_write(h->ctl, elem->old);
197                         if (err < 0) {
198                                 SNDERR("Cannot restore ctl elem");
199                                 return err;
200                         }
201                 }
202         }
203         return 0;
204 }
205
206 static int snd_config_get_ctl_elem_enumerated(snd_config_t *n, snd_ctl_t *ctl,
207                                               snd_ctl_elem_info_t *info)
208 {
209         const char *str;
210         long val;
211         unsigned int idx, items;
212         switch (snd_config_get_type(n)) {
213         case SND_CONFIG_TYPE_INTEGER:
214                 snd_config_get_integer(n, &val);
215                 return val;
216         case SND_CONFIG_TYPE_STRING:
217                 snd_config_get_string(n, &str);
218                 break;
219         default:
220                 return -1;
221         }
222         items = snd_ctl_elem_info_get_items(info);
223         for (idx = 0; idx < items; idx++) {
224                 int err;
225                 snd_ctl_elem_info_set_item(info, idx);
226                 err = snd_ctl_elem_info(ctl, info);
227                 if (err < 0) {
228                         SNDERR("Cannot obtain info for CTL elem");
229                         return err;
230                 }
231                 if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
232                         return idx;
233         }
234         return -1;
235 }
236
237 static int snd_config_get_ctl_elem_value(snd_config_t *conf,
238                                          snd_ctl_t *ctl,
239                                          snd_ctl_elem_value_t *val,
240                                          snd_ctl_elem_value_t *mask,
241                                          snd_ctl_elem_info_t *info)
242 {
243         int err;
244         snd_config_iterator_t i, next;
245         snd_ctl_elem_id_t *id;
246         snd_ctl_elem_type_t type;
247         unsigned int count;
248         long v;
249         long idx;
250         snd_ctl_elem_id_alloca(&id);
251         snd_ctl_elem_value_get_id(val, id);
252         count = snd_ctl_elem_info_get_count(info);
253         type = snd_ctl_elem_info_get_type(info);
254         if (count == 1) {
255                 switch (type) {
256                 case SND_CTL_ELEM_TYPE_BOOLEAN:
257                         v = snd_config_get_bool(conf);
258                         if (v >= 0) {
259                                 snd_ctl_elem_value_set_boolean(val, 0, v);
260                                 if (mask)
261                                         snd_ctl_elem_value_set_boolean(mask, 0, 1);
262                                 return 0;
263                         }
264                         break;
265                 case SND_CTL_ELEM_TYPE_INTEGER:
266                         err = snd_config_get_integer(conf, &v);
267                         if (err == 0) {
268                                 snd_ctl_elem_value_set_integer(val, 0, v);
269                                 if (mask)
270                                         snd_ctl_elem_value_set_integer(mask, 0, ~0L);
271                                 return 0;
272                         }
273                         break;
274                 case SND_CTL_ELEM_TYPE_ENUMERATED:
275                         v = snd_config_get_ctl_elem_enumerated(conf, ctl, info);
276                         if (v >= 0) {
277                                 snd_ctl_elem_value_set_enumerated(val, 0, v);
278                                 if (mask)
279                                         snd_ctl_elem_value_set_enumerated(mask, 0, ~0);
280                                 return 0;
281                         }
282                         break;
283                 case SND_CTL_ELEM_TYPE_BYTES:
284                 case SND_CTL_ELEM_TYPE_IEC958:
285                         break;
286                 default:
287                         SNDERR("Unknown control type: %d", type);
288                         return -EINVAL;
289                 }
290         }
291         switch (type) {
292         case SND_CTL_ELEM_TYPE_IEC958:
293                 count = sizeof(snd_aes_iec958_t);
294                 /* Fall through */
295         case SND_CTL_ELEM_TYPE_BYTES:
296         {
297                 const char *buf;
298                 err = snd_config_get_string(conf, &buf);
299                 if (err >= 0) {
300                         int c1 = 0;
301                         unsigned int len = strlen(buf);
302                         unsigned int idx = 0;
303                         if (len % 2 != 0 || len > count * 2) {
304                         _bad_content:
305                                 SNDERR("bad value content\n");
306                                 return -EINVAL;
307                         }
308                         while (*buf) {
309                                 int c = *buf++;
310                                 if (c >= '0' && c <= '9')
311                                         c -= '0';
312                                 else if (c >= 'a' && c <= 'f')
313                                         c = c - 'a' + 10;
314                                 else if (c >= 'A' && c <= 'F')
315                                         c = c - 'A' + 10;
316                                 else {
317                                         goto _bad_content;
318                                 }
319                                 if (idx % 2 == 1) {
320                                         snd_ctl_elem_value_set_byte(val, idx / 2, c1 << 4 | c);
321                                         if (mask)
322                                                 snd_ctl_elem_value_set_byte(mask, idx / 2, 0xff);
323                                 } else
324                                         c1 = c;
325                                 idx++;
326                         }
327                         return 0;
328                 }
329         }
330         default:
331                 break;
332         }
333         if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) {
334                 SNDERR("bad value type");
335                 return -EINVAL;
336         }
337
338         snd_config_for_each(i, next, conf) {
339                 snd_config_t *n = snd_config_iterator_entry(i);
340                 const char *id;
341                 if (snd_config_get_id(n, &id) < 0)
342                         continue;
343                 err = safe_strtol(id, &idx);
344                 if (err < 0 || idx < 0 || (unsigned int) idx >= count) {
345                         SNDERR("bad value index");
346                         return -EINVAL;
347                 }
348                 switch (type) {
349                 case SND_CTL_ELEM_TYPE_BOOLEAN:
350                         v = snd_config_get_bool(n);
351                         if (v < 0)
352                                 goto _bad_content;
353                         snd_ctl_elem_value_set_boolean(val, idx, v);
354                         if (mask)
355                                 snd_ctl_elem_value_set_boolean(mask, idx, 1);
356                         break;
357                 case SND_CTL_ELEM_TYPE_INTEGER:
358                         err = snd_config_get_integer(n, &v);
359                         if (err < 0)
360                                 goto _bad_content;
361                         snd_ctl_elem_value_set_integer(val, idx, v);
362                         if (mask)
363                                 snd_ctl_elem_value_set_integer(mask, idx, ~0L);
364                         break;
365                 case SND_CTL_ELEM_TYPE_ENUMERATED:
366                         v = snd_config_get_ctl_elem_enumerated(n, ctl, info);
367                         if (v < 0)
368                                 goto _bad_content;
369                         snd_ctl_elem_value_set_enumerated(val, idx, v);
370                         if (mask)
371                                 snd_ctl_elem_value_set_enumerated(mask, idx, ~0);
372                         break;
373                 case SND_CTL_ELEM_TYPE_BYTES:
374                 case SND_CTL_ELEM_TYPE_IEC958:
375                         err = snd_config_get_integer(n, &v);
376                         if (err < 0 || v < 0 || v > 255)
377                                 goto _bad_content;
378                         snd_ctl_elem_value_set_byte(val, idx, v);
379                         if (mask)
380                                 snd_ctl_elem_value_set_byte(mask, idx, 0xff);
381                         break;
382                 default:
383                         break;
384                 }
385         }
386         return 0;
387 }
388
389 static int add_elem(snd_sctl_t *h, snd_config_t *_conf, snd_config_t *private_data)
390 {
391         snd_config_t *conf;
392         snd_config_iterator_t i, next;
393         char *tmp;
394         int iface = SND_CTL_ELEM_IFACE_MIXER;
395         const char *name = NULL;
396         long index = 0;
397         long device = -1;
398         long subdevice = -1;
399         int lock = 0;
400         int preserve = 0;
401         int optional = 0;
402         snd_config_t *value = NULL, *mask = NULL;
403         snd_sctl_elem_t *elem = NULL;
404         int err;
405         err = snd_config_expand(_conf, _conf, NULL, private_data, &conf);
406         if (err < 0)
407                 return err;
408         snd_config_for_each(i, next, conf) {
409                 snd_config_t *n = snd_config_iterator_entry(i);
410                 const char *id;
411                 if (snd_config_get_id(n, &id) < 0)
412                         continue;
413                 if (strcmp(id, "comment") == 0)
414                         continue;
415                 if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
416                         const char *ptr;
417                         if ((err = snd_config_get_string(n, &ptr)) < 0) {
418                                 SNDERR("field %s is not a string", id);
419                                 goto _err;
420                         }
421                         if ((err = snd_config_get_ctl_iface_ascii(ptr)) < 0) {
422                                 SNDERR("Invalid value for '%s'", id);
423                                 goto _err;
424                         }
425                         iface = err;
426                         continue;
427                 }
428                 if (strcmp(id, "name") == 0) {
429                         if ((err = snd_config_get_string(n, &name)) < 0) {
430                                 SNDERR("field %s is not a string", id);
431                                 goto _err;
432                         }
433                         continue;
434                 }
435                 if (strcmp(id, "index") == 0) {
436                         if ((err = snd_config_get_integer(n, &index)) < 0) {
437                                 SNDERR("field %s is not an integer", id);
438                                 goto _err;
439                         }
440                         continue;
441                 }
442                 if (strcmp(id, "device") == 0) {
443                         if ((err = snd_config_get_integer(n, &device)) < 0) {
444                                 SNDERR("field %s is not an integer", id);
445                                 goto _err;
446                         }
447                         continue;
448                 }
449                 if (strcmp(id, "subdevice") == 0) {
450                         if ((err = snd_config_get_integer(n, &subdevice)) < 0) {
451                                 SNDERR("field %s is not an integer", id);
452                                 goto _err;
453                         }
454                         continue;
455                 }
456                 if (strcmp(id, "lock") == 0) {
457                         if ((err = snd_config_get_ascii(n, &tmp)) < 0) {
458                                 SNDERR("field %s has an invalid type", id);
459                                 goto _err;
460                         }
461                         err = snd_config_get_bool_ascii(tmp);
462                         if (err < 0) {
463                                 SNDERR("field %s is not a boolean", id);
464                                 free(tmp);
465                                 goto _err;
466                         }
467                         lock = err;
468                         free(tmp);
469                         continue;
470                 }
471                 if (strcmp(id, "preserve") == 0) {
472                         if ((err = snd_config_get_ascii(n, &tmp)) < 0) {
473                                 SNDERR("field %s has an invalid type", id);
474                                 goto _err;
475                         }
476                         err = snd_config_get_bool_ascii(tmp);
477                         if (err < 0) {
478                                 SNDERR("field %s is not a boolean", id);
479                                 free(tmp);
480                                 goto _err;
481                         }
482                         preserve = err;
483                         free(tmp);
484                         continue;
485                 }
486                 if (strcmp(id, "value") == 0) {
487                         value = n;
488                         continue;
489                 }
490                 if (strcmp(id, "mask") == 0) {
491                         mask = n;
492                         continue;
493                 }
494                 if (strcmp(id, "optional") == 0) {
495                         if ((err = snd_config_get_ascii(n, &tmp)) < 0) {
496                                 SNDERR("field %s has an invalid type", id);
497                                 goto _err;
498                         }
499                         err = snd_config_get_bool_ascii(tmp);
500                         if (err < 0) {
501                                 SNDERR("field %s is not a boolean", id);
502                                 free(tmp);
503                                 goto _err;
504                         }
505                         optional = err;
506                         free(tmp);
507                         continue;
508                 }
509                 SNDERR("Unknown field %s", id);
510                 return -EINVAL;
511         }
512         if (name == NULL) {
513                 SNDERR("Missing control name");
514                 err = -EINVAL;
515                 goto _err;
516         }
517         if (value == NULL) {
518                 SNDERR("Missing control value");
519                 err = -EINVAL;
520                 goto _err;
521         }
522         if (device < 0)
523                 device = 0;
524         if (subdevice < 0)
525                 subdevice = 0;
526         elem = calloc(1, sizeof(*elem));
527         if (!elem)
528                 return -ENOMEM;
529         err = snd_ctl_elem_id_malloc(&elem->id);
530         if (err < 0)
531                 goto _err;
532         err = snd_ctl_elem_info_malloc(&elem->info);
533         if (err < 0)
534                 goto _err;
535         err = snd_ctl_elem_value_malloc(&elem->val);
536         if (err < 0)
537                 goto _err;
538         err = snd_ctl_elem_value_malloc(&elem->mask);
539         if (err < 0)
540                 goto _err;
541         err = snd_ctl_elem_value_malloc(&elem->old);
542         if (err < 0)
543                 goto _err;
544         elem->lock = lock;
545         elem->preserve = preserve;
546         snd_ctl_elem_id_set_interface(elem->id, iface);
547         snd_ctl_elem_id_set_name(elem->id, name);
548         snd_ctl_elem_id_set_index(elem->id, index);
549         snd_ctl_elem_id_set_device(elem->id, device);
550         snd_ctl_elem_id_set_subdevice(elem->id, subdevice);
551         snd_ctl_elem_info_set_id(elem->info, elem->id);
552         err = snd_ctl_elem_info(h->ctl, elem->info);
553         if (err < 0) {
554                 if (! optional)
555                         SNDERR("Cannot obtain info for CTL elem (%s,'%s',%li,%li,%li): %s", snd_ctl_elem_iface_name(iface), name, index, device, subdevice, snd_strerror(err));
556                 goto _err;
557         }
558         snd_ctl_elem_value_set_id(elem->val, elem->id);
559         snd_ctl_elem_value_set_id(elem->old, elem->id);
560         if (mask) {
561                 err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, NULL, elem->info);
562                 if (err < 0)
563                         goto _err;
564                 err = snd_config_get_ctl_elem_value(mask, h->ctl, elem->mask, NULL, elem->info);
565                 if (err < 0)
566                         goto _err;
567         } else {
568                 err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
569                 if (err < 0)
570                         goto _err;
571         }
572                 
573         err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info);
574         if (err < 0)
575                 goto _err;
576         list_add_tail(&elem->list, &h->elems);
577
578  _err:
579         if (err < 0 && elem) {
580                 if (elem->id)
581                         snd_ctl_elem_id_free(elem->id);
582                 if (elem->info)
583                         snd_ctl_elem_info_free(elem->info);
584                 if (elem->val)
585                         snd_ctl_elem_value_free(elem->val);
586                 if (elem->mask)
587                         snd_ctl_elem_value_free(elem->mask);
588                 if (elem->old)
589                         snd_ctl_elem_value_free(elem->old);
590                 free(elem);
591                 if (err != -ENOMEM && optional)
592                         err = 0; /* ignore the error */
593         }
594         if (conf)
595                 snd_config_delete(conf);
596         return err;
597 }
598
599 /**
600  * \brief Build setup control handle
601  * \param sctl Result - setup control handle
602  * \param handle Master control handle
603  * \param conf Setup configuration
604  * \param private_data Private data for runtime evaluation
605  * \param mode Build mode - SND_SCTL_xxxx
606  * \result zero if success, otherwise a negative error code
607  */
608 int snd_sctl_build(snd_sctl_t **sctl, snd_ctl_t *handle, snd_config_t *conf, snd_config_t *private_data, int mode)
609 {
610         snd_sctl_t *h;
611         snd_config_iterator_t i, next;
612         int err;
613
614         assert(sctl);
615         assert(handle);
616         assert(conf);
617         *sctl = NULL;
618         if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
619                 return -EINVAL;
620         h = calloc(1, sizeof(*h));
621         if (!h) {
622                 if (mode & SND_SCTL_NOFREE)
623                         return -ENOMEM;
624                 snd_ctl_close(handle);
625                 return -ENOMEM;
626         }
627         h->mode = mode;
628         h->ctl = handle;
629         INIT_LIST_HEAD(&h->elems);
630         snd_config_for_each(i, next, conf) {
631                 snd_config_t *n = snd_config_iterator_entry(i);
632                 err = add_elem(h, n, private_data);
633                 if (err < 0) {
634                         free_elems(h);
635                         return err;
636                 }
637         }
638         *sctl = h;
639         return 0;
640 }
641
642 /**
643  * \brief Free setup control handle
644  * \param sctl Setup control handle
645  * \result zero if success, otherwise a negative error code
646  */
647 int snd_sctl_free(snd_sctl_t *sctl)
648 {
649         assert(sctl);
650         return free_elems(sctl);
651 }