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, int device,
93 switch (list->iface) {
95 case SND_CTL_ELEM_IFACE_HWDEP:
97 snd_hwdep_info_t *info;
98 snd_hwdep_info_alloca(&info);
99 snd_hwdep_info_set_device(info, device);
100 if (snd_ctl_hwdep_info(list->ctl, info) < 0)
102 *res = strdup(snd_hwdep_info_get_name(info));
107 case SND_CTL_ELEM_IFACE_PCM:
109 snd_pcm_info_t *info;
110 snd_pcm_info_alloca(&info);
111 snd_pcm_info_set_device(info, device);
112 snd_pcm_info_set_stream(info, stream ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK);
113 if (snd_ctl_pcm_info(list->ctl, info) < 0)
115 switch (snd_pcm_info_get_class(info)) {
116 case SND_PCM_CLASS_MODEM:
117 case SND_PCM_CLASS_DIGITIZER:
122 *res = strdup(snd_pcm_info_get_name(info));
127 case SND_CTL_ELEM_IFACE_RAWMIDI:
129 snd_rawmidi_info_t *info;
130 snd_rawmidi_info_alloca(&info);
131 snd_rawmidi_info_set_device(info, device);
132 snd_rawmidi_info_set_stream(info, stream ? SND_RAWMIDI_STREAM_INPUT : SND_RAWMIDI_STREAM_OUTPUT);
133 if (snd_ctl_rawmidi_info(list->ctl, info) < 0)
135 *res = strdup(snd_rawmidi_info_get_name(info));
144 static char *get_dev_name(struct hint_list *list)
146 char *str1, *str2, *res;
149 device = list->device_input >= 0 ? list->device_input : list->device;
150 if (get_dev_name1(list, &str1, device, 1) < 0)
152 device = list->device_output >= 0 ? list->device_output : list->device;
153 if (get_dev_name1(list, &str2, device, 0) < 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 /* if the specified device doesn't exist, skip this entry */
202 if (list->device >= 0 || list->device_input >= 0 || list->device_output >= 0)
204 return strdup(list->cardname);
211 static int try_config(struct hint_list *list,
215 snd_lib_error_handler_t eh;
216 snd_config_t *res = NULL, *cfg, *cfg1, *n;
217 snd_config_iterator_t i, next;
218 char *buf, *buf1 = NULL, *buf2;
221 long dev = list->device;
224 list->device_input = -1;
225 list->device_output = -1;
226 buf = malloc(BUF_SIZE);
229 sprintf(buf, "%s.%s", base, name);
230 /* look for redirection */
231 if (snd_config_search(snd_config, buf, &cfg) >= 0 &&
232 snd_config_get_string(cfg, &str) >= 0 &&
233 ((strncmp(base, str, strlen(base)) == 0 &&
234 str[strlen(base)] == '.') || strchr(str, '.') == NULL))
236 if (list->card >= 0 && list->device >= 0)
237 sprintf(buf, "%s:CARD=%s,DEV=%i", name, snd_ctl_card_info_get_id(list->info), list->device);
238 else if (list->card >= 0)
239 sprintf(buf, "%s:CARD=%s", name, snd_ctl_card_info_get_id(list->info));
243 snd_lib_error_set_handler(&zero_handler);
244 err = snd_config_search_definition(snd_config, base, buf, &res);
245 snd_lib_error_set_handler(eh);
250 if (snd_config_get_type(res) != SND_CONFIG_TYPE_COMPOUND)
252 if (snd_config_search(res, "type", NULL) < 0)
255 #if 0 /* for debug purposes */
258 fprintf(stderr, "********* PCM '%s':\n", buf);
259 snd_output_stdio_attach(&out, stderr, 0);
260 snd_config_save(res, out);
261 snd_output_close(out);
262 fprintf(stderr, "\n");
270 if (snd_config_search(cfg1, "type", &cfg) >= 0 &&
271 snd_config_get_string(cfg, &str) >= 0 &&
272 strcmp(str, "hw") == 0) {
274 list->device_input = -1;
275 list->device_output = -1;
276 if (snd_config_search(cfg1, "device", &cfg) >= 0) {
277 if (snd_config_get_integer(cfg, &dev) < 0) {
278 SNDERR("(%s) device must be an integer", buf);
285 if (snd_config_search(cfg1, "hint", &cfg) >= 0) {
286 if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
287 SNDERR("hint (%s) must be a compound", buf);
292 snd_config_search(cfg, "show", &n) >= 0 &&
293 snd_config_get_bool(n) <= 0)
296 snd_config_search(cfg, "description", &n) >= 0 &&
297 snd_config_get_string(n, &str) >= 0) {
304 if (snd_config_search(cfg, "device", &n) >= 0) {
305 if (snd_config_get_integer(n, &dev) < 0) {
306 SNDERR("(%s) device must be an integer", buf);
310 list->device_input = dev;
311 list->device_output = dev;
313 if (snd_config_search(cfg, "device_input", &n) >= 0) {
314 if (snd_config_get_integer(n, &list->device_input) < 0) {
315 SNDERR("(%s) device_input must be an integer", buf);
319 list->device_output = -1;
321 if (snd_config_search(cfg, "device_output", &n) >= 0) {
322 if (snd_config_get_integer(n, &list->device_output) < 0) {
323 SNDERR("(%s) device_output must be an integer", buf);
328 } else if (level == 1 && !list->show_all)
330 if (snd_config_search(cfg1, "slave", &cfg) >= 0 &&
331 snd_config_search(cfg, base, &cfg1) >= 0)
333 snd_config_delete(res);
336 if (strchr(buf, ':') != NULL)
338 /* find, if all parameters have a default, */
339 /* otherwise filter this definition */
341 snd_lib_error_set_handler(&zero_handler);
342 err = snd_config_search_alias_hooks(snd_config, base, buf, &res);
343 snd_lib_error_set_handler(eh);
346 if (snd_config_search(res, "@args", &cfg) >= 0) {
347 snd_config_for_each(i, next, cfg) {
348 if (snd_config_search(snd_config_iterator_entry(i),
349 "default", NULL) < 0) {
360 str = list->card >= 0 ? get_dev_name(list) : NULL;
362 level = (buf1 == NULL ? 0 : strlen(buf1)) + 1 + strlen(str);
363 buf2 = realloc((char *)str, level + 1);
366 str = strchr(buf2, '|');
368 memmove(buf2 + (level - strlen(str)), str, strlen(str));
370 str = buf2 + strlen(buf2);
371 *(char *)str++ = '\n';
372 memcpy((char *)str, buf1, strlen(buf1));
380 } else if (list->device >= 0)
382 err = hint_list_add(list, buf, buf1);
385 if (res && cleanup_res)
386 snd_config_delete(res);
394 #define IFACE(v, fcn) [SND_CTL_ELEM_IFACE_##v] = (next_devices_t)fcn
396 typedef int (*next_devices_t)(snd_ctl_t *, int *);
398 static const next_devices_t next_devices[] = {
400 IFACE(HWDEP, snd_ctl_hwdep_next_device),
402 IFACE(PCM, snd_ctl_pcm_next_device),
403 IFACE(RAWMIDI, snd_ctl_rawmidi_next_device),
405 IFACE(SEQUENCER, NULL)
409 static int add_card(struct hint_list *list, int card)
412 snd_config_t *conf, *n;
413 snd_config_iterator_t i, next;
416 snd_ctl_card_info_t *info;
417 int device, max_device = 0;
419 snd_ctl_card_info_alloca(&info);
421 err = snd_config_search(snd_config, list->siface, &conf);
424 sprintf(ctl_name, "hw:%i", card);
425 err = snd_ctl_open(&list->ctl, ctl_name, 0);
428 err = snd_ctl_card_info(list->ctl, info);
431 snd_config_for_each(i, next, conf) {
432 n = snd_config_iterator_entry(i);
433 if (snd_config_get_id(n, &str) < 0)
436 if (next_devices[list->iface] != NULL) {
438 device = max_device = -1;
439 err = next_devices[list->iface](list->ctl, &device);
444 while (err >= 0 && device >= 0) {
445 err = next_devices[list->iface](list->ctl, &device);
446 if (err >= 0 && device > max_device)
450 for (device = 0; err >= 0 && device <= max_device; device++) {
451 list->device = device;
452 err = try_config(list, list->siface, str);
467 err = try_config(list, list->siface, str);
474 snd_ctl_close(list->ctl);
478 static int get_card_name(struct hint_list *list, int card)
483 free(list->cardname);
484 list->cardname = NULL;
485 err = snd_card_get_name(card, &list->cardname);
488 sprintf(scard, " #%i", card);
489 s = realloc(list->cardname, strlen(list->cardname) + strlen(scard) + 1);
496 static int add_software_devices(struct hint_list *list)
499 snd_config_t *conf, *n;
500 snd_config_iterator_t i, next;
503 err = snd_config_search(snd_config, list->siface, &conf);
506 snd_config_for_each(i, next, conf) {
507 n = snd_config_iterator_entry(i);
508 if (snd_config_get_id(n, &str) < 0)
512 err = try_config(list, list->siface, str);
520 * \brief Get a set of device name hints
521 * \param card Card number or -1 (means all cards)
522 * \param iface Interface identification (like "pcm", "rawmidi", "timer", "seq")
523 * \param hints Result - array of device name hints
524 * \result zero if success, otherwise a negative error code
526 * hints will receive a NULL-terminated array of device name hints,
527 * which can be passed to #snd_device_name_get_hint to extract usable
528 * values. When no longer needed, hints should be passed to
529 * #snd_device_name_free_hint to release resources.
531 * User-defined hints are gathered from namehint.IFACE tree like:
535 * myfile "file:FILE=/tmp/soundwave.raw|Save sound output to /tmp/soundwave.raw"<br>
536 * myplug "plug:front:Do all conversions for front speakers"<br>
540 * Note: The device description is separated with '|' char.
542 * Special variables: defaults.namehint.showall specifies if all device
543 * definitions are accepted (boolean type).
545 int snd_device_name_hint(int card, const char *iface, void ***hints)
547 struct hint_list list;
551 snd_config_iterator_t i, next;
556 err = snd_config_update();
560 list.count = list.allocated = 0;
562 if (strcmp(iface, "card") == 0)
563 list.iface = SND_CTL_ELEM_IFACE_CARD;
564 else if (strcmp(iface, "pcm") == 0)
565 list.iface = SND_CTL_ELEM_IFACE_PCM;
566 else if (strcmp(iface, "rawmidi") == 0)
567 list.iface = SND_CTL_ELEM_IFACE_RAWMIDI;
568 else if (strcmp(iface, "timer") == 0)
569 list.iface = SND_CTL_ELEM_IFACE_TIMER;
570 else if (strcmp(iface, "seq") == 0)
571 list.iface = SND_CTL_ELEM_IFACE_SEQUENCER;
572 else if (strcmp(iface, "hwdep") == 0)
573 list.iface = SND_CTL_ELEM_IFACE_HWDEP;
574 else if (strcmp(iface, "ctl") == 0)
575 list.iface = SND_CTL_ELEM_IFACE_MIXER;
579 list.cardname = NULL;
580 if (snd_config_search(snd_config, "defaults.namehint.showall", &conf) >= 0)
581 list.show_all = snd_config_get_bool(conf) > 0;
583 err = get_card_name(&list, card);
585 err = add_card(&list, card);
587 add_software_devices(&list);
588 err = snd_card_next(&card);
592 err = get_card_name(&list, card);
595 err = add_card(&list, card);
598 err = snd_card_next(&card);
603 sprintf(ehints, "namehint.%s", list.siface);
604 err = snd_config_search(snd_config, ehints, &conf);
606 snd_config_for_each(i, next, conf) {
607 if (snd_config_get_string(snd_config_iterator_entry(i),
610 err = hint_list_add(&list, str, NULL);
618 snd_device_name_free_hint((void **)list.list);
623 err = hint_list_add(&list, NULL, NULL);
626 *hints = (void **)list.list;
634 * \brief Free a list of device name hints.
635 * \param hints List to free
636 * \result zero if success, otherwise a negative error code
638 int snd_device_name_free_hint(void **hints)
654 * \brief Extract a value from a hint
655 * \param hint A pointer to hint
656 * \param id Hint value to extract ("NAME", "DESC", or "IOID", see below)
657 * \result an allocated ASCII string if success, otherwise NULL
660 * NAME - name of device
661 * DESC - description of device
662 * IOID - input / output identification ("Input" or "Output"), NULL means both
664 * The return value should be freed when no longer needed.
666 char *snd_device_name_get_hint(const void *hint, const char *id)
668 const char *hint1 = (const char *)hint, *delim;
674 while (*hint1 != '\0') {
675 delim = strchr(hint1, '|');
676 if (memcmp(id, hint1, 4) != 0) {
683 return strdup(hint1 + 4);
684 size = delim - hint1 - 4;
685 res = malloc(size + 1);
687 memcpy(res, hint1 + 4, size);