1d5ac70f0Sopenharmony_ci/**
2d5ac70f0Sopenharmony_ci * \file control/namehint.c
3d5ac70f0Sopenharmony_ci * \ingroup Configuration
4d5ac70f0Sopenharmony_ci * \brief Give device name hints
5d5ac70f0Sopenharmony_ci * \author Jaroslav Kysela <perex@perex.cz>
6d5ac70f0Sopenharmony_ci * \date 2006
7d5ac70f0Sopenharmony_ci */
8d5ac70f0Sopenharmony_ci/*
9d5ac70f0Sopenharmony_ci *  Give device name hints  - main file
10d5ac70f0Sopenharmony_ci *  Copyright (c) 2006 by Jaroslav Kysela <perex@perex.cz>
11d5ac70f0Sopenharmony_ci *
12d5ac70f0Sopenharmony_ci *
13d5ac70f0Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
14d5ac70f0Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as
15d5ac70f0Sopenharmony_ci *   published by the Free Software Foundation; either version 2.1 of
16d5ac70f0Sopenharmony_ci *   the License, or (at your option) any later version.
17d5ac70f0Sopenharmony_ci *
18d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
19d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21d5ac70f0Sopenharmony_ci *   GNU Lesser General Public License for more details.
22d5ac70f0Sopenharmony_ci *
23d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public
24d5ac70f0Sopenharmony_ci *   License along with this library; if not, write to the Free Software
25d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26d5ac70f0Sopenharmony_ci *
27d5ac70f0Sopenharmony_ci */
28d5ac70f0Sopenharmony_ci
29d5ac70f0Sopenharmony_ci#include "local.h"
30d5ac70f0Sopenharmony_ci
31d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
32d5ac70f0Sopenharmony_ci#define DEV_SKIP	9999 /* some non-existing device number */
33d5ac70f0Sopenharmony_cistruct hint_list {
34d5ac70f0Sopenharmony_ci	char **list;
35d5ac70f0Sopenharmony_ci	unsigned int count;
36d5ac70f0Sopenharmony_ci	unsigned int allocated;
37d5ac70f0Sopenharmony_ci	const char *siface;
38d5ac70f0Sopenharmony_ci	snd_ctl_elem_iface_t iface;
39d5ac70f0Sopenharmony_ci	snd_ctl_t *ctl;
40d5ac70f0Sopenharmony_ci	snd_ctl_card_info_t *info;
41d5ac70f0Sopenharmony_ci	int card;
42d5ac70f0Sopenharmony_ci	int device;
43d5ac70f0Sopenharmony_ci	long device_input;
44d5ac70f0Sopenharmony_ci	long device_output;
45d5ac70f0Sopenharmony_ci	int stream;
46d5ac70f0Sopenharmony_ci	int show_all;
47d5ac70f0Sopenharmony_ci	char *cardname;
48d5ac70f0Sopenharmony_ci};
49d5ac70f0Sopenharmony_ci#endif
50d5ac70f0Sopenharmony_ci
51d5ac70f0Sopenharmony_cistatic int hint_list_add(struct hint_list *list,
52d5ac70f0Sopenharmony_ci			 const char *name,
53d5ac70f0Sopenharmony_ci			 const char *description)
54d5ac70f0Sopenharmony_ci{
55d5ac70f0Sopenharmony_ci	char *x;
56d5ac70f0Sopenharmony_ci
57d5ac70f0Sopenharmony_ci	if (list->count + 1 >= list->allocated) {
58d5ac70f0Sopenharmony_ci		char **n = realloc(list->list, (list->allocated + 10) * sizeof(char *));
59d5ac70f0Sopenharmony_ci		if (n == NULL)
60d5ac70f0Sopenharmony_ci			return -ENOMEM;
61d5ac70f0Sopenharmony_ci		memset(n + list->allocated, 0, 10 * sizeof(*n));
62d5ac70f0Sopenharmony_ci		list->allocated += 10;
63d5ac70f0Sopenharmony_ci		list->list = n;
64d5ac70f0Sopenharmony_ci	}
65d5ac70f0Sopenharmony_ci	if (name == NULL) {
66d5ac70f0Sopenharmony_ci		x = NULL;
67d5ac70f0Sopenharmony_ci	} else {
68d5ac70f0Sopenharmony_ci		x = malloc(4 + strlen(name) + (description != NULL ? (4 + strlen(description) + 1) : 0) + 1);
69d5ac70f0Sopenharmony_ci		if (x == NULL)
70d5ac70f0Sopenharmony_ci			return -ENOMEM;
71d5ac70f0Sopenharmony_ci		memcpy(x, "NAME", 4);
72d5ac70f0Sopenharmony_ci		strcpy(x + 4, name);
73d5ac70f0Sopenharmony_ci		if (description != NULL) {
74d5ac70f0Sopenharmony_ci			strcat(x, "|DESC");
75d5ac70f0Sopenharmony_ci			strcat(x, description);
76d5ac70f0Sopenharmony_ci		}
77d5ac70f0Sopenharmony_ci	}
78d5ac70f0Sopenharmony_ci	list->list[list->count++] = x;
79d5ac70f0Sopenharmony_ci	return 0;
80d5ac70f0Sopenharmony_ci}
81d5ac70f0Sopenharmony_ci
82d5ac70f0Sopenharmony_ci/**
83d5ac70f0Sopenharmony_ci * Add a namehint from string given in a user configuration file
84d5ac70f0Sopenharmony_ci */
85d5ac70f0Sopenharmony_cistatic int hint_list_add_custom(struct hint_list *list,
86d5ac70f0Sopenharmony_ci				const char *entry)
87d5ac70f0Sopenharmony_ci{
88d5ac70f0Sopenharmony_ci	int err;
89d5ac70f0Sopenharmony_ci	const char *sep;
90d5ac70f0Sopenharmony_ci	char *name;
91d5ac70f0Sopenharmony_ci
92d5ac70f0Sopenharmony_ci	assert(entry);
93d5ac70f0Sopenharmony_ci
94d5ac70f0Sopenharmony_ci	sep = strchr(entry, '|');
95d5ac70f0Sopenharmony_ci	if (sep == NULL)
96d5ac70f0Sopenharmony_ci		return hint_list_add(list, entry, NULL);
97d5ac70f0Sopenharmony_ci
98d5ac70f0Sopenharmony_ci	name = strndup(entry, sep - entry);
99d5ac70f0Sopenharmony_ci	if (name == NULL)
100d5ac70f0Sopenharmony_ci		return -ENOMEM;
101d5ac70f0Sopenharmony_ci
102d5ac70f0Sopenharmony_ci	err = hint_list_add(list, name, sep + 1);
103d5ac70f0Sopenharmony_ci	free(name);
104d5ac70f0Sopenharmony_ci	return err;
105d5ac70f0Sopenharmony_ci}
106d5ac70f0Sopenharmony_ci
107d5ac70f0Sopenharmony_cistatic void zero_handler(const char *file ATTRIBUTE_UNUSED,
108d5ac70f0Sopenharmony_ci			 int line ATTRIBUTE_UNUSED,
109d5ac70f0Sopenharmony_ci			 const char *function ATTRIBUTE_UNUSED,
110d5ac70f0Sopenharmony_ci			 int err ATTRIBUTE_UNUSED,
111d5ac70f0Sopenharmony_ci			 const char *fmt ATTRIBUTE_UNUSED,
112d5ac70f0Sopenharmony_ci			 va_list arg ATTRIBUTE_UNUSED)
113d5ac70f0Sopenharmony_ci{
114d5ac70f0Sopenharmony_ci}
115d5ac70f0Sopenharmony_ci
116d5ac70f0Sopenharmony_cistatic int get_dev_name1(struct hint_list *list, char **res, int device,
117d5ac70f0Sopenharmony_ci			 int stream)
118d5ac70f0Sopenharmony_ci{
119d5ac70f0Sopenharmony_ci	*res = NULL;
120d5ac70f0Sopenharmony_ci	if (device < 0 || device == DEV_SKIP)
121d5ac70f0Sopenharmony_ci		return 0;
122d5ac70f0Sopenharmony_ci	switch (list->iface) {
123d5ac70f0Sopenharmony_ci#ifdef BUILD_HWDEP
124d5ac70f0Sopenharmony_ci	case SND_CTL_ELEM_IFACE_HWDEP:
125d5ac70f0Sopenharmony_ci		{
126d5ac70f0Sopenharmony_ci			snd_hwdep_info_t info = {0};
127d5ac70f0Sopenharmony_ci			snd_hwdep_info_set_device(&info, device);
128d5ac70f0Sopenharmony_ci			if (snd_ctl_hwdep_info(list->ctl, &info) < 0)
129d5ac70f0Sopenharmony_ci				return 0;
130d5ac70f0Sopenharmony_ci			*res = strdup(snd_hwdep_info_get_name(&info));
131d5ac70f0Sopenharmony_ci			return 0;
132d5ac70f0Sopenharmony_ci		}
133d5ac70f0Sopenharmony_ci#endif
134d5ac70f0Sopenharmony_ci#ifdef BUILD_PCM
135d5ac70f0Sopenharmony_ci	case SND_CTL_ELEM_IFACE_PCM:
136d5ac70f0Sopenharmony_ci		{
137d5ac70f0Sopenharmony_ci			snd_pcm_info_t info = {0};
138d5ac70f0Sopenharmony_ci			snd_pcm_info_set_device(&info, device);
139d5ac70f0Sopenharmony_ci			snd_pcm_info_set_stream(&info, stream ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK);
140d5ac70f0Sopenharmony_ci			if (snd_ctl_pcm_info(list->ctl, &info) < 0)
141d5ac70f0Sopenharmony_ci				return 0;
142d5ac70f0Sopenharmony_ci			switch (snd_pcm_info_get_class(&info)) {
143d5ac70f0Sopenharmony_ci			case SND_PCM_CLASS_MODEM:
144d5ac70f0Sopenharmony_ci			case SND_PCM_CLASS_DIGITIZER:
145d5ac70f0Sopenharmony_ci				return -ENODEV;
146d5ac70f0Sopenharmony_ci			default:
147d5ac70f0Sopenharmony_ci				break;
148d5ac70f0Sopenharmony_ci			}
149d5ac70f0Sopenharmony_ci			*res = strdup(snd_pcm_info_get_name(&info));
150d5ac70f0Sopenharmony_ci			return 0;
151d5ac70f0Sopenharmony_ci		}
152d5ac70f0Sopenharmony_ci#endif
153d5ac70f0Sopenharmony_ci#ifdef BUILD_RAWMIDI
154d5ac70f0Sopenharmony_ci	case SND_CTL_ELEM_IFACE_RAWMIDI:
155d5ac70f0Sopenharmony_ci		{
156d5ac70f0Sopenharmony_ci			snd_rawmidi_info_t info = {0};
157d5ac70f0Sopenharmony_ci			snd_rawmidi_info_set_device(&info, device);
158d5ac70f0Sopenharmony_ci			snd_rawmidi_info_set_stream(&info, stream ? SND_RAWMIDI_STREAM_INPUT : SND_RAWMIDI_STREAM_OUTPUT);
159d5ac70f0Sopenharmony_ci			if (snd_ctl_rawmidi_info(list->ctl, &info) < 0)
160d5ac70f0Sopenharmony_ci				return 0;
161d5ac70f0Sopenharmony_ci			*res = strdup(snd_rawmidi_info_get_name(&info));
162d5ac70f0Sopenharmony_ci			return 0;
163d5ac70f0Sopenharmony_ci		}
164d5ac70f0Sopenharmony_ci#endif
165d5ac70f0Sopenharmony_ci	default:
166d5ac70f0Sopenharmony_ci		return 0;
167d5ac70f0Sopenharmony_ci	}
168d5ac70f0Sopenharmony_ci}
169d5ac70f0Sopenharmony_ci
170d5ac70f0Sopenharmony_cistatic char *get_dev_name(struct hint_list *list)
171d5ac70f0Sopenharmony_ci{
172d5ac70f0Sopenharmony_ci	char *str1, *str2, *res;
173d5ac70f0Sopenharmony_ci	int device;
174d5ac70f0Sopenharmony_ci
175d5ac70f0Sopenharmony_ci	device = list->device_input >= 0 ? list->device_input : list->device;
176d5ac70f0Sopenharmony_ci	if (get_dev_name1(list, &str1, device, 1) < 0)
177d5ac70f0Sopenharmony_ci		return NULL;
178d5ac70f0Sopenharmony_ci	device = list->device_output >= 0 ? list->device_output : list->device;
179d5ac70f0Sopenharmony_ci	if (get_dev_name1(list, &str2, device, 0) < 0) {
180d5ac70f0Sopenharmony_ci		if (str1)
181d5ac70f0Sopenharmony_ci			free(str1);
182d5ac70f0Sopenharmony_ci		return NULL;
183d5ac70f0Sopenharmony_ci	}
184d5ac70f0Sopenharmony_ci	if (str1 != NULL || str2 != NULL) {
185d5ac70f0Sopenharmony_ci		if (str1 != NULL && str2 != NULL) {
186d5ac70f0Sopenharmony_ci			if (strcmp(str1, str2) == 0) {
187d5ac70f0Sopenharmony_ci				res = malloc(strlen(list->cardname) + strlen(str2) + 3);
188d5ac70f0Sopenharmony_ci				if (res != NULL) {
189d5ac70f0Sopenharmony_ci					strcpy(res, list->cardname);
190d5ac70f0Sopenharmony_ci					strcat(res, ", ");
191d5ac70f0Sopenharmony_ci					strcat(res, str2);
192d5ac70f0Sopenharmony_ci				}
193d5ac70f0Sopenharmony_ci			} else {
194d5ac70f0Sopenharmony_ci				res = malloc(strlen(list->cardname) + strlen(str2) + strlen(str1) + 6);
195d5ac70f0Sopenharmony_ci				if (res != NULL) {
196d5ac70f0Sopenharmony_ci					strcpy(res, list->cardname);
197d5ac70f0Sopenharmony_ci					strcat(res, ", ");
198d5ac70f0Sopenharmony_ci					strcat(res, str2);
199d5ac70f0Sopenharmony_ci					strcat(res, " / ");
200d5ac70f0Sopenharmony_ci					strcat(res, str1);
201d5ac70f0Sopenharmony_ci				}
202d5ac70f0Sopenharmony_ci			}
203d5ac70f0Sopenharmony_ci			free(str2);
204d5ac70f0Sopenharmony_ci			free(str1);
205d5ac70f0Sopenharmony_ci			return res;
206d5ac70f0Sopenharmony_ci		} else {
207d5ac70f0Sopenharmony_ci			if (str1 != NULL) {
208d5ac70f0Sopenharmony_ci				str2 = "Input";
209d5ac70f0Sopenharmony_ci			} else {
210d5ac70f0Sopenharmony_ci				str1 = str2;
211d5ac70f0Sopenharmony_ci				str2 = "Output";
212d5ac70f0Sopenharmony_ci			}
213d5ac70f0Sopenharmony_ci			res = malloc(strlen(list->cardname) + strlen(str1) + 19);
214d5ac70f0Sopenharmony_ci			if (res == NULL) {
215d5ac70f0Sopenharmony_ci				free(str1);
216d5ac70f0Sopenharmony_ci				return NULL;
217d5ac70f0Sopenharmony_ci			}
218d5ac70f0Sopenharmony_ci			strcpy(res, list->cardname);
219d5ac70f0Sopenharmony_ci			strcat(res, ", ");
220d5ac70f0Sopenharmony_ci			strcat(res, str1);
221d5ac70f0Sopenharmony_ci			strcat(res, "|IOID");
222d5ac70f0Sopenharmony_ci			strcat(res, str2);
223d5ac70f0Sopenharmony_ci			free(str1);
224d5ac70f0Sopenharmony_ci			return res;
225d5ac70f0Sopenharmony_ci		}
226d5ac70f0Sopenharmony_ci	}
227d5ac70f0Sopenharmony_ci	/* if the specified device doesn't exist, skip this entry */
228d5ac70f0Sopenharmony_ci	if (list->device >= 0 || list->device_input >= 0 || list->device_output >= 0)
229d5ac70f0Sopenharmony_ci		return NULL;
230d5ac70f0Sopenharmony_ci	return strdup(list->cardname);
231d5ac70f0Sopenharmony_ci}
232d5ac70f0Sopenharmony_ci
233d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
234d5ac70f0Sopenharmony_ci#define BUF_SIZE 128
235d5ac70f0Sopenharmony_ci#endif
236d5ac70f0Sopenharmony_ci
237d5ac70f0Sopenharmony_cistatic int try_config(snd_config_t *config,
238d5ac70f0Sopenharmony_ci		      struct hint_list *list,
239d5ac70f0Sopenharmony_ci		      const char *base,
240d5ac70f0Sopenharmony_ci		      const char *name)
241d5ac70f0Sopenharmony_ci{
242d5ac70f0Sopenharmony_ci	snd_local_error_handler_t eh;
243d5ac70f0Sopenharmony_ci	snd_config_t *res = NULL, *cfg, *cfg1, *n;
244d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
245d5ac70f0Sopenharmony_ci	char *buf, *buf1 = NULL, *buf2;
246d5ac70f0Sopenharmony_ci	const char *str;
247d5ac70f0Sopenharmony_ci	int err = 0, level;
248d5ac70f0Sopenharmony_ci	long dev = list->device;
249d5ac70f0Sopenharmony_ci	int cleanup_res = 0;
250d5ac70f0Sopenharmony_ci
251d5ac70f0Sopenharmony_ci	list->device_input = -1;
252d5ac70f0Sopenharmony_ci	list->device_output = -1;
253d5ac70f0Sopenharmony_ci	buf = malloc(BUF_SIZE);
254d5ac70f0Sopenharmony_ci	if (buf == NULL)
255d5ac70f0Sopenharmony_ci		return -ENOMEM;
256d5ac70f0Sopenharmony_ci	sprintf(buf, "%s.%s", base, name);
257d5ac70f0Sopenharmony_ci	/* look for redirection */
258d5ac70f0Sopenharmony_ci	if (snd_config_search(config, buf, &cfg) >= 0 &&
259d5ac70f0Sopenharmony_ci	    snd_config_get_string(cfg, &str) >= 0 &&
260d5ac70f0Sopenharmony_ci	    ((strncmp(base, str, strlen(base)) == 0 &&
261d5ac70f0Sopenharmony_ci	     str[strlen(base)] == '.') || strchr(str, '.') == NULL))
262d5ac70f0Sopenharmony_ci	     	goto __skip_add;
263d5ac70f0Sopenharmony_ci	if (list->card >= 0 && list->device >= 0)
264d5ac70f0Sopenharmony_ci		sprintf(buf, "%s:CARD=%s,DEV=%i", name, snd_ctl_card_info_get_id(list->info), list->device);
265d5ac70f0Sopenharmony_ci	else if (list->card >= 0)
266d5ac70f0Sopenharmony_ci		sprintf(buf, "%s:CARD=%s", name, snd_ctl_card_info_get_id(list->info));
267d5ac70f0Sopenharmony_ci	else
268d5ac70f0Sopenharmony_ci		strcpy(buf, name);
269d5ac70f0Sopenharmony_ci	eh = snd_lib_error_set_local(&zero_handler);
270d5ac70f0Sopenharmony_ci	err = snd_config_search_definition(config, base, buf, &res);
271d5ac70f0Sopenharmony_ci	snd_lib_error_set_local(eh);
272d5ac70f0Sopenharmony_ci	if (err < 0)
273d5ac70f0Sopenharmony_ci		goto __skip_add;
274d5ac70f0Sopenharmony_ci	cleanup_res = 1;
275d5ac70f0Sopenharmony_ci	err = -EINVAL;
276d5ac70f0Sopenharmony_ci	if (snd_config_get_type(res) != SND_CONFIG_TYPE_COMPOUND)
277d5ac70f0Sopenharmony_ci		goto __cleanup;
278d5ac70f0Sopenharmony_ci	if (snd_config_search(res, "type", NULL) < 0)
279d5ac70f0Sopenharmony_ci		goto __cleanup;
280d5ac70f0Sopenharmony_ci
281d5ac70f0Sopenharmony_ci#if 0	/* for debug purposes */
282d5ac70f0Sopenharmony_ci		{
283d5ac70f0Sopenharmony_ci			snd_output_t *out;
284d5ac70f0Sopenharmony_ci			fprintf(stderr, "********* PCM '%s':\n", buf);
285d5ac70f0Sopenharmony_ci			snd_output_stdio_attach(&out, stderr, 0);
286d5ac70f0Sopenharmony_ci			snd_config_save(res, out);
287d5ac70f0Sopenharmony_ci			snd_output_close(out);
288d5ac70f0Sopenharmony_ci			fprintf(stderr, "\n");
289d5ac70f0Sopenharmony_ci		}
290d5ac70f0Sopenharmony_ci#endif
291d5ac70f0Sopenharmony_ci
292d5ac70f0Sopenharmony_ci	cfg1 = res;
293d5ac70f0Sopenharmony_ci	level = 0;
294d5ac70f0Sopenharmony_ci      __hint:
295d5ac70f0Sopenharmony_ci      	level++;
296d5ac70f0Sopenharmony_ci	if (snd_config_search(cfg1, "type", &cfg) >= 0 &&
297d5ac70f0Sopenharmony_ci	    snd_config_get_string(cfg, &str) >= 0 &&
298d5ac70f0Sopenharmony_ci	    strcmp(str, "hw") == 0) {
299d5ac70f0Sopenharmony_ci		if (snd_config_search(cfg1, "device", &cfg) >= 0) {
300d5ac70f0Sopenharmony_ci			if (snd_config_get_integer(cfg, &dev) < 0) {
301d5ac70f0Sopenharmony_ci				SNDERR("(%s) device must be an integer", buf);
302d5ac70f0Sopenharmony_ci				err = -EINVAL;
303d5ac70f0Sopenharmony_ci				goto __cleanup;
304d5ac70f0Sopenharmony_ci			}
305d5ac70f0Sopenharmony_ci		}
306d5ac70f0Sopenharmony_ci	}
307d5ac70f0Sopenharmony_ci
308d5ac70f0Sopenharmony_ci	if (snd_config_search(cfg1, "hint", &cfg) >= 0) {
309d5ac70f0Sopenharmony_ci		if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
310d5ac70f0Sopenharmony_ci			SNDERR("hint (%s) must be a compound", buf);
311d5ac70f0Sopenharmony_ci			err = -EINVAL;
312d5ac70f0Sopenharmony_ci			goto __cleanup;
313d5ac70f0Sopenharmony_ci		}
314d5ac70f0Sopenharmony_ci		if (list->card < 0 &&
315d5ac70f0Sopenharmony_ci		    snd_config_search(cfg, "omit_noargs", &n) >= 0 &&
316d5ac70f0Sopenharmony_ci		    snd_config_get_bool(n) > 0)
317d5ac70f0Sopenharmony_ci			goto __skip_add;
318d5ac70f0Sopenharmony_ci		if (level == 1 &&
319d5ac70f0Sopenharmony_ci		    snd_config_search(cfg, "show", &n) >= 0 &&
320d5ac70f0Sopenharmony_ci		    snd_config_get_bool(n) <= 0)
321d5ac70f0Sopenharmony_ci			goto __skip_add;
322d5ac70f0Sopenharmony_ci		if (buf1 == NULL &&
323d5ac70f0Sopenharmony_ci		    snd_config_search(cfg, "description", &n) >= 0 &&
324d5ac70f0Sopenharmony_ci		    snd_config_get_string(n, &str) >= 0) {
325d5ac70f0Sopenharmony_ci			buf1 = strdup(str);
326d5ac70f0Sopenharmony_ci			if (buf1 == NULL) {
327d5ac70f0Sopenharmony_ci				err = -ENOMEM;
328d5ac70f0Sopenharmony_ci				goto __cleanup;
329d5ac70f0Sopenharmony_ci			}
330d5ac70f0Sopenharmony_ci		}
331d5ac70f0Sopenharmony_ci		if (snd_config_search(cfg, "device", &n) >= 0) {
332d5ac70f0Sopenharmony_ci			if (snd_config_get_integer(n, &dev) < 0) {
333d5ac70f0Sopenharmony_ci				SNDERR("(%s) device must be an integer", buf);
334d5ac70f0Sopenharmony_ci				err = -EINVAL;
335d5ac70f0Sopenharmony_ci				goto __cleanup;
336d5ac70f0Sopenharmony_ci			}
337d5ac70f0Sopenharmony_ci			list->device_input = dev;
338d5ac70f0Sopenharmony_ci			list->device_output = dev;
339d5ac70f0Sopenharmony_ci		}
340d5ac70f0Sopenharmony_ci		if (snd_config_search(cfg, "device_input", &n) >= 0) {
341d5ac70f0Sopenharmony_ci			if (snd_config_get_integer(n, &list->device_input) < 0) {
342d5ac70f0Sopenharmony_ci				SNDERR("(%s) device_input must be an integer", buf);
343d5ac70f0Sopenharmony_ci				err = -EINVAL;
344d5ac70f0Sopenharmony_ci				goto __cleanup;
345d5ac70f0Sopenharmony_ci			}
346d5ac70f0Sopenharmony_ci			/* skip the counterpart if only a single direction is defined */
347d5ac70f0Sopenharmony_ci			if (list->device_output < 0)
348d5ac70f0Sopenharmony_ci				list->device_output = DEV_SKIP;
349d5ac70f0Sopenharmony_ci		}
350d5ac70f0Sopenharmony_ci		if (snd_config_search(cfg, "device_output", &n) >= 0) {
351d5ac70f0Sopenharmony_ci			if (snd_config_get_integer(n, &list->device_output) < 0) {
352d5ac70f0Sopenharmony_ci				SNDERR("(%s) device_output must be an integer", buf);
353d5ac70f0Sopenharmony_ci				err = -EINVAL;
354d5ac70f0Sopenharmony_ci				goto __cleanup;
355d5ac70f0Sopenharmony_ci			}
356d5ac70f0Sopenharmony_ci			/* skip the counterpart if only a single direction is defined */
357d5ac70f0Sopenharmony_ci			if (list->device_input < 0)
358d5ac70f0Sopenharmony_ci				list->device_input = DEV_SKIP;
359d5ac70f0Sopenharmony_ci		}
360d5ac70f0Sopenharmony_ci	} else if (level == 1 && !list->show_all)
361d5ac70f0Sopenharmony_ci		goto __skip_add;
362d5ac70f0Sopenharmony_ci	if (snd_config_search(cfg1, "slave", &cfg) >= 0 &&
363d5ac70f0Sopenharmony_ci	    snd_config_search(cfg, base, &cfg1) >= 0)
364d5ac70f0Sopenharmony_ci	    	goto __hint;
365d5ac70f0Sopenharmony_ci	snd_config_delete(res);
366d5ac70f0Sopenharmony_ci	res = NULL;
367d5ac70f0Sopenharmony_ci	cleanup_res = 0;
368d5ac70f0Sopenharmony_ci	if (strchr(buf, ':') != NULL)
369d5ac70f0Sopenharmony_ci		goto __ok;
370d5ac70f0Sopenharmony_ci	/* find, if all parameters have a default, */
371d5ac70f0Sopenharmony_ci	/* otherwise filter this definition */
372d5ac70f0Sopenharmony_ci	eh = snd_lib_error_set_local(&zero_handler);
373d5ac70f0Sopenharmony_ci	err = snd_config_search_alias_hooks(config, base, buf, &res);
374d5ac70f0Sopenharmony_ci	snd_lib_error_set_local(eh);
375d5ac70f0Sopenharmony_ci	if (err < 0)
376d5ac70f0Sopenharmony_ci		goto __cleanup;
377d5ac70f0Sopenharmony_ci	if (snd_config_search(res, "@args", &cfg) >= 0) {
378d5ac70f0Sopenharmony_ci		snd_config_for_each(i, next, cfg) {
379d5ac70f0Sopenharmony_ci			/* skip the argument list */
380d5ac70f0Sopenharmony_ci			if (snd_config_get_id(snd_config_iterator_entry(i), &str) < 0)
381d5ac70f0Sopenharmony_ci				continue;
382d5ac70f0Sopenharmony_ci			while (*str && *str >= '0' && *str <= '9') str++;
383d5ac70f0Sopenharmony_ci			if (*str == '\0')
384d5ac70f0Sopenharmony_ci				continue;
385d5ac70f0Sopenharmony_ci			/* the argument definition must have the default */
386d5ac70f0Sopenharmony_ci			if (snd_config_search(snd_config_iterator_entry(i),
387d5ac70f0Sopenharmony_ci					      "default", NULL) < 0) {
388d5ac70f0Sopenharmony_ci				err = -EINVAL;
389d5ac70f0Sopenharmony_ci				goto __cleanup;
390d5ac70f0Sopenharmony_ci			}
391d5ac70f0Sopenharmony_ci		}
392d5ac70f0Sopenharmony_ci	}
393d5ac70f0Sopenharmony_ci      __ok:
394d5ac70f0Sopenharmony_ci	err = 0;
395d5ac70f0Sopenharmony_ci      __cleanup:
396d5ac70f0Sopenharmony_ci      	if (err >= 0) {
397d5ac70f0Sopenharmony_ci      		list->device = dev;
398d5ac70f0Sopenharmony_ci 		str = list->card >= 0 ? get_dev_name(list) : NULL;
399d5ac70f0Sopenharmony_ci      		if (str != NULL) {
400d5ac70f0Sopenharmony_ci      			level = (buf1 == NULL ? 0 : strlen(buf1)) + 1 + strlen(str);
401d5ac70f0Sopenharmony_ci      			buf2 = realloc((char *)str, level + 1);
402d5ac70f0Sopenharmony_ci      			if (buf2 != NULL) {
403d5ac70f0Sopenharmony_ci      				if (buf1 != NULL) {
404d5ac70f0Sopenharmony_ci      					str = strchr(buf2, '|');
405d5ac70f0Sopenharmony_ci      					if (str != NULL)
406d5ac70f0Sopenharmony_ci						memmove(buf2 + (level - strlen(str)), str, strlen(str));
407d5ac70f0Sopenharmony_ci					else
408d5ac70f0Sopenharmony_ci						str = buf2 + strlen(buf2);
409d5ac70f0Sopenharmony_ci      					*(char *)str++ = '\n';
410d5ac70f0Sopenharmony_ci	      				memcpy((char *)str, buf1, strlen(buf1));
411d5ac70f0Sopenharmony_ci	      				buf2[level] = '\0';
412d5ac70f0Sopenharmony_ci					free(buf1);
413d5ac70f0Sopenharmony_ci				}
414d5ac70f0Sopenharmony_ci				buf1 = buf2;
415d5ac70f0Sopenharmony_ci			} else {
416d5ac70f0Sopenharmony_ci				free((char *)str);
417d5ac70f0Sopenharmony_ci			}
418d5ac70f0Sopenharmony_ci      		} else if (list->device >= 0)
419d5ac70f0Sopenharmony_ci      			goto __skip_add;
420d5ac70f0Sopenharmony_ci	      	err = hint_list_add(list, buf, buf1);
421d5ac70f0Sopenharmony_ci	}
422d5ac70f0Sopenharmony_ci      __skip_add:
423d5ac70f0Sopenharmony_ci	if (res && cleanup_res)
424d5ac70f0Sopenharmony_ci	      	snd_config_delete(res);
425d5ac70f0Sopenharmony_ci	if (buf1)
426d5ac70f0Sopenharmony_ci		free(buf1);
427d5ac70f0Sopenharmony_ci      	free(buf);
428d5ac70f0Sopenharmony_ci	return err;
429d5ac70f0Sopenharmony_ci}
430d5ac70f0Sopenharmony_ci
431d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
432d5ac70f0Sopenharmony_ci#define IFACE(v, fcn) [SND_CTL_ELEM_IFACE_##v] = (next_devices_t)fcn
433d5ac70f0Sopenharmony_ci
434d5ac70f0Sopenharmony_citypedef int (*next_devices_t)(snd_ctl_t *, int *);
435d5ac70f0Sopenharmony_ci
436d5ac70f0Sopenharmony_cistatic const next_devices_t next_devices[] = {
437d5ac70f0Sopenharmony_ci	IFACE(CARD, NULL),
438d5ac70f0Sopenharmony_ci	IFACE(HWDEP, snd_ctl_hwdep_next_device),
439d5ac70f0Sopenharmony_ci	IFACE(MIXER, NULL),
440d5ac70f0Sopenharmony_ci	IFACE(PCM, snd_ctl_pcm_next_device),
441d5ac70f0Sopenharmony_ci	IFACE(RAWMIDI, snd_ctl_rawmidi_next_device),
442d5ac70f0Sopenharmony_ci	IFACE(TIMER, NULL),
443d5ac70f0Sopenharmony_ci	IFACE(SEQUENCER, NULL)
444d5ac70f0Sopenharmony_ci};
445d5ac70f0Sopenharmony_ci#endif
446d5ac70f0Sopenharmony_ci
447d5ac70f0Sopenharmony_cistatic int add_card(snd_config_t *config, snd_config_t *rw_config, struct hint_list *list, int card)
448d5ac70f0Sopenharmony_ci{
449d5ac70f0Sopenharmony_ci	int err, ok;
450d5ac70f0Sopenharmony_ci	snd_config_t *conf, *n;
451d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
452d5ac70f0Sopenharmony_ci	const char *str;
453d5ac70f0Sopenharmony_ci	char ctl_name[16];
454d5ac70f0Sopenharmony_ci	snd_ctl_card_info_t info = {0};
455d5ac70f0Sopenharmony_ci	int device, max_device = 0;
456d5ac70f0Sopenharmony_ci
457d5ac70f0Sopenharmony_ci	list->info = &info;
458d5ac70f0Sopenharmony_ci	err = snd_config_search(config, list->siface, &conf);
459d5ac70f0Sopenharmony_ci	if (err < 0)
460d5ac70f0Sopenharmony_ci		return err;
461d5ac70f0Sopenharmony_ci	sprintf(ctl_name, "hw:%i", card);
462d5ac70f0Sopenharmony_ci	err = snd_ctl_open(&list->ctl, ctl_name, 0);
463d5ac70f0Sopenharmony_ci	if (err < 0)
464d5ac70f0Sopenharmony_ci		return err;
465d5ac70f0Sopenharmony_ci	err = snd_ctl_card_info(list->ctl, &info);
466d5ac70f0Sopenharmony_ci	if (err < 0)
467d5ac70f0Sopenharmony_ci		goto __error;
468d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, conf) {
469d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
470d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &str) < 0)
471d5ac70f0Sopenharmony_ci			continue;
472d5ac70f0Sopenharmony_ci
473d5ac70f0Sopenharmony_ci		if (next_devices[list->iface] != NULL) {
474d5ac70f0Sopenharmony_ci			list->card = card;
475d5ac70f0Sopenharmony_ci			device = max_device = -1;
476d5ac70f0Sopenharmony_ci			err = next_devices[list->iface](list->ctl, &device);
477d5ac70f0Sopenharmony_ci			if (device < 0)
478d5ac70f0Sopenharmony_ci				err = -EINVAL;
479d5ac70f0Sopenharmony_ci			else
480d5ac70f0Sopenharmony_ci				max_device = device;
481d5ac70f0Sopenharmony_ci			while (err >= 0 && device >= 0) {
482d5ac70f0Sopenharmony_ci				err = next_devices[list->iface](list->ctl, &device);
483d5ac70f0Sopenharmony_ci				if (err >= 0 && device > max_device)
484d5ac70f0Sopenharmony_ci					max_device = device;
485d5ac70f0Sopenharmony_ci			}
486d5ac70f0Sopenharmony_ci			ok = 0;
487d5ac70f0Sopenharmony_ci			for (device = 0; err >= 0 && device <= max_device; device++) {
488d5ac70f0Sopenharmony_ci				list->device = device;
489d5ac70f0Sopenharmony_ci				err = try_config(rw_config, list, list->siface, str);
490d5ac70f0Sopenharmony_ci				if (err < 0)
491d5ac70f0Sopenharmony_ci					break;
492d5ac70f0Sopenharmony_ci				ok++;
493d5ac70f0Sopenharmony_ci			}
494d5ac70f0Sopenharmony_ci			if (ok)
495d5ac70f0Sopenharmony_ci				continue;
496d5ac70f0Sopenharmony_ci		} else {
497d5ac70f0Sopenharmony_ci			err = -EINVAL;
498d5ac70f0Sopenharmony_ci		}
499d5ac70f0Sopenharmony_ci		if (err == -EXDEV)
500d5ac70f0Sopenharmony_ci			continue;
501d5ac70f0Sopenharmony_ci		if (err < 0) {
502d5ac70f0Sopenharmony_ci			list->card = card;
503d5ac70f0Sopenharmony_ci			list->device = -1;
504d5ac70f0Sopenharmony_ci			err = try_config(rw_config, list, list->siface, str);
505d5ac70f0Sopenharmony_ci		}
506d5ac70f0Sopenharmony_ci		if (err == -ENOMEM)
507d5ac70f0Sopenharmony_ci			goto __error;
508d5ac70f0Sopenharmony_ci	}
509d5ac70f0Sopenharmony_ci	err = 0;
510d5ac70f0Sopenharmony_ci      __error:
511d5ac70f0Sopenharmony_ci      	snd_ctl_close(list->ctl);
512d5ac70f0Sopenharmony_ci	return err;
513d5ac70f0Sopenharmony_ci}
514d5ac70f0Sopenharmony_ci
515d5ac70f0Sopenharmony_cistatic int get_card_name(struct hint_list *list, int card)
516d5ac70f0Sopenharmony_ci{
517d5ac70f0Sopenharmony_ci	char scard[16], *s;
518d5ac70f0Sopenharmony_ci	int err;
519d5ac70f0Sopenharmony_ci
520d5ac70f0Sopenharmony_ci	free(list->cardname);
521d5ac70f0Sopenharmony_ci	list->cardname = NULL;
522d5ac70f0Sopenharmony_ci	err = snd_card_get_name(card, &list->cardname);
523d5ac70f0Sopenharmony_ci	if (err <= 0)
524d5ac70f0Sopenharmony_ci		return 0;
525d5ac70f0Sopenharmony_ci	sprintf(scard, " #%i", card);
526d5ac70f0Sopenharmony_ci	s = realloc(list->cardname, strlen(list->cardname) + strlen(scard) + 1);
527d5ac70f0Sopenharmony_ci	if (s == NULL)
528d5ac70f0Sopenharmony_ci		return -ENOMEM;
529d5ac70f0Sopenharmony_ci	list->cardname = s;
530d5ac70f0Sopenharmony_ci	return 0;
531d5ac70f0Sopenharmony_ci}
532d5ac70f0Sopenharmony_ci
533d5ac70f0Sopenharmony_cistatic int add_software_devices(snd_config_t *config, snd_config_t *rw_config,
534d5ac70f0Sopenharmony_ci				struct hint_list *list)
535d5ac70f0Sopenharmony_ci{
536d5ac70f0Sopenharmony_ci	int err;
537d5ac70f0Sopenharmony_ci	snd_config_t *conf, *n;
538d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
539d5ac70f0Sopenharmony_ci	const char *str;
540d5ac70f0Sopenharmony_ci
541d5ac70f0Sopenharmony_ci	err = snd_config_search(config, list->siface, &conf);
542d5ac70f0Sopenharmony_ci	if (err < 0)
543d5ac70f0Sopenharmony_ci		return err;
544d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, conf) {
545d5ac70f0Sopenharmony_ci		n = snd_config_iterator_entry(i);
546d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &str) < 0)
547d5ac70f0Sopenharmony_ci			continue;
548d5ac70f0Sopenharmony_ci		list->card = -1;
549d5ac70f0Sopenharmony_ci		list->device = -1;
550d5ac70f0Sopenharmony_ci		err = try_config(rw_config, list, list->siface, str);
551d5ac70f0Sopenharmony_ci		if (err == -ENOMEM)
552d5ac70f0Sopenharmony_ci			return -ENOMEM;
553d5ac70f0Sopenharmony_ci	}
554d5ac70f0Sopenharmony_ci	return 0;
555d5ac70f0Sopenharmony_ci}
556d5ac70f0Sopenharmony_ci
557d5ac70f0Sopenharmony_ci/**
558d5ac70f0Sopenharmony_ci * \brief Get a set of device name hints
559d5ac70f0Sopenharmony_ci * \param card Card number or -1 (means all cards)
560d5ac70f0Sopenharmony_ci * \param iface Interface identification (like "pcm", "rawmidi", "timer", "seq")
561d5ac70f0Sopenharmony_ci * \param hints Result - array of device name hints
562d5ac70f0Sopenharmony_ci * \result zero if success, otherwise a negative error code
563d5ac70f0Sopenharmony_ci *
564d5ac70f0Sopenharmony_ci * hints will receive a NULL-terminated array of device name hints,
565d5ac70f0Sopenharmony_ci * which can be passed to #snd_device_name_get_hint to extract usable
566d5ac70f0Sopenharmony_ci * values. When no longer needed, hints should be passed to
567d5ac70f0Sopenharmony_ci * #snd_device_name_free_hint to release resources.
568d5ac70f0Sopenharmony_ci *
569d5ac70f0Sopenharmony_ci * User-defined hints are gathered from namehint.IFACE tree like:
570d5ac70f0Sopenharmony_ci *
571d5ac70f0Sopenharmony_ci * <code>
572d5ac70f0Sopenharmony_ci * namehint.pcm [<br>
573d5ac70f0Sopenharmony_ci *   myfile "file:FILE=/tmp/soundwave.raw|Save sound output to /tmp/soundwave.raw"<br>
574d5ac70f0Sopenharmony_ci *   myplug "plug:front|Do all conversions for front speakers"<br>
575d5ac70f0Sopenharmony_ci * ]
576d5ac70f0Sopenharmony_ci * </code>
577d5ac70f0Sopenharmony_ci *
578d5ac70f0Sopenharmony_ci * Note: The device description is separated with '|' char.
579d5ac70f0Sopenharmony_ci *
580d5ac70f0Sopenharmony_ci * Special variables: defaults.namehint.showall specifies if all device
581d5ac70f0Sopenharmony_ci * definitions are accepted (boolean type).
582d5ac70f0Sopenharmony_ci */
583d5ac70f0Sopenharmony_ciint snd_device_name_hint(int card, const char *iface, void ***hints)
584d5ac70f0Sopenharmony_ci{
585d5ac70f0Sopenharmony_ci	struct hint_list list;
586d5ac70f0Sopenharmony_ci	char ehints[24];
587d5ac70f0Sopenharmony_ci	const char *str;
588d5ac70f0Sopenharmony_ci	snd_config_t *conf, *local_config = NULL, *local_config_rw = NULL;
589d5ac70f0Sopenharmony_ci	snd_config_update_t *local_config_update = NULL;
590d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
591d5ac70f0Sopenharmony_ci	int err;
592d5ac70f0Sopenharmony_ci
593d5ac70f0Sopenharmony_ci	if (hints == NULL)
594d5ac70f0Sopenharmony_ci		return -EINVAL;
595d5ac70f0Sopenharmony_ci	err = snd_config_update_r(&local_config, &local_config_update, NULL);
596d5ac70f0Sopenharmony_ci	if (err < 0)
597d5ac70f0Sopenharmony_ci		return err;
598d5ac70f0Sopenharmony_ci	err = snd_config_copy(&local_config_rw, local_config);
599d5ac70f0Sopenharmony_ci	if (err < 0)
600d5ac70f0Sopenharmony_ci		return err;
601d5ac70f0Sopenharmony_ci	list.list = NULL;
602d5ac70f0Sopenharmony_ci	list.count = list.allocated = 0;
603d5ac70f0Sopenharmony_ci	list.siface = iface;
604d5ac70f0Sopenharmony_ci	list.show_all = 0;
605d5ac70f0Sopenharmony_ci	list.cardname = NULL;
606d5ac70f0Sopenharmony_ci	if (strcmp(iface, "pcm") == 0)
607d5ac70f0Sopenharmony_ci		list.iface = SND_CTL_ELEM_IFACE_PCM;
608d5ac70f0Sopenharmony_ci	else if (strcmp(iface, "rawmidi") == 0)
609d5ac70f0Sopenharmony_ci		list.iface = SND_CTL_ELEM_IFACE_RAWMIDI;
610d5ac70f0Sopenharmony_ci	else if (strcmp(iface, "timer") == 0)
611d5ac70f0Sopenharmony_ci		list.iface = SND_CTL_ELEM_IFACE_TIMER;
612d5ac70f0Sopenharmony_ci	else if (strcmp(iface, "seq") == 0)
613d5ac70f0Sopenharmony_ci		list.iface = SND_CTL_ELEM_IFACE_SEQUENCER;
614d5ac70f0Sopenharmony_ci	else if (strcmp(iface, "hwdep") == 0)
615d5ac70f0Sopenharmony_ci		list.iface = SND_CTL_ELEM_IFACE_HWDEP;
616d5ac70f0Sopenharmony_ci	else if (strcmp(iface, "ctl") == 0)
617d5ac70f0Sopenharmony_ci		list.iface = SND_CTL_ELEM_IFACE_MIXER;
618d5ac70f0Sopenharmony_ci	else {
619d5ac70f0Sopenharmony_ci		err = -EINVAL;
620d5ac70f0Sopenharmony_ci		goto __error;
621d5ac70f0Sopenharmony_ci	}
622d5ac70f0Sopenharmony_ci
623d5ac70f0Sopenharmony_ci	if (snd_config_search(local_config, "defaults.namehint.showall", &conf) >= 0)
624d5ac70f0Sopenharmony_ci		list.show_all = snd_config_get_bool(conf) > 0;
625d5ac70f0Sopenharmony_ci	if (card >= 0) {
626d5ac70f0Sopenharmony_ci		err = get_card_name(&list, card);
627d5ac70f0Sopenharmony_ci		if (err >= 0)
628d5ac70f0Sopenharmony_ci			err = add_card(local_config, local_config_rw, &list, card);
629d5ac70f0Sopenharmony_ci	} else {
630d5ac70f0Sopenharmony_ci		add_software_devices(local_config, local_config_rw, &list);
631d5ac70f0Sopenharmony_ci		err = snd_card_next(&card);
632d5ac70f0Sopenharmony_ci		if (err < 0)
633d5ac70f0Sopenharmony_ci			goto __error;
634d5ac70f0Sopenharmony_ci		while (card >= 0) {
635d5ac70f0Sopenharmony_ci			err = get_card_name(&list, card);
636d5ac70f0Sopenharmony_ci			if (err < 0)
637d5ac70f0Sopenharmony_ci				goto __error;
638d5ac70f0Sopenharmony_ci			err = add_card(local_config, local_config_rw, &list, card);
639d5ac70f0Sopenharmony_ci			if (err < 0)
640d5ac70f0Sopenharmony_ci				goto __error;
641d5ac70f0Sopenharmony_ci			err = snd_card_next(&card);
642d5ac70f0Sopenharmony_ci			if (err < 0)
643d5ac70f0Sopenharmony_ci				goto __error;
644d5ac70f0Sopenharmony_ci		}
645d5ac70f0Sopenharmony_ci	}
646d5ac70f0Sopenharmony_ci	sprintf(ehints, "namehint.%s", list.siface);
647d5ac70f0Sopenharmony_ci	err = snd_config_search(local_config, ehints, &conf);
648d5ac70f0Sopenharmony_ci	if (err >= 0) {
649d5ac70f0Sopenharmony_ci		snd_config_for_each(i, next, conf) {
650d5ac70f0Sopenharmony_ci			if (snd_config_get_string(snd_config_iterator_entry(i),
651d5ac70f0Sopenharmony_ci						  &str) < 0)
652d5ac70f0Sopenharmony_ci				continue;
653d5ac70f0Sopenharmony_ci			err = hint_list_add_custom(&list, str);
654d5ac70f0Sopenharmony_ci			if (err < 0)
655d5ac70f0Sopenharmony_ci				goto __error;
656d5ac70f0Sopenharmony_ci		}
657d5ac70f0Sopenharmony_ci	}
658d5ac70f0Sopenharmony_ci	err = 0;
659d5ac70f0Sopenharmony_ci      __error:
660d5ac70f0Sopenharmony_ci	/* add an empty entry if nothing has been added yet; the caller
661d5ac70f0Sopenharmony_ci	 * expects non-NULL return
662d5ac70f0Sopenharmony_ci	 */
663d5ac70f0Sopenharmony_ci	if (!err && !list.list)
664d5ac70f0Sopenharmony_ci		err = hint_list_add(&list, NULL, NULL);
665d5ac70f0Sopenharmony_ci	if (err < 0)
666d5ac70f0Sopenharmony_ci      		snd_device_name_free_hint((void **)list.list);
667d5ac70f0Sopenharmony_ci	else
668d5ac70f0Sopenharmony_ci      		*hints = (void **)list.list;
669d5ac70f0Sopenharmony_ci	free(list.cardname);
670d5ac70f0Sopenharmony_ci	if (local_config_rw)
671d5ac70f0Sopenharmony_ci		snd_config_delete(local_config_rw);
672d5ac70f0Sopenharmony_ci	if (local_config)
673d5ac70f0Sopenharmony_ci		snd_config_delete(local_config);
674d5ac70f0Sopenharmony_ci	if (local_config_update)
675d5ac70f0Sopenharmony_ci		snd_config_update_free(local_config_update);
676d5ac70f0Sopenharmony_ci	return err;
677d5ac70f0Sopenharmony_ci}
678d5ac70f0Sopenharmony_ci
679d5ac70f0Sopenharmony_ci/**
680d5ac70f0Sopenharmony_ci * \brief Free a list of device name hints.
681d5ac70f0Sopenharmony_ci * \param hints List to free
682d5ac70f0Sopenharmony_ci * \result zero if success, otherwise a negative error code
683d5ac70f0Sopenharmony_ci */
684d5ac70f0Sopenharmony_ciint snd_device_name_free_hint(void **hints)
685d5ac70f0Sopenharmony_ci{
686d5ac70f0Sopenharmony_ci	char **h;
687d5ac70f0Sopenharmony_ci
688d5ac70f0Sopenharmony_ci	if (hints == NULL)
689d5ac70f0Sopenharmony_ci		return 0;
690d5ac70f0Sopenharmony_ci	h = (char **)hints;
691d5ac70f0Sopenharmony_ci	while (*h) {
692d5ac70f0Sopenharmony_ci		free(*h);
693d5ac70f0Sopenharmony_ci		h++;
694d5ac70f0Sopenharmony_ci	}
695d5ac70f0Sopenharmony_ci	free(hints);
696d5ac70f0Sopenharmony_ci	return 0;
697d5ac70f0Sopenharmony_ci}
698d5ac70f0Sopenharmony_ci
699d5ac70f0Sopenharmony_ci/**
700d5ac70f0Sopenharmony_ci * \brief Extract a value from a hint
701d5ac70f0Sopenharmony_ci * \param hint A pointer to hint
702d5ac70f0Sopenharmony_ci * \param id Hint value to extract ("NAME", "DESC", or "IOID", see below)
703d5ac70f0Sopenharmony_ci * \result an allocated ASCII string if success, otherwise NULL
704d5ac70f0Sopenharmony_ci *
705d5ac70f0Sopenharmony_ci * List of valid IDs:
706d5ac70f0Sopenharmony_ci * NAME - name of device
707d5ac70f0Sopenharmony_ci * DESC - description of device
708d5ac70f0Sopenharmony_ci * IOID - input / output identification ("Input" or "Output"), NULL means both
709d5ac70f0Sopenharmony_ci *
710d5ac70f0Sopenharmony_ci * The return value should be freed when no longer needed.
711d5ac70f0Sopenharmony_ci */
712d5ac70f0Sopenharmony_cichar *snd_device_name_get_hint(const void *hint, const char *id)
713d5ac70f0Sopenharmony_ci{
714d5ac70f0Sopenharmony_ci	const char *hint1 = (const char *)hint, *delim;
715d5ac70f0Sopenharmony_ci	char *res;
716d5ac70f0Sopenharmony_ci	unsigned size;
717d5ac70f0Sopenharmony_ci
718d5ac70f0Sopenharmony_ci	if (strlen(id) != 4)
719d5ac70f0Sopenharmony_ci		return NULL;
720d5ac70f0Sopenharmony_ci	while (*hint1 != '\0') {
721d5ac70f0Sopenharmony_ci		delim = strchr(hint1, '|');
722d5ac70f0Sopenharmony_ci		if (memcmp(id, hint1, 4) != 0) {
723d5ac70f0Sopenharmony_ci			if (delim == NULL)
724d5ac70f0Sopenharmony_ci				return NULL;
725d5ac70f0Sopenharmony_ci			hint1 = delim + 1;
726d5ac70f0Sopenharmony_ci			continue;
727d5ac70f0Sopenharmony_ci		}
728d5ac70f0Sopenharmony_ci		if (delim == NULL)
729d5ac70f0Sopenharmony_ci			return strdup(hint1 + 4);
730d5ac70f0Sopenharmony_ci		size = delim - hint1 - 4;
731d5ac70f0Sopenharmony_ci		res = malloc(size + 1);
732d5ac70f0Sopenharmony_ci		if (res != NULL) {
733d5ac70f0Sopenharmony_ci			memcpy(res, hint1 + 4, size);
734d5ac70f0Sopenharmony_ci			res[size] = '\0';
735d5ac70f0Sopenharmony_ci		}
736d5ac70f0Sopenharmony_ci		return res;
737d5ac70f0Sopenharmony_ci	}
738d5ac70f0Sopenharmony_ci	return NULL;
739d5ac70f0Sopenharmony_ci}
740