1 /**
2  * \file control/control_hw.c
3  * \brief CTL HW Plugin Interface
4  * \author Jaroslav Kysela <perex@perex.cz>
5  * \date 2000
6  */
7 /*
8  *  Control Interface - Hardware
9  *  Copyright (c) 1998,1999,2000 by Jaroslav Kysela <perex@perex.cz>
10  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
11  *
12  *
13  *   This library is free software; you can redistribute it and/or modify
14  *   it under the terms of the GNU Lesser General Public License as
15  *   published by the Free Software Foundation; either version 2.1 of
16  *   the License, or (at your option) any later version.
17  *
18  *   This program is distributed in the hope that it will be useful,
19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *   GNU Lesser General Public License for more details.
22  *
23  *   You should have received a copy of the GNU Lesser General Public
24  *   License along with this library; if not, write to the Free Software
25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26  *
27  */
28 
29 #include "control_local.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <sys/ioctl.h>
37 
38 #ifndef PIC
39 /* entry for static linking */
40 const char *_snd_module_control_hw = "";
41 #endif
42 
43 #ifndef DOC_HIDDEN
44 
45 #ifndef F_SETSIG
46 #define F_SETSIG 10
47 #endif
48 
49 #define SNDRV_FILE_CONTROL	ALSA_DEVICE_DIRECTORY "controlC%i"
50 #define SNDRV_CTL_VERSION_MAX	SNDRV_PROTOCOL_VERSION(2, 0, 4)
51 
52 typedef struct {
53 	int card;
54 	int fd;
55 	unsigned int protocol;
56 } snd_ctl_hw_t;
57 #endif /* DOC_HIDDEN */
58 
snd_ctl_hw_close(snd_ctl_t *handle)59 static int snd_ctl_hw_close(snd_ctl_t *handle)
60 {
61 	snd_ctl_hw_t *hw = handle->private_data;
62 	int res;
63 	res = close(hw->fd) < 0 ? -errno : 0;
64 	free(hw);
65 	return res;
66 }
67 
snd_ctl_hw_nonblock(snd_ctl_t *handle, int nonblock)68 static int snd_ctl_hw_nonblock(snd_ctl_t *handle, int nonblock)
69 {
70 	snd_ctl_hw_t *hw = handle->private_data;
71 	long flags;
72 	int fd = hw->fd;
73 	if ((flags = fcntl(fd, F_GETFL)) < 0) {
74 		SYSERR("F_GETFL failed");
75 		return -errno;
76 	}
77 	if (nonblock)
78 		flags |= O_NONBLOCK;
79 	else
80 		flags &= ~O_NONBLOCK;
81 	if (fcntl(fd, F_SETFL, flags) < 0) {
82 		SYSERR("F_SETFL for O_NONBLOCK failed");
83 		return -errno;
84 	}
85 	return 0;
86 }
87 
snd_ctl_hw_async(snd_ctl_t *ctl, int sig, pid_t pid)88 static int snd_ctl_hw_async(snd_ctl_t *ctl, int sig, pid_t pid)
89 {
90 	long flags;
91 	snd_ctl_hw_t *hw = ctl->private_data;
92 	int fd = hw->fd;
93 
94 	if ((flags = fcntl(fd, F_GETFL)) < 0) {
95 		SYSERR("F_GETFL failed");
96 		return -errno;
97 	}
98 	if (sig >= 0)
99 		flags |= O_ASYNC;
100 	else
101 		flags &= ~O_ASYNC;
102 	if (fcntl(fd, F_SETFL, flags) < 0) {
103 		SYSERR("F_SETFL for O_ASYNC failed");
104 		return -errno;
105 	}
106 	if (sig < 0)
107 		return 0;
108 	if (fcntl(fd, F_SETSIG, (long)sig) < 0) {
109 		SYSERR("F_SETSIG failed");
110 		return -errno;
111 	}
112 	if (fcntl(fd, F_SETOWN, (long)pid) < 0) {
113 		SYSERR("F_SETOWN failed");
114 		return -errno;
115 	}
116 	return 0;
117 }
118 
snd_ctl_hw_subscribe_events(snd_ctl_t *handle, int subscribe)119 static int snd_ctl_hw_subscribe_events(snd_ctl_t *handle, int subscribe)
120 {
121 	snd_ctl_hw_t *hw = handle->private_data;
122 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, &subscribe) < 0) {
123 		SYSERR("SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS failed");
124 		return -errno;
125 	}
126 	return 0;
127 }
128 
snd_ctl_hw_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info)129 static int snd_ctl_hw_card_info(snd_ctl_t *handle, snd_ctl_card_info_t *info)
130 {
131 	snd_ctl_hw_t *hw = handle->private_data;
132 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_CARD_INFO, info) < 0) {
133 		SYSERR("SNDRV_CTL_IOCTL_CARD_INFO failed");
134 		return -errno;
135 	}
136 	return 0;
137 }
138 
snd_ctl_hw_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list)139 static int snd_ctl_hw_elem_list(snd_ctl_t *handle, snd_ctl_elem_list_t *list)
140 {
141 	snd_ctl_hw_t *hw = handle->private_data;
142 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LIST, list) < 0)
143 		return -errno;
144 	return 0;
145 }
146 
snd_ctl_hw_elem_info(snd_ctl_t *handle, snd_ctl_elem_info_t *info)147 static int snd_ctl_hw_elem_info(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
148 {
149 	snd_ctl_hw_t *hw = handle->private_data;
150 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_INFO, info) < 0)
151 		return -errno;
152 	return 0;
153 }
154 
snd_ctl_hw_elem_add(snd_ctl_t *handle, snd_ctl_elem_info_t *info)155 static int snd_ctl_hw_elem_add(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
156 {
157 	snd_ctl_hw_t *hw = handle->private_data;
158 
159 	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
160 	    hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 7))
161 		return -ENXIO;
162 
163 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_ADD, info) < 0)
164 		return -errno;
165 	return 0;
166 }
167 
snd_ctl_hw_elem_replace(snd_ctl_t *handle, snd_ctl_elem_info_t *info)168 static int snd_ctl_hw_elem_replace(snd_ctl_t *handle, snd_ctl_elem_info_t *info)
169 {
170 	snd_ctl_hw_t *hw = handle->private_data;
171 
172 	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
173 	    hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 7))
174 		return -ENXIO;
175 
176 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REPLACE, info) < 0)
177 		return -errno;
178 	return 0;
179 }
180 
snd_ctl_hw_elem_remove(snd_ctl_t *handle, snd_ctl_elem_id_t *id)181 static int snd_ctl_hw_elem_remove(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
182 {
183 	snd_ctl_hw_t *hw = handle->private_data;
184 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_REMOVE, id) < 0)
185 		return -errno;
186 	return 0;
187 }
188 
snd_ctl_hw_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *control)189 static int snd_ctl_hw_elem_read(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
190 {
191 	snd_ctl_hw_t *hw = handle->private_data;
192 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_READ, control) < 0)
193 		return -errno;
194 	return 0;
195 }
196 
snd_ctl_hw_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control)197 static int snd_ctl_hw_elem_write(snd_ctl_t *handle, snd_ctl_elem_value_t *control)
198 {
199 	snd_ctl_hw_t *hw = handle->private_data;
200 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, control) < 0)
201 		return -errno;
202 	return 0;
203 }
204 
snd_ctl_hw_elem_lock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)205 static int snd_ctl_hw_elem_lock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
206 {
207 	snd_ctl_hw_t *hw = handle->private_data;
208 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_LOCK, id) < 0)
209 		return -errno;
210 	return 0;
211 }
212 
snd_ctl_hw_elem_unlock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)213 static int snd_ctl_hw_elem_unlock(snd_ctl_t *handle, snd_ctl_elem_id_t *id)
214 {
215 	snd_ctl_hw_t *hw = handle->private_data;
216 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_ELEM_UNLOCK, id) < 0)
217 		return -errno;
218 	return 0;
219 }
220 
snd_ctl_hw_elem_tlv(snd_ctl_t *handle, int op_flag, unsigned int numid, unsigned int *tlv, unsigned int tlv_size)221 static int snd_ctl_hw_elem_tlv(snd_ctl_t *handle, int op_flag,
222 			       unsigned int numid,
223 			       unsigned int *tlv, unsigned int tlv_size)
224 {
225 	unsigned int inum;
226 	snd_ctl_hw_t *hw = handle->private_data;
227 	struct snd_ctl_tlv *xtlv;
228 
229 	/* we don't support TLV on protocol ver 2.0.3 or earlier */
230 	if (hw->protocol < SNDRV_PROTOCOL_VERSION(2, 0, 4))
231 		return -ENXIO;
232 
233 	switch (op_flag) {
234 	case -1: inum = SNDRV_CTL_IOCTL_TLV_COMMAND; break;
235  	case 0:	inum = SNDRV_CTL_IOCTL_TLV_READ; break;
236 	case 1:	inum = SNDRV_CTL_IOCTL_TLV_WRITE; break;
237 	default: return -EINVAL;
238 	}
239 	xtlv = malloc(sizeof(struct snd_ctl_tlv) + tlv_size);
240 	if (xtlv == NULL)
241 		return -ENOMEM;
242 	xtlv->numid = numid;
243 	xtlv->length = tlv_size;
244 	memcpy(xtlv->tlv, tlv, tlv_size);
245 	if (ioctl(hw->fd, inum, xtlv) < 0) {
246 		free(xtlv);
247 		return -errno;
248 	}
249 	if (op_flag == 0) {
250 		unsigned int size;
251 		size = xtlv->tlv[SNDRV_CTL_TLVO_LEN] + 2 * sizeof(unsigned int);
252 		if (size > tlv_size) {
253 			free(xtlv);
254 			return -EFAULT;
255 		}
256 		memcpy(tlv, xtlv->tlv, size);
257 	}
258 	free(xtlv);
259 	return 0;
260 }
261 
snd_ctl_hw_hwdep_next_device(snd_ctl_t *handle, int * device)262 static int snd_ctl_hw_hwdep_next_device(snd_ctl_t *handle, int * device)
263 {
264 	snd_ctl_hw_t *hw = handle->private_data;
265 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, device) < 0)
266 		return -errno;
267 	return 0;
268 }
269 
snd_ctl_hw_hwdep_info(snd_ctl_t *handle, snd_hwdep_info_t * info)270 static int snd_ctl_hw_hwdep_info(snd_ctl_t *handle, snd_hwdep_info_t * info)
271 {
272 	snd_ctl_hw_t *hw = handle->private_data;
273 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_HWDEP_INFO, info) < 0)
274 		return -errno;
275 	return 0;
276 }
277 
snd_ctl_hw_pcm_next_device(snd_ctl_t *handle, int * device)278 static int snd_ctl_hw_pcm_next_device(snd_ctl_t *handle, int * device)
279 {
280 	snd_ctl_hw_t *hw = handle->private_data;
281 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, device) < 0)
282 		return -errno;
283 	return 0;
284 }
285 
snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info)286 static int snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info)
287 {
288 	snd_ctl_hw_t *hw = handle->private_data;
289 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_INFO, info) < 0)
290 		return -errno;
291 	/* may be configurable (optional) */
292 	if (__snd_pcm_info_eld_fixup_check(info))
293 		return __snd_pcm_info_eld_fixup(info);
294 	return 0;
295 }
296 
snd_ctl_hw_pcm_prefer_subdevice(snd_ctl_t *handle, int subdev)297 static int snd_ctl_hw_pcm_prefer_subdevice(snd_ctl_t *handle, int subdev)
298 {
299 	snd_ctl_hw_t *hw = handle->private_data;
300 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, &subdev) < 0)
301 		return -errno;
302 	return 0;
303 }
304 
snd_ctl_hw_rawmidi_next_device(snd_ctl_t *handle, int * device)305 static int snd_ctl_hw_rawmidi_next_device(snd_ctl_t *handle, int * device)
306 {
307 	snd_ctl_hw_t *hw = handle->private_data;
308 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE, device) < 0)
309 		return -errno;
310 	return 0;
311 }
312 
snd_ctl_hw_rawmidi_info(snd_ctl_t *handle, snd_rawmidi_info_t * info)313 static int snd_ctl_hw_rawmidi_info(snd_ctl_t *handle, snd_rawmidi_info_t * info)
314 {
315 	snd_ctl_hw_t *hw = handle->private_data;
316 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, info) < 0)
317 		return -errno;
318 	return 0;
319 }
320 
snd_ctl_hw_rawmidi_prefer_subdevice(snd_ctl_t *handle, int subdev)321 static int snd_ctl_hw_rawmidi_prefer_subdevice(snd_ctl_t *handle, int subdev)
322 {
323 	snd_ctl_hw_t *hw = handle->private_data;
324 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE, &subdev) < 0)
325 		return -errno;
326 	return 0;
327 }
328 
snd_ctl_hw_ump_next_device(snd_ctl_t *handle, int *device)329 static int snd_ctl_hw_ump_next_device(snd_ctl_t *handle, int *device)
330 {
331 	snd_ctl_hw_t *hw = handle->private_data;
332 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE, device) < 0)
333 		return -errno;
334 	return 0;
335 }
336 
snd_ctl_hw_ump_endpoint_info(snd_ctl_t *handle, snd_ump_endpoint_info_t *info)337 static int snd_ctl_hw_ump_endpoint_info(snd_ctl_t *handle,
338 					snd_ump_endpoint_info_t *info)
339 {
340 	snd_ctl_hw_t *hw = handle->private_data;
341 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO, info) < 0)
342 		return -errno;
343 	return 0;
344 }
345 
snd_ctl_hw_ump_block_info(snd_ctl_t *handle, snd_ump_block_info_t *info)346 static int snd_ctl_hw_ump_block_info(snd_ctl_t *handle,
347 				     snd_ump_block_info_t *info)
348 {
349 	snd_ctl_hw_t *hw = handle->private_data;
350 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_BLOCK_INFO, info) < 0)
351 		return -errno;
352 	return 0;
353 }
354 
snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state)355 static int snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state)
356 {
357 	snd_ctl_hw_t *hw = handle->private_data;
358 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER, &state) < 0)
359 		return -errno;
360 	return 0;
361 }
362 
snd_ctl_hw_get_power_state(snd_ctl_t *handle, unsigned int *state)363 static int snd_ctl_hw_get_power_state(snd_ctl_t *handle, unsigned int *state)
364 {
365 	snd_ctl_hw_t *hw = handle->private_data;
366 	if (ioctl(hw->fd, SNDRV_CTL_IOCTL_POWER_STATE, state) < 0)
367 		return -errno;
368 	return 0;
369 }
370 
snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event)371 static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event)
372 {
373 	snd_ctl_hw_t *hw = handle->private_data;
374 	ssize_t res = read(hw->fd, event, sizeof(*event));
375 	if (res <= 0)
376 		return -errno;
377 	if (CHECK_SANITY(res != sizeof(*event))) {
378 		SNDMSG("snd_ctl_hw_read: read size error (req:%d, got:%d)",
379 		       sizeof(*event), res);
380 		return -EINVAL;
381 	}
382 	return 1;
383 }
384 
385 static const snd_ctl_ops_t snd_ctl_hw_ops = {
386 	.close = snd_ctl_hw_close,
387 	.nonblock = snd_ctl_hw_nonblock,
388 	.async = snd_ctl_hw_async,
389 	.subscribe_events = snd_ctl_hw_subscribe_events,
390 	.card_info = snd_ctl_hw_card_info,
391 	.element_list = snd_ctl_hw_elem_list,
392 	.element_info = snd_ctl_hw_elem_info,
393 	.element_add = snd_ctl_hw_elem_add,
394 	.element_replace = snd_ctl_hw_elem_replace,
395 	.element_remove = snd_ctl_hw_elem_remove,
396 	.element_read = snd_ctl_hw_elem_read,
397 	.element_write = snd_ctl_hw_elem_write,
398 	.element_lock = snd_ctl_hw_elem_lock,
399 	.element_unlock = snd_ctl_hw_elem_unlock,
400 	.element_tlv = snd_ctl_hw_elem_tlv,
401 	.hwdep_next_device = snd_ctl_hw_hwdep_next_device,
402 	.hwdep_info = snd_ctl_hw_hwdep_info,
403 	.pcm_next_device = snd_ctl_hw_pcm_next_device,
404 	.pcm_info = snd_ctl_hw_pcm_info,
405 	.pcm_prefer_subdevice = snd_ctl_hw_pcm_prefer_subdevice,
406 	.rawmidi_next_device = snd_ctl_hw_rawmidi_next_device,
407 	.rawmidi_info = snd_ctl_hw_rawmidi_info,
408 	.rawmidi_prefer_subdevice = snd_ctl_hw_rawmidi_prefer_subdevice,
409 	.ump_next_device = snd_ctl_hw_ump_next_device,
410 	.ump_endpoint_info = snd_ctl_hw_ump_endpoint_info,
411 	.ump_block_info = snd_ctl_hw_ump_block_info,
412 	.set_power_state = snd_ctl_hw_set_power_state,
413 	.get_power_state = snd_ctl_hw_get_power_state,
414 	.read = snd_ctl_hw_read,
415 };
416 
417 /**
418  * \brief Creates a new hw control
419  * \param handle Returns created control handle
420  * \param name Name of control device
421  * \param card Number of card
422  * \param mode Control mode
423  * \retval zero on success otherwise a negative error code
424  * \warning Using of this function might be dangerous in the sense
425  *          of compatibility reasons. The prototype might be freely
426  *          changed in future.
427  */
snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode)428 int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode)
429 {
430 	int fd, ver;
431 	char filename[sizeof(SNDRV_FILE_CONTROL) + 10];
432 	int fmode;
433 	snd_ctl_t *ctl;
434 	snd_ctl_hw_t *hw;
435 	int err;
436 
437 	*handle = NULL;
438 
439 	if (CHECK_SANITY(card < 0 || card >= SND_MAX_CARDS)) {
440 		SNDMSG("Invalid card index %d", card);
441 		return -EINVAL;
442 	}
443 	sprintf(filename, SNDRV_FILE_CONTROL, card);
444 	if (mode & SND_CTL_READONLY)
445 		fmode = O_RDONLY;
446 	else
447 		fmode = O_RDWR;
448 	if (mode & SND_CTL_NONBLOCK)
449 		fmode |= O_NONBLOCK;
450 	if (mode & SND_CTL_ASYNC)
451 		fmode |= O_ASYNC;
452 	fd = snd_open_device(filename, fmode);
453 	if (fd < 0) {
454 		snd_card_load(card);
455 		fd = snd_open_device(filename, fmode);
456 		if (fd < 0)
457 			return -errno;
458 	}
459 	if (ioctl(fd, SNDRV_CTL_IOCTL_PVERSION, &ver) < 0) {
460 		err = -errno;
461 		close(fd);
462 		return err;
463 	}
464 	if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_CTL_VERSION_MAX)) {
465 		close(fd);
466 		return -SND_ERROR_INCOMPATIBLE_VERSION;
467 	}
468 	hw = calloc(1, sizeof(snd_ctl_hw_t));
469 	if (hw == NULL) {
470 		close(fd);
471 		return -ENOMEM;
472 	}
473 	hw->card = card;
474 	hw->fd = fd;
475 	hw->protocol = ver;
476 
477 	err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name, mode);
478 	if (err < 0) {
479 		close(fd);
480 		free(hw);
481 		return err;
482 	}
483 	ctl->ops = &snd_ctl_hw_ops;
484 	ctl->private_data = hw;
485 	ctl->poll_fd = fd;
486 	*handle = ctl;
487 	return 0;
488 }
489 
490 /*! \page control_plugins
491 
492 \section control_plugins_hw Plugin: hw
493 
494 This plugin communicates directly with the ALSA kernel driver. It is a raw
495 communication without any conversions.
496 
497 \code
498 control.name {
499 	type hw			# Kernel PCM
500 	card INT/STR		# Card name (string) or number (integer)
501 }
502 \endcode
503 
504 \subsection control_plugins_hw_funcref Function reference
505 
506 <UL>
507   <LI>snd_ctl_hw_open()
508   <LI>_snd_ctl_hw_open()
509 </UL>
510 
511 */
512 
513 /**
514  * \brief Creates a new hw control handle
515  * \param handlep Returns created control handle
516  * \param name Name of control device
517  * \param root Root configuration node
518  * \param conf Configuration node with hw PCM description
519  * \param mode Control Mode
520  * \warning Using of this function might be dangerous in the sense
521  *          of compatibility reasons. The prototype might be freely
522  *          changed in future.
523  */
_snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode)524 int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode)
525 {
526 	snd_config_iterator_t i, next;
527 	long card = -1;
528 	int err;
529 	snd_config_for_each(i, next, conf) {
530 		snd_config_t *n = snd_config_iterator_entry(i);
531 		const char *id;
532 		if (snd_config_get_id(n, &id) < 0)
533 			continue;
534 		if (_snd_conf_generic_id(id))
535 			continue;
536 		if (strcmp(id, "card") == 0) {
537 			err = snd_config_get_card(n);
538 			if (err < 0)
539 				return err;
540 			card = err;
541 			continue;
542 		}
543 		return -EINVAL;
544 	}
545 	if (card < 0)
546 		return -EINVAL;
547 	return snd_ctl_hw_open(handlep, name, card, mode);
548 }
549 #ifndef DOC_HIDDEN
550 SND_DLSYM_BUILD_VERSION(_snd_ctl_hw_open, SND_CONTROL_DLSYM_VERSION);
551 #endif
552