2 * \file control/namehint.c
3 * \brief Give device name hints
4 * \author Jaroslav Kysela <perex@perex.cz>
8 * Give device name hints - main file
9 * Copyright (c) 2006 by Jaroslav Kysela <perex@perex.cz>
12 * This library is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License as
14 * published by the Free Software Foundation; either version 2.1 of
15 * the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 unsigned int allocated;
36 snd_ctl_elem_iface_t iface;
38 snd_ctl_card_info_t *info;
49 static int hint_list_add(struct hint_list *list,
51 const char *description)
55 if (list->count == list->allocated) {
56 char **n = realloc(list->list, (list->allocated + 10) * sizeof(char *));
59 list->allocated += 10;
65 x = malloc(4 + strlen(name) + (description != NULL ? (4 + strlen(description) + 1) : 0) + 1);
70 if (description != NULL) {
72 strcat(x, description);
75 list->list[list->count++] = x;
79 static void zero_handler(const char *file ATTRIBUTE_UNUSED,
80 int line ATTRIBUTE_UNUSED,
81 const char *function ATTRIBUTE_UNUSED,
82 int err ATTRIBUTE_UNUSED,
83 const char *fmt ATTRIBUTE_UNUSED, ...)
87 static int get_dev_name1(struct hint_list *list, char **res)
92 switch (list->iface) {
94 case SND_CTL_ELEM_IFACE_HWDEP:
96 snd_hwdep_info_t *info;
97 snd_hwdep_info_alloca(&info);
98 snd_hwdep_info_set_device(info, list->device);
99 if (snd_ctl_hwdep_info(list->ctl, info) < 0)
101 *res = strdup(snd_hwdep_info_get_name(info));
106 case SND_CTL_ELEM_IFACE_PCM:
108 snd_pcm_info_t *info;
109 snd_pcm_info_alloca(&info);
110 snd_pcm_info_set_device(info, list->device);
111 snd_pcm_info_set_stream(info, list->stream ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK);
112 if (snd_ctl_pcm_info(list->ctl, info) < 0)
114 switch (snd_pcm_info_get_class(info)) {
115 case SND_PCM_CLASS_MODEM:
116 case SND_PCM_CLASS_DIGITIZER:
121 *res = strdup(snd_pcm_info_get_name(info));
126 case SND_CTL_ELEM_IFACE_RAWMIDI:
128 snd_rawmidi_info_t *info;
129 snd_rawmidi_info_alloca(&info);
130 snd_rawmidi_info_set_device(info, list->device);
131 snd_rawmidi_info_set_stream(info, list->stream ? SND_RAWMIDI_STREAM_INPUT : SND_RAWMIDI_STREAM_OUTPUT);
132 if (snd_ctl_rawmidi_info(list->ctl, info) < 0)
134 *res = strdup(snd_rawmidi_info_get_name(info));
143 static char *get_dev_name(struct hint_list *list)
145 char *str1, *str2, *res;
147 list->device = list->device_input >= 0 ? list->device_input : list->device;
149 if (get_dev_name1(list, &str1) < 0)
151 list->device = list->device_output >= 0 ? list->device_input : list->device;
153 if (get_dev_name1(list, &str2) < 0) {
158 if (str1 != NULL || str2 != NULL) {
159 if (str1 != NULL && str2 != NULL) {
160 if (strcmp(str1, str2) == 0) {
161 res = malloc(strlen(list->cardname) + strlen(str2) + 3);
163 strcpy(res, list->cardname);
168 res = malloc(strlen(list->cardname) + strlen(str2) + strlen(str1) + 6);
170 strcpy(res, list->cardname);
187 res = malloc(strlen(list->cardname) + strlen(str1) + 19);
192 strcpy(res, list->cardname);
195 strcat(res, "|IOID");
201 return strdup(list->cardname);
210 static int try_config(struct hint_list *list,
214 snd_lib_error_handler_t eh;
215 snd_config_t *res = NULL, *cfg, *cfg1, *n;
216 snd_config_iterator_t i, next;
217 char *buf, *buf1 = NULL, *buf2;
220 long dev = list->device;
222 list->device_input = -1;
223 list->device_output = -1;
224 buf = malloc(BUF_SIZE);
227 sprintf(buf, "%s.%s", base, name);
228 /* look for redirection */
229 if (snd_config_search(snd_config, buf, &cfg) >= 0 &&
230 snd_config_get_string(cfg, &str) >= 0 &&
231 ((strncmp(base, str, strlen(base)) == 0 &&
232 str[strlen(base)] == '.') || strchr(str, '.') == NULL))
234 if (list->card >= 0 && list->device >= 0)
235 sprintf(buf, "%s:CARD=%s,DEV=%i", name, snd_ctl_card_info_get_id(list->info), list->device);
236 else if (list->card >= 0)
237 sprintf(buf, "%s:CARD=%s", name, snd_ctl_card_info_get_id(list->info));
241 snd_lib_error_set_handler(&zero_handler);
242 err = snd_config_search_definition(snd_config, base, buf, &res);
243 snd_lib_error_set_handler(eh);
247 if (snd_config_get_type(res) != SND_CONFIG_TYPE_COMPOUND)
249 if (snd_config_search(res, "type", NULL) < 0)
252 #if 0 /* for debug purposes */
255 fprintf(stderr, "********* PCM '%s':\n", buf);
256 snd_output_stdio_attach(&out, stderr, 0);
257 snd_config_save(res, out);
258 snd_output_close(out);
259 fprintf(stderr, "\n");
267 if (snd_config_search(cfg1, "type", &cfg) >= 0 &&
268 snd_config_get_string(cfg, &str) >= 0 &&
269 strcmp(str, "hw") == 0) {
271 list->device_input = -1;
272 list->device_output = -1;
273 if (snd_config_search(cfg1, "device", &cfg) >= 0) {
274 if (snd_config_get_integer(cfg, &dev) < 0) {
275 SNDERR("(%s) device must be an integer", buf);
282 if (snd_config_search(cfg1, "hint", &cfg) >= 0) {
283 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
284 SNDERR("hint (%s) must be a compound", buf);
289 snd_config_search(cfg, "show", &n) >= 0 &&
290 snd_config_get_bool(n) <= 0)
293 snd_config_search(cfg, "description", &n) >= 0 &&
294 snd_config_get_string(n, &str) >= 0) {
301 if (snd_config_search(cfg, "device", &n) >= 0) {
302 if (snd_config_get_integer(n, &dev) < 0) {
303 SNDERR("(%s) device must be an integer", buf);
307 list->device_input = -1;
308 list->device_output = -1;
310 if (snd_config_search(cfg, "device_input", &n) >= 0) {
311 if (snd_config_get_integer(n, &list->device_input) < 0) {
312 SNDERR("(%s) device_input must be an integer", buf);
316 list->device_output = -1;
318 if (snd_config_search(cfg, "device_output", &n) >= 0) {
319 if (snd_config_get_integer(n, &list->device_output) < 0) {
320 SNDERR("(%s) device_output must be an integer", buf);
325 } else if (level == 1 && !list->show_all)
327 if (snd_config_search(cfg1, "slave", &cfg) >= 0 &&
328 snd_config_search(cfg, base, &cfg1) >= 0)
330 snd_config_delete(res);
332 if (strchr(buf, ':') != NULL)
334 /* find, if all parameters have a default, */
335 /* otherwise filter this definition */
337 snd_lib_error_set_handler(&zero_handler);
338 err = snd_config_search_alias_hooks(snd_config, base, buf, &res);
339 snd_lib_error_set_handler(eh);
342 if (snd_config_search(res, "@args", &cfg) >= 0) {
343 snd_config_for_each(i, next, cfg) {
344 if (snd_config_search(snd_config_iterator_entry(i),
345 "default", NULL) < 0) {
356 str = list->card >= 0 ? get_dev_name(list) : NULL;
358 level = (buf1 == NULL ? 0 : strlen(buf1)) + 1 + strlen(str);
359 buf2 = realloc((char *)str, level + 1);
362 str = strchr(buf2, '|');
364 memmove(buf2 + (level - strlen(str)), str, strlen(str));
366 str = buf2 + strlen(buf2);
367 *(char *)str++ = '\n';
368 memcpy((char *)str, buf1, strlen(buf1));
376 } else if (list->device >= 0)
378 err = hint_list_add(list, buf, buf1);
382 snd_config_delete(res);
390 #define IFACE(v, fcn) [SND_CTL_ELEM_IFACE_##v] = (next_devices_t)fcn
392 typedef int (*next_devices_t)(snd_ctl_t *, int *);
394 static next_devices_t next_devices[] = {
396 IFACE(HWDEP, snd_ctl_hwdep_next_device),
398 IFACE(PCM, snd_ctl_pcm_next_device),
399 IFACE(RAWMIDI, snd_ctl_rawmidi_next_device),
401 IFACE(SEQUENCER, NULL)
405 static int add_card(struct hint_list *list, int card)
408 snd_config_t *conf, *n;
409 snd_config_iterator_t i, next;
412 snd_ctl_card_info_t *info;
414 snd_ctl_card_info_alloca(&info);
416 err = snd_config_search(snd_config, list->siface, &conf);
419 sprintf(ctl_name, "hw:%i", card);
420 err = snd_ctl_open(&list->ctl, ctl_name, 0);
423 err = snd_ctl_card_info(list->ctl, info);
426 snd_config_for_each(i, next, conf) {
427 n = snd_config_iterator_entry(i);
428 if (snd_config_get_id(n, &str) < 0)
430 if (next_devices[list->iface] != NULL) {
433 err = next_devices[list->iface](list->ctl, &list->device);
434 if (list->device < 0)
437 while (err >= 0 && list->device >= 0) {
438 err = try_config(list, list->siface, str);
441 err = next_devices[list->iface](list->ctl, &list->device);
453 err = try_config(list, list->siface, str);
457 err = try_config(list, list->siface, str);
464 snd_ctl_close(list->ctl);
468 static int get_card_name(struct hint_list *list, int card)
473 err = snd_card_get_name(card, &list->cardname);
476 sprintf(scard, " #%i", card);
477 s = realloc(list->cardname, strlen(list->cardname) + strlen(scard) + 1);
485 * \brief Return string list with device name hints.
486 * \param card Card number or -1 (means all cards)
487 * \param iface Interface identification (like "pcm", "rawmidi", "timer", "seq")
488 * \param hints Result - array of string with device name hints
489 * \result zero if success, otherwise a negative error code
491 * Note: The device description is separated with '|' char.
493 * User defined hints are gathered from namehint.IFACE tree like:
497 * myfile "file:FILE=/tmp/soundwave.raw|Save sound output to /tmp/soundwave.raw"<br>
498 * myplug "plug:front:Do all conversions for front speakers"<br>
502 * Special variables: defaults.namehint.showall specifies if all device
503 * definitions are accepted (boolean type).
505 int snd_device_name_hint(int card, const char *iface, void ***hints)
507 struct hint_list list;
511 snd_config_iterator_t i, next;
516 err = snd_config_update();
520 list.count = list.allocated = 0;
522 if (strcmp(iface, "card") == 0)
523 list.iface = SND_CTL_ELEM_IFACE_CARD;
524 else if (strcmp(iface, "pcm") == 0)
525 list.iface = SND_CTL_ELEM_IFACE_PCM;
526 else if (strcmp(iface, "rawmidi") == 0)
527 list.iface = SND_CTL_ELEM_IFACE_RAWMIDI;
528 else if (strcmp(iface, "timer") == 0)
529 list.iface = SND_CTL_ELEM_IFACE_TIMER;
530 else if (strcmp(iface, "seq") == 0)
531 list.iface = SND_CTL_ELEM_IFACE_SEQUENCER;
532 else if (strcmp(iface, "hwdep") == 0)
533 list.iface = SND_CTL_ELEM_IFACE_HWDEP;
537 list.cardname = NULL;
538 if (snd_config_search(snd_config, "defaults.namehint.showall", &conf) >= 0)
539 list.show_all = snd_config_get_bool(conf) > 0;
541 err = get_card_name(&list, card);
543 err = add_card(&list, card);
545 err = snd_card_next(&card);
549 err = get_card_name(&list, card);
552 err = add_card(&list, card);
555 err = snd_card_next(&card);
560 sprintf(ehints, "namehint.%s", list.siface);
561 err = snd_config_search(snd_config, ehints, &conf);
563 snd_config_for_each(i, next, conf) {
564 if (snd_config_get_string(snd_config_iterator_entry(i),
567 err = hint_list_add(&list, str, NULL);
575 snd_device_name_free_hint((void **)list.list);
580 err = hint_list_add(&list, NULL, NULL);
583 *hints = (void **)list.list;
591 * \brief Free a string list with device name hints.
592 * \param hints A string list to free
593 * \result zero if success, otherwise a negative error code
595 int snd_device_name_free_hint(void **hints)
611 * \brief Get a hint Free a string list with device name hints.
612 * \param hint A pointer to hint
613 * \param id Hint ID (see bellow)
614 * \result an allocated ASCII string if success, otherwise NULL
617 * NAME - name of device
618 * DESC - description of device
619 * IOID - input / output identification (Input or Output strings),
620 * not present (NULL) means both
622 char *snd_device_name_get_hint(const void *hint, const char *id)
624 const char *hint1 = (const char *)hint, *delim;
630 while (*hint1 != '\0') {
631 delim = strchr(hint1, '|');
632 if (memcmp(id, hint1, 4) != 0) {
639 return strdup(hint1 + 4);
640 size = delim - hint1 - 4;
641 res = malloc(size + 1);
643 memcpy(res, hint1 + 4, size);