1/**
2 * \file control/control_remap.c
3 * \brief CTL Remap Plugin Interface
4 * \author Jaroslav Kysela <perex@perex.cz>
5 * \date 2021
6 */
7/*
8 *  Control - Remap Controls
9 *  Copyright (c) 2021 by Jaroslav Kysela <perex@perex.cz>
10 *
11 *
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.
16 *
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.
21 *
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
25 *
26 */
27
28#include "control_local.h"
29#include <stdio.h>
30#include <stdlib.h>
31#include <stdint.h>
32#include <stdarg.h>
33#include <unistd.h>
34#include <string.h>
35
36#ifndef DOC_HIDDEN
37#if 0
38#define REMAP_DEBUG 1
39#define debug(format, args...) fprintf(stderr, format, ##args)
40#define debug_id(id, format, args...) do { \
41	char *s = snd_ctl_ascii_elem_id_get(id); \
42	fprintf(stderr, "%s: ", s); free(s); \
43	fprintf(stderr, format, ##args); \
44} while (0)
45#else
46#define REMAP_DEBUG 0
47#define debug(format, args...) do { } while (0)
48#define debug_id(id, format, args...) do { } while (0)
49#endif
50
51#define EREMAPNOTFOUND (888899)
52#endif /* DOC_HIDDEN */
53
54#ifndef PIC
55/* entry for static linking */
56const char *_snd_module_control_remap = "";
57#endif
58
59#ifndef DOC_HIDDEN
60typedef struct {
61	unsigned int numid_child;
62	unsigned int numid_app;
63} snd_ctl_numid_t;
64
65typedef struct {
66	snd_ctl_elem_id_t id_child;
67	snd_ctl_elem_id_t id_app;
68} snd_ctl_remap_id_t;
69
70typedef struct {
71	snd_ctl_elem_id_t map_id;
72	snd_ctl_elem_type_t type;
73	size_t controls_items;
74	size_t controls_alloc;
75	struct snd_ctl_map_ctl {
76		snd_ctl_elem_id_t id_child;
77		size_t channel_map_items;
78		size_t channel_map_alloc;
79		long *channel_map;
80	} *controls;
81	unsigned int event_mask;
82} snd_ctl_map_t;
83
84typedef struct {
85	snd_ctl_t *child;
86	int numid_remap_active;
87	unsigned int numid_app_last;
88	size_t numid_items;
89	size_t numid_alloc;
90	snd_ctl_numid_t *numid;
91	snd_ctl_numid_t numid_temp;
92	size_t remap_items;
93	size_t remap_alloc;
94	snd_ctl_remap_id_t *remap;
95	size_t map_items;
96	size_t map_alloc;
97	snd_ctl_map_t *map;
98	size_t map_read_queue_head;
99	size_t map_read_queue_tail;
100	snd_ctl_map_t **map_read_queue;
101} snd_ctl_remap_t;
102#endif
103
104static snd_ctl_numid_t *remap_numid_temp(snd_ctl_remap_t *priv, unsigned int numid)
105{
106	priv->numid_temp.numid_child = numid;
107	priv->numid_temp.numid_app = numid;
108	return &priv->numid_temp;
109}
110
111static snd_ctl_numid_t *remap_find_numid_app(snd_ctl_remap_t *priv, unsigned int numid_app)
112{
113	snd_ctl_numid_t *numid;
114	size_t count;
115
116	if (!priv->numid_remap_active)
117		return remap_numid_temp(priv, numid_app);
118	numid = priv->numid;
119	for (count = priv->numid_items; count > 0; count--, numid++)
120		if (numid_app == numid->numid_app)
121			return numid;
122	return NULL;
123}
124
125static snd_ctl_numid_t *remap_numid_new(snd_ctl_remap_t *priv, unsigned int numid_child,
126					unsigned int numid_app)
127{
128	snd_ctl_numid_t *numid;
129
130	if (priv->numid_alloc == priv->numid_items) {
131		numid = realloc(priv->numid, (priv->numid_alloc + 16) * sizeof(*numid));
132		if (numid == NULL)
133			return NULL;
134		memset(numid + priv->numid_alloc, 0, sizeof(*numid) * 16);
135		priv->numid_alloc += 16;
136		priv->numid = numid;
137	}
138	numid = &priv->numid[priv->numid_items++];
139	numid->numid_child = numid_child;
140	numid->numid_app = numid_app;
141	debug("new numid: child %u app %u\n", numid->numid_child, numid->numid_app);
142	return numid;
143}
144
145static snd_ctl_numid_t *remap_numid_child_new(snd_ctl_remap_t *priv, unsigned int numid_child)
146{
147	unsigned int numid_app;
148
149	if (numid_child == 0)
150		return NULL;
151	if (priv->numid_remap_active && remap_find_numid_app(priv, numid_child)) {
152		while (remap_find_numid_app(priv, priv->numid_app_last))
153			priv->numid_app_last++;
154		numid_app = priv->numid_app_last;
155	} else {
156		numid_app = numid_child;
157	}
158	return remap_numid_new(priv, numid_child, numid_app);
159}
160
161static snd_ctl_numid_t *remap_find_numid_child(snd_ctl_remap_t *priv, unsigned int numid_child)
162{
163	snd_ctl_numid_t *numid;
164	size_t count;
165
166	if (!priv->numid_remap_active)
167		return remap_numid_temp(priv, numid_child);
168	numid = priv->numid;
169	for (count = priv->numid_items; count > 0; count--, numid++)
170		if (numid_child == numid->numid_child)
171			return numid;
172	return remap_numid_child_new(priv, numid_child);
173}
174
175static snd_ctl_remap_id_t *remap_find_id_child(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id)
176{
177	size_t count;
178	snd_ctl_remap_id_t *rid;
179
180	if (id->numid > 0) {
181		rid = priv->remap;
182		for (count = priv->remap_items; count > 0; count--, rid++)
183			if (id->numid == rid->id_child.numid)
184				return rid;
185	}
186	rid = priv->remap;
187	for (count = priv->remap_items; count > 0; count--, rid++)
188		if (snd_ctl_elem_id_compare_set(id, &rid->id_child) == 0)
189			return rid;
190	return NULL;
191}
192
193static snd_ctl_remap_id_t *remap_find_id_app(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id)
194{
195	size_t count;
196	snd_ctl_remap_id_t *rid;
197
198	if (id->numid > 0) {
199		rid = priv->remap;
200		for (count = priv->remap_items; count > 0; count--, rid++)
201			if (id->numid == rid->id_app.numid)
202				return rid;
203	}
204	rid = priv->remap;
205	for (count = priv->remap_items; count > 0; count--, rid++)
206		if (snd_ctl_elem_id_compare_set(id, &rid->id_app) == 0)
207			return rid;
208	return NULL;
209}
210
211static snd_ctl_map_t *remap_find_map_numid(snd_ctl_remap_t *priv, unsigned int numid)
212{
213	size_t count;
214	snd_ctl_map_t *map;
215
216	if (numid == 0)
217		return NULL;
218	map = priv->map;
219	for (count = priv->map_items; count > 0; count--, map++) {
220		if (numid == map->map_id.numid)
221			return map;
222	}
223	return NULL;
224}
225
226static snd_ctl_map_t *remap_find_map_id(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id)
227{
228	size_t count;
229	snd_ctl_map_t *map;
230
231	if (id->numid > 0)
232		return remap_find_map_numid(priv, id->numid);
233	map = priv->map;
234	for (count = priv->map_items; count > 0; count--, map++)
235		if (snd_ctl_elem_id_compare_set(id, &map->map_id) == 0)
236			return map;
237	return NULL;
238}
239
240static int remap_id_to_child(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, snd_ctl_remap_id_t **_rid)
241{
242	snd_ctl_remap_id_t *rid;
243	snd_ctl_numid_t *numid;
244
245	debug_id(id, "%s enter\n", __func__);
246	rid = remap_find_id_app(priv, id);
247	if (rid) {
248		if (rid->id_app.numid == 0) {
249			numid = remap_find_numid_app(priv, id->numid);
250			if (numid) {
251				rid->id_child.numid = numid->numid_child;
252				rid->id_app.numid = numid->numid_app;
253			}
254		}
255		*id = rid->id_child;
256	} else {
257		if (remap_find_id_child(priv, id))
258			return -ENOENT;
259		numid = remap_find_numid_app(priv, id->numid);
260		if (numid)
261			id->numid = numid->numid_child;
262		else
263			id->numid = 0;
264	}
265	*_rid = rid;
266	debug_id(id, "%s leave\n", __func__);
267	return 0;
268}
269
270static int remap_id_to_app(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, snd_ctl_remap_id_t *rid, int err)
271{
272	snd_ctl_numid_t *numid;
273
274	if (rid) {
275		if (err >= 0 && rid->id_app.numid == 0) {
276			numid = remap_numid_child_new(priv, id->numid);
277			if (numid == NULL)
278				return -EIO;
279			rid->id_child.numid = numid->numid_child;
280			rid->id_app.numid = numid->numid_app;
281		}
282		*id = rid->id_app;
283	} else {
284		if (err >= 0) {
285			numid = remap_find_numid_child(priv, id->numid);
286			if (numid == NULL)
287				return -EIO;
288			id->numid = numid->numid_app;
289		}
290	}
291	return err;
292}
293
294static void remap_free(snd_ctl_remap_t *priv)
295{
296	size_t idx1, idx2;
297	snd_ctl_map_t *map;
298
299	for (idx1 = 0; idx1 < priv->map_items; idx1++) {
300		map = &priv->map[idx1];
301		for (idx2 = 0; idx2 < map->controls_items; idx2++)
302			free(map->controls[idx2].channel_map);
303		free(map->controls);
304	}
305	free(priv->map_read_queue);
306	free(priv->map);
307	free(priv->remap);
308	free(priv->numid);
309	free(priv);
310}
311
312static int snd_ctl_remap_close(snd_ctl_t *ctl)
313{
314	snd_ctl_remap_t *priv = ctl->private_data;
315	int err = snd_ctl_close(priv->child);
316	remap_free(priv);
317	return err;
318}
319
320static int snd_ctl_remap_nonblock(snd_ctl_t *ctl, int nonblock)
321{
322	snd_ctl_remap_t *priv = ctl->private_data;
323	return snd_ctl_nonblock(priv->child, nonblock);
324}
325
326static int snd_ctl_remap_async(snd_ctl_t *ctl, int sig, pid_t pid)
327{
328	snd_ctl_remap_t *priv = ctl->private_data;
329	return snd_ctl_async(priv->child, sig, pid);
330}
331
332static int snd_ctl_remap_subscribe_events(snd_ctl_t *ctl, int subscribe)
333{
334	snd_ctl_remap_t *priv = ctl->private_data;
335	return snd_ctl_subscribe_events(priv->child, subscribe);
336}
337
338static int snd_ctl_remap_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info)
339{
340	snd_ctl_remap_t *priv = ctl->private_data;
341	return snd_ctl_card_info(priv->child, info);
342}
343
344static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list)
345{
346	snd_ctl_remap_t *priv = ctl->private_data;
347	snd_ctl_elem_id_t *id;
348	snd_ctl_remap_id_t *rid;
349	snd_ctl_numid_t *numid;
350	snd_ctl_map_t *map;
351	unsigned int index;
352	size_t index2;
353	int err;
354
355	err = snd_ctl_elem_list(priv->child, list);
356	if (err < 0)
357		return err;
358	for (index = 0; index < list->used; index++) {
359		id = &list->pids[index];
360		rid = remap_find_id_child(priv, id);
361		if (rid) {
362			rid->id_app.numid = id->numid;
363			*id = rid->id_app;
364		}
365		numid = remap_find_numid_child(priv, id->numid);
366		if (numid == NULL)
367			return -EIO;
368		id->numid = numid->numid_app;
369	}
370	if (list->offset >= list->count + priv->map_items)
371		return 0;
372	index2 = 0;
373	if (list->offset > list->count)
374		index2 = list->offset - list->count;
375	for ( ; index < list->space && index2 < priv->map_items; index2++, index++) {
376		id = &list->pids[index];
377		map = &priv->map[index2];
378		*id = map->map_id;
379		list->used++;
380	}
381	list->count += priv->map_items;
382	return 0;
383}
384
385#ifndef DOC_HIDDEN
386#define ACCESS_BITS(bits) \
387	(bits & (SNDRV_CTL_ELEM_ACCESS_READWRITE|\
388		 SNDRV_CTL_ELEM_ACCESS_VOLATILE|\
389		 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE))
390#endif /* DOC_HIDDEN */
391
392static int remap_map_elem_info(snd_ctl_remap_t *priv, snd_ctl_elem_info_t *info)
393{
394	snd_ctl_map_t *map;
395	snd_ctl_elem_info_t info2, info3;
396	size_t item;
397	unsigned int access;
398	size_t count;
399	int owner, err;
400
401	map = remap_find_map_id(priv, &info->id);
402	if (map == NULL)
403		return -EREMAPNOTFOUND;
404	debug_id(&info->id, "%s\n", __func__);
405	assert(map->controls_items > 0);
406	snd_ctl_elem_info_clear(&info2);
407	info2.id = map->controls[0].id_child;
408	debug_id(&info2.id, "%s controls[0]\n", __func__);
409	err = snd_ctl_elem_info(priv->child, &info2);
410	if (err < 0)
411		return err;
412	if (info2.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
413	    info2.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
414	    info2.type != SNDRV_CTL_ELEM_TYPE_INTEGER64 &&
415	    info2.type != SNDRV_CTL_ELEM_TYPE_BYTES)
416		return -EIO;
417	map->controls[0].id_child.numid = info2.id.numid;
418	map->type = info2.type;
419	access = info2.access;
420	owner = info2.owner;
421	count = map->controls[0].channel_map_items;
422	for (item = 1; item < map->controls_items; item++) {
423		snd_ctl_elem_info_clear(&info3);
424		info3.id = map->controls[item].id_child;
425		debug_id(&info3.id, "%s controls[%zd]\n", __func__, item);
426		err = snd_ctl_elem_info(priv->child, &info3);
427		if (err < 0)
428			return err;
429		if (info2.type != info3.type)
430			return -EIO;
431		if (ACCESS_BITS(info2.access) != ACCESS_BITS(info3.access))
432			return -EIO;
433		if (info2.type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
434		    info2.type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
435			if (memcmp(&info2.value.integer, &info3.value.integer, sizeof(info2.value.integer)))
436				return -EIO;
437		} else if (info2.type == SNDRV_CTL_ELEM_TYPE_INTEGER64) {
438			if (memcmp(&info2.value.integer64, &info3.value.integer64, sizeof(info2.value.integer64)))
439				return -EIO;
440		}
441		access |= info3.access;
442		if (owner == 0)
443			owner = info3.owner;
444		if (count < map->controls[item].channel_map_items)
445			count = map->controls[item].channel_map_items;
446	}
447	snd_ctl_elem_info_clear(info);
448	info->id = map->map_id;
449	info->type = info2.type;
450	info->access = access;
451	info->count = count;
452	if (info2.type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
453	    info2.type == SNDRV_CTL_ELEM_TYPE_INTEGER)
454		info->value.integer = info2.value.integer;
455	else if (info2.type == SNDRV_CTL_ELEM_TYPE_INTEGER64)
456		info->value.integer64 = info2.value.integer64;
457	if (access & SNDRV_CTL_ELEM_ACCESS_LOCK)
458		info->owner = owner;
459	return 0;
460}
461
462static int snd_ctl_remap_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info)
463{
464	snd_ctl_remap_t *priv = ctl->private_data;
465	snd_ctl_remap_id_t *rid;
466	int err;
467
468	debug_id(&info->id, "%s\n", __func__);
469	err = remap_map_elem_info(priv, info);
470	if (err != -EREMAPNOTFOUND)
471		return err;
472	err = remap_id_to_child(priv, &info->id, &rid);
473	if (err < 0)
474		return err;
475	err = snd_ctl_elem_info(priv->child, info);
476	return remap_id_to_app(priv, &info->id, rid, err);
477}
478
479static int remap_map_elem_read(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *control)
480{
481	snd_ctl_map_t *map;
482	struct snd_ctl_map_ctl *mctl;
483	snd_ctl_elem_value_t control2;
484	size_t item, index;
485	int err;
486
487	map = remap_find_map_id(priv, &control->id);
488	if (map == NULL)
489		return -EREMAPNOTFOUND;
490	debug_id(&control->id, "%s\n", __func__);
491	snd_ctl_elem_value_clear(control);
492	control->id = map->map_id;
493	for (item = 0; item < map->controls_items; item++) {
494		mctl = &map->controls[item];
495		snd_ctl_elem_value_clear(&control2);
496		control2.id = mctl->id_child;
497		debug_id(&control2.id, "%s controls[%zd]\n", __func__, item);
498		err = snd_ctl_elem_read(priv->child, &control2);
499		if (err < 0)
500			return err;
501		if (map->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
502		    map->type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
503			for (index = 0; index < mctl->channel_map_items; index++) {
504				long src = mctl->channel_map[index];
505				if ((unsigned long)src < ARRAY_SIZE(control->value.integer.value))
506					control->value.integer.value[index] = control2.value.integer.value[src];
507			}
508		} else if (map->type == SNDRV_CTL_ELEM_TYPE_INTEGER64) {
509			for (index = 0; index < mctl->channel_map_items; index++) {
510				long src = mctl->channel_map[index];
511				if ((unsigned long)src < ARRAY_SIZE(control->value.integer64.value))
512					control->value.integer64.value[index] = control2.value.integer64.value[src];
513			}
514		} else if (map->type == SNDRV_CTL_ELEM_TYPE_BYTES) {
515			for (index = 0; index < mctl->channel_map_items; index++) {
516				long src = mctl->channel_map[index];
517				if ((unsigned long)src < ARRAY_SIZE(control->value.bytes.data))
518					control->value.bytes.data[index] = control2.value.bytes.data[src];
519			}
520		}
521	}
522	return 0;
523}
524
525static int snd_ctl_remap_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *control)
526{
527	snd_ctl_remap_t *priv = ctl->private_data;
528	snd_ctl_remap_id_t *rid;
529	int err;
530
531	debug_id(&control->id, "%s\n", __func__);
532	err = remap_map_elem_read(priv, control);
533	if (err != -EREMAPNOTFOUND)
534		return err;
535	err = remap_id_to_child(priv, &control->id, &rid);
536	if (err < 0)
537		return err;
538	err = snd_ctl_elem_read(priv->child, control);
539	return remap_id_to_app(priv, &control->id, rid, err);
540}
541
542static int remap_map_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *control)
543{
544	snd_ctl_map_t *map;
545	struct snd_ctl_map_ctl *mctl;
546	snd_ctl_elem_value_t control2;
547	size_t item, index;
548	int err, changes;
549
550	map = remap_find_map_id(priv, &control->id);
551	if (map == NULL)
552		return -EREMAPNOTFOUND;
553	debug_id(&control->id, "%s\n", __func__);
554	control->id = map->map_id;
555	for (item = 0; item < map->controls_items; item++) {
556		mctl = &map->controls[item];
557		snd_ctl_elem_value_clear(&control2);
558		control2.id = mctl->id_child;
559		debug_id(&control2.id, "%s controls[%zd]\n", __func__, item);
560		err = snd_ctl_elem_read(priv->child, &control2);
561		if (err < 0)
562			return err;
563		changes = 0;
564		if (map->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
565		    map->type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
566			for (index = 0; index < mctl->channel_map_items; index++) {
567				long dst = mctl->channel_map[index];
568				if ((unsigned long)dst < ARRAY_SIZE(control->value.integer.value)) {
569					changes |= control2.value.integer.value[dst] != control->value.integer.value[index];
570					control2.value.integer.value[dst] = control->value.integer.value[index];
571				}
572			}
573		} else if (map->type == SNDRV_CTL_ELEM_TYPE_INTEGER64) {
574			for (index = 0; index < mctl->channel_map_items; index++) {
575				long dst = mctl->channel_map[index];
576				if ((unsigned long)dst < ARRAY_SIZE(control->value.integer64.value)) {
577					changes |= control2.value.integer64.value[dst] != control->value.integer64.value[index];
578					control2.value.integer64.value[dst] = control->value.integer64.value[index];
579				}
580			}
581		} else if (map->type == SNDRV_CTL_ELEM_TYPE_BYTES) {
582			for (index = 0; index < mctl->channel_map_items; index++) {
583				long dst = mctl->channel_map[index];
584				if ((unsigned long)dst < ARRAY_SIZE(control->value.bytes.data)) {
585					changes |= control2.value.bytes.data[dst] != control->value.bytes.data[index];
586					control2.value.bytes.data[dst] = control->value.bytes.data[index];
587				}
588			}
589		}
590		debug_id(&control2.id, "%s changes %d\n", __func__, changes);
591		if (changes > 0) {
592			err = snd_ctl_elem_write(priv->child, &control2);
593			if (err < 0)
594				return err;
595		}
596	}
597	return 0;
598}
599
600static int snd_ctl_remap_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *control)
601{
602	snd_ctl_remap_t *priv = ctl->private_data;
603	snd_ctl_remap_id_t *rid;
604	int err;
605
606	debug_id(&control->id, "%s\n", __func__);
607	err = remap_map_elem_write(priv, control);
608	if (err != -EREMAPNOTFOUND)
609		return err;
610	err = remap_id_to_child(priv, &control->id, &rid);
611	if (err < 0)
612		return err;
613	err = snd_ctl_elem_write(priv->child, control);
614	return remap_id_to_app(priv, &control->id, rid, err);
615}
616
617static int snd_ctl_remap_elem_lock(snd_ctl_t *ctl, snd_ctl_elem_id_t *id)
618{
619	snd_ctl_remap_t *priv = ctl->private_data;
620	snd_ctl_remap_id_t *rid;
621	int err;
622
623	debug_id(id, "%s\n", __func__);
624	err = remap_id_to_child(priv, id, &rid);
625	if (err < 0)
626		return err;
627	err = snd_ctl_elem_lock(priv->child, id);
628	return remap_id_to_app(priv, id, rid, err);
629}
630
631static int snd_ctl_remap_elem_unlock(snd_ctl_t *ctl, snd_ctl_elem_id_t *id)
632{
633	snd_ctl_remap_t *priv = ctl->private_data;
634	snd_ctl_remap_id_t *rid;
635	int err;
636
637	debug_id(id, "%s\n", __func__);
638	err = remap_id_to_child(priv, id, &rid);
639	if (err < 0)
640		return err;
641	err = snd_ctl_elem_unlock(priv->child, id);
642	return remap_id_to_app(priv, id, rid, err);
643}
644
645static int remap_get_map_numid(snd_ctl_remap_t *priv, struct snd_ctl_map_ctl *mctl)
646{
647	snd_ctl_elem_info_t info;
648	snd_ctl_numid_t *numid;
649	int err;
650
651	if (mctl->id_child.numid > 0)
652		return 0;
653	debug_id(&mctl->id_child, "%s get numid\n", __func__);
654	snd_ctl_elem_info_clear(&info);
655	info.id = mctl->id_child;
656	err = snd_ctl_elem_info(priv->child, &info);
657	if (err < 0)
658		return err;
659	numid = remap_find_numid_child(priv, info.id.numid);
660	if (numid == NULL)
661		return -EIO;
662	mctl->id_child.numid = info.id.numid;
663	return 0;
664}
665
666static int remap_map_elem_tlv(snd_ctl_remap_t *priv, int op_flag, unsigned int numid,
667			      unsigned int *tlv, unsigned int tlv_size)
668{
669	snd_ctl_map_t *map;
670	struct snd_ctl_map_ctl *mctl;
671	size_t item;
672	unsigned int *tlv2;
673	int err;
674
675	map = remap_find_map_numid(priv, numid);
676	if (map == NULL)
677		return -EREMAPNOTFOUND;
678	if (op_flag != 0)	/* read only */
679		return -ENXIO;
680	debug("%s numid %d\n", __func__, numid);
681	mctl = &map->controls[0];
682	err = remap_get_map_numid(priv, mctl);
683	if (err < 0)
684		return err;
685	memset(tlv, 0, tlv_size);
686	err = priv->child->ops->element_tlv(priv->child, op_flag, mctl->id_child.numid, tlv, tlv_size);
687	if (err < 0)
688		return err;
689	tlv2 = malloc(tlv_size);
690	if (tlv2 == NULL)
691		return -ENOMEM;
692	for (item = 1; item < map->controls_items; item++) {
693		mctl = &map->controls[item];
694		err = remap_get_map_numid(priv, mctl);
695		if (err < 0) {
696			free(tlv2);
697			return err;
698		}
699		memset(tlv2, 0, tlv_size);
700		err = priv->child->ops->element_tlv(priv->child, op_flag, mctl->id_child.numid, tlv2, tlv_size);
701		if (err < 0) {
702			free(tlv2);
703			return err;
704		}
705		if (memcmp(tlv, tlv2, tlv_size) != 0) {
706			free(tlv2);
707			return -EIO;
708		}
709	}
710	free(tlv2);
711	return 0;
712}
713
714static int snd_ctl_remap_elem_tlv(snd_ctl_t *ctl, int op_flag,
715				  unsigned int numid,
716				  unsigned int *tlv, unsigned int tlv_size)
717{
718	snd_ctl_remap_t *priv = ctl->private_data;
719	snd_ctl_numid_t *map_numid;
720	int err;
721
722	debug("%s: numid = %d, op_flag = %d\n", __func__, numid, op_flag);
723	err = remap_map_elem_tlv(priv, op_flag, numid, tlv, tlv_size);
724	if (err != -EREMAPNOTFOUND)
725		return err;
726	map_numid = remap_find_numid_app(priv, numid);
727	if (map_numid == NULL)
728		return -ENOENT;
729	return priv->child->ops->element_tlv(priv->child, op_flag, map_numid->numid_child, tlv, tlv_size);
730}
731
732static int snd_ctl_remap_hwdep_next_device(snd_ctl_t *ctl, int * device)
733{
734	snd_ctl_remap_t *priv = ctl->private_data;
735	return snd_ctl_hwdep_next_device(priv->child, device);
736}
737
738static int snd_ctl_remap_hwdep_info(snd_ctl_t *ctl, snd_hwdep_info_t * info)
739{
740	snd_ctl_remap_t *priv = ctl->private_data;
741	return snd_ctl_hwdep_info(priv->child, info);
742}
743
744static int snd_ctl_remap_pcm_next_device(snd_ctl_t *ctl, int * device)
745{
746	snd_ctl_remap_t *priv = ctl->private_data;
747	return snd_ctl_pcm_next_device(priv->child, device);
748}
749
750static int snd_ctl_remap_pcm_info(snd_ctl_t *ctl, snd_pcm_info_t * info)
751{
752	snd_ctl_remap_t *priv = ctl->private_data;
753	return snd_ctl_pcm_info(priv->child, info);
754}
755
756static int snd_ctl_remap_pcm_prefer_subdevice(snd_ctl_t *ctl, int subdev)
757{
758	snd_ctl_remap_t *priv = ctl->private_data;
759	return snd_ctl_pcm_prefer_subdevice(priv->child, subdev);
760}
761
762static int snd_ctl_remap_rawmidi_next_device(snd_ctl_t *ctl, int * device)
763{
764	snd_ctl_remap_t *priv = ctl->private_data;
765	return snd_ctl_rawmidi_next_device(priv->child, device);
766}
767
768static int snd_ctl_remap_rawmidi_info(snd_ctl_t *ctl, snd_rawmidi_info_t * info)
769{
770	snd_ctl_remap_t *priv = ctl->private_data;
771	return snd_ctl_rawmidi_info(priv->child, info);
772}
773
774static int snd_ctl_remap_rawmidi_prefer_subdevice(snd_ctl_t *ctl, int subdev)
775{
776	snd_ctl_remap_t *priv = ctl->private_data;
777	return snd_ctl_rawmidi_prefer_subdevice(priv->child, subdev);
778}
779
780static int snd_ctl_remap_set_power_state(snd_ctl_t *ctl, unsigned int state)
781{
782	snd_ctl_remap_t *priv = ctl->private_data;
783	return snd_ctl_set_power_state(priv->child, state);
784}
785
786static int snd_ctl_remap_get_power_state(snd_ctl_t *ctl, unsigned int *state)
787{
788	snd_ctl_remap_t *priv = ctl->private_data;
789	return snd_ctl_get_power_state(priv->child, state);
790}
791
792static void _next_ptr(size_t *ptr, size_t count)
793{
794	*ptr = (*ptr + 1) % count;
795}
796
797static void remap_event_for_all_map_controls(snd_ctl_remap_t *priv,
798					     snd_ctl_elem_id_t *id,
799					     unsigned int event_mask)
800{
801	size_t count, index, head;
802	snd_ctl_map_t *map;
803	struct snd_ctl_map_ctl *mctl;
804	int found;
805
806	if (event_mask == SNDRV_CTL_EVENT_MASK_REMOVE)
807		event_mask = SNDRV_CTL_EVENT_MASK_INFO;
808	map = priv->map;
809	for (count = priv->map_items; count > 0; count--, map++) {
810		for (index = 0; index < map->controls_items; index++) {
811			mctl = &map->controls[index];
812			if (mctl->id_child.numid == 0) {
813				if (snd_ctl_elem_id_compare_set(id, &mctl->id_child))
814					continue;
815				mctl->id_child.numid = id->numid;
816			}
817			if (id->numid != mctl->id_child.numid)
818				continue;
819			debug_id(&map->map_id, "%s found (all)\n", __func__);
820			map->event_mask |= event_mask;
821			found = 0;
822			for (head = priv->map_read_queue_head;
823			     head != priv->map_read_queue_tail;
824			     _next_ptr(&head, priv->map_items))
825				if (priv->map_read_queue[head] == map) {
826					found = 1;
827					break;
828				}
829			if (found)
830				continue;
831			debug_id(&map->map_id, "%s marking for read\n", __func__);
832			priv->map_read_queue[priv->map_read_queue_tail] = map;
833			_next_ptr(&priv->map_read_queue_tail, priv->map_items);
834		}
835	}
836}
837
838static int snd_ctl_remap_read(snd_ctl_t *ctl, snd_ctl_event_t *event)
839{
840	snd_ctl_remap_t *priv = ctl->private_data;
841	snd_ctl_remap_id_t *rid;
842	snd_ctl_numid_t *numid;
843	snd_ctl_map_t *map;
844	int err;
845
846	if (priv->map_read_queue_head != priv->map_read_queue_tail) {
847		map = priv->map_read_queue[priv->map_read_queue_head];
848		_next_ptr(&priv->map_read_queue_head, priv->map_items);
849		memset(event, 0, sizeof(*event));
850		event->type = SNDRV_CTL_EVENT_ELEM;
851		event->data.elem.mask = map->event_mask;
852		event->data.elem.id = map->map_id;
853		map->event_mask = 0;
854		debug_id(&map->map_id, "%s queue read\n", __func__);
855		return 1;
856	}
857	err = snd_ctl_read(priv->child, event);
858	if (err < 0 || event->type != SNDRV_CTL_EVENT_ELEM)
859		return err;
860	if (event->data.elem.mask == SNDRV_CTL_EVENT_MASK_REMOVE ||
861	    (event->data.elem.mask & (SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO |
862				      SNDRV_CTL_EVENT_MASK_ADD | SNDRV_CTL_EVENT_MASK_TLV)) != 0) {
863		debug_id(&event->data.elem.id, "%s event mask 0x%x\n", __func__, event->data.elem.mask);
864		remap_event_for_all_map_controls(priv, &event->data.elem.id, event->data.elem.mask);
865		rid = remap_find_id_child(priv, &event->data.elem.id);
866		if (rid) {
867			if (rid->id_child.numid == 0) {
868				numid = remap_find_numid_child(priv, event->data.elem.id.numid);
869				if (numid == NULL)
870					return -EIO;
871				rid->id_child.numid = numid->numid_child;
872				rid->id_app.numid = numid->numid_app;
873			}
874			event->data.elem.id = rid->id_app;
875		} else {
876			numid = remap_find_numid_child(priv, event->data.elem.id.numid);
877			if (numid == NULL)
878				return -EIO;
879			event->data.elem.id.numid = numid->numid_app;
880		}
881	}
882	return err;
883}
884
885static const snd_ctl_ops_t snd_ctl_remap_ops = {
886	.close = snd_ctl_remap_close,
887	.nonblock = snd_ctl_remap_nonblock,
888	.async = snd_ctl_remap_async,
889	.subscribe_events = snd_ctl_remap_subscribe_events,
890	.card_info = snd_ctl_remap_card_info,
891	.element_list = snd_ctl_remap_elem_list,
892	.element_info = snd_ctl_remap_elem_info,
893	.element_read = snd_ctl_remap_elem_read,
894	.element_write = snd_ctl_remap_elem_write,
895	.element_lock = snd_ctl_remap_elem_lock,
896	.element_unlock = snd_ctl_remap_elem_unlock,
897	.element_tlv = snd_ctl_remap_elem_tlv,
898	.hwdep_next_device = snd_ctl_remap_hwdep_next_device,
899	.hwdep_info = snd_ctl_remap_hwdep_info,
900	.pcm_next_device = snd_ctl_remap_pcm_next_device,
901	.pcm_info = snd_ctl_remap_pcm_info,
902	.pcm_prefer_subdevice = snd_ctl_remap_pcm_prefer_subdevice,
903	.rawmidi_next_device = snd_ctl_remap_rawmidi_next_device,
904	.rawmidi_info = snd_ctl_remap_rawmidi_info,
905	.rawmidi_prefer_subdevice = snd_ctl_remap_rawmidi_prefer_subdevice,
906	.set_power_state = snd_ctl_remap_set_power_state,
907	.get_power_state = snd_ctl_remap_get_power_state,
908	.read = snd_ctl_remap_read,
909};
910
911static int add_to_remap(snd_ctl_remap_t *priv,
912			snd_ctl_elem_id_t *child,
913			snd_ctl_elem_id_t *app)
914{
915	snd_ctl_remap_id_t *rid;
916
917	if (priv->remap_alloc == priv->remap_items) {
918		rid = realloc(priv->remap, (priv->remap_alloc + 16) * sizeof(*rid));
919		if (rid == NULL)
920			return -ENOMEM;
921		memset(rid + priv->remap_alloc, 0, sizeof(*rid) * 16);
922		priv->remap_alloc += 16;
923		priv->remap = rid;
924	}
925	rid = &priv->remap[priv->remap_items++];
926	rid->id_child = *child;
927	rid->id_app = *app;
928	debug_id(&rid->id_child, "%s remap child\n", __func__);
929	debug_id(&rid->id_app, "%s remap app\n", __func__);
930	return 0;
931}
932
933static int parse_remap(snd_ctl_remap_t *priv, snd_config_t *conf)
934{
935	snd_config_iterator_t i, next;
936	snd_ctl_elem_id_t child, app;
937	int err;
938
939	if (conf == NULL)
940		return 0;
941	snd_config_for_each(i, next, conf) {
942		snd_config_t *n = snd_config_iterator_entry(i);
943		const char *id, *str;
944		if (snd_config_get_id(n, &id) < 0)
945			continue;
946		if (snd_config_get_string(n, &str) < 0) {
947			SNDERR("expected string with the target control id!");
948			return -EINVAL;
949		}
950		snd_ctl_elem_id_clear(&app);
951		err = snd_ctl_ascii_elem_id_parse(&app, str);
952		if (err < 0) {
953			SNDERR("unable to parse target id '%s'!", str);
954			return -EINVAL;
955		}
956		if (remap_find_id_app(priv, &app)) {
957			SNDERR("duplicate target id '%s'!", id);
958			return -EINVAL;
959		}
960		snd_ctl_elem_id_clear(&child);
961		err = snd_ctl_ascii_elem_id_parse(&child, id);
962		if (err < 0) {
963			SNDERR("unable to parse source id '%s'!", id);
964			return -EINVAL;
965		}
966		if (remap_find_id_child(priv, &app)) {
967			SNDERR("duplicate source id '%s'!", id);
968			return -EINVAL;
969		}
970		err = add_to_remap(priv, &child, &app);
971		if (err < 0)
972			return err;
973	}
974
975	return 0;
976}
977
978static int new_map(snd_ctl_remap_t *priv, snd_ctl_map_t **_map, snd_ctl_elem_id_t *id)
979{
980	snd_ctl_map_t *map;
981	snd_ctl_numid_t *numid;
982
983	if (priv->map_alloc == priv->map_items) {
984		map = realloc(priv->map, (priv->map_alloc + 16) * sizeof(*map));
985		if (map == NULL)
986			return -ENOMEM;
987		memset(map + priv->map_alloc, 0, sizeof(*map) * 16);
988		priv->map_alloc += 16;
989		priv->map = map;
990	}
991	map = &priv->map[priv->map_items++];
992	map->map_id = *id;
993	numid = remap_numid_new(priv, 0, ++priv->numid_app_last);
994	if (numid == NULL)
995		return -ENOMEM;
996	map->map_id.numid = numid->numid_app;
997	debug_id(&map->map_id, "%s created\n", __func__);
998	*_map = map;
999	return 0;
1000}
1001
1002static int add_ctl_to_map(snd_ctl_map_t *map, struct snd_ctl_map_ctl **_mctl, snd_ctl_elem_id_t *id)
1003{
1004	struct snd_ctl_map_ctl *mctl;
1005
1006	if (map->controls_alloc == map->controls_items) {
1007		mctl = realloc(map->controls, (map->controls_alloc + 4) * sizeof(*mctl));
1008		if (mctl == NULL)
1009			return -ENOMEM;
1010		memset(mctl + map->controls_alloc, 0, sizeof(*mctl) * 4);
1011		map->controls_alloc += 4;
1012		map->controls = mctl;
1013	}
1014	mctl = &map->controls[map->controls_items++];
1015	mctl->id_child = *id;
1016	*_mctl = mctl;
1017	return 0;
1018}
1019
1020static int add_chn_to_map(struct snd_ctl_map_ctl *mctl, long idx, long val)
1021{
1022	size_t off;
1023	long *map;
1024
1025	if (mctl->channel_map_alloc <= (size_t)idx) {
1026		map = realloc(mctl->channel_map, (idx + 4) * sizeof(*map));
1027		if (map == NULL)
1028			return -ENOMEM;
1029		mctl->channel_map = map;
1030		off = mctl->channel_map_alloc;
1031		mctl->channel_map_alloc = idx + 4;
1032		for ( ; off < mctl->channel_map_alloc; off++)
1033			map[off] = -1;
1034	}
1035	if ((size_t)idx >= mctl->channel_map_items)
1036		mctl->channel_map_items = idx + 1;
1037	mctl->channel_map[idx] = val;
1038	return 0;
1039}
1040
1041static int parse_map_vindex(struct snd_ctl_map_ctl *mctl, snd_config_t *conf)
1042{
1043	snd_config_iterator_t i, next;
1044	int err;
1045
1046	snd_config_for_each(i, next, conf) {
1047		snd_config_t *n = snd_config_iterator_entry(i);
1048		long idx = -1, chn = -1;
1049		const char *id;
1050		if (snd_config_get_id(n, &id) < 0)
1051			continue;
1052		if (safe_strtol(id, &idx) || snd_config_get_integer(n, &chn)) {
1053			SNDERR("Wrong channel mapping (%ld -> %ld)", idx, chn);
1054			return -EINVAL;
1055		}
1056		err = add_chn_to_map(mctl, idx, chn);
1057		if (err < 0)
1058			return err;
1059	}
1060
1061	return 0;
1062}
1063
1064static int parse_map_config(struct snd_ctl_map_ctl *mctl, snd_config_t *conf)
1065{
1066	snd_config_iterator_t i, next;
1067	int err;
1068
1069	snd_config_for_each(i, next, conf) {
1070		snd_config_t *n = snd_config_iterator_entry(i);
1071		const char *id;
1072		if (snd_config_get_id(n, &id) < 0)
1073			continue;
1074		if (strcmp(id, "vindex") == 0) {
1075			err = parse_map_vindex(mctl, n);
1076			if (err < 0)
1077				return err;
1078		}
1079	}
1080	return 0;
1081}
1082
1083static int parse_map1(snd_ctl_map_t *map, snd_config_t *conf)
1084{
1085	snd_config_iterator_t i, next;
1086	snd_ctl_elem_id_t cid;
1087	struct snd_ctl_map_ctl *mctl;
1088	int err;
1089
1090	snd_config_for_each(i, next, conf) {
1091		snd_config_t *n = snd_config_iterator_entry(i);
1092		const char *id;
1093		if (snd_config_get_id(n, &id) < 0)
1094			continue;
1095		snd_ctl_elem_id_clear(&cid);
1096		err = snd_ctl_ascii_elem_id_parse(&cid, id);
1097		if (err < 0) {
1098			SNDERR("unable to parse control id '%s'!", id);
1099			return -EINVAL;
1100		}
1101		err = add_ctl_to_map(map, &mctl, &cid);
1102		if (err < 0)
1103			return err;
1104		err = parse_map_config(mctl, n);
1105		if (err < 0)
1106			return err;
1107	}
1108
1109	return 0;
1110}
1111
1112static int parse_map(snd_ctl_remap_t *priv, snd_config_t *conf)
1113{
1114	snd_config_iterator_t i, next;
1115	snd_ctl_elem_id_t eid;
1116	snd_ctl_map_t *map;
1117	int err;
1118
1119	if (conf == NULL)
1120		return 0;
1121	snd_config_for_each(i, next, conf) {
1122		snd_config_t *n = snd_config_iterator_entry(i);
1123		const char *id;
1124		if (snd_config_get_id(n, &id) < 0)
1125			continue;
1126		snd_ctl_elem_id_clear(&eid);
1127		err = snd_ctl_ascii_elem_id_parse(&eid, id);
1128		if (err < 0) {
1129			SNDERR("unable to parse id '%s'!", id);
1130			return -EINVAL;
1131		}
1132		err = new_map(priv, &map, &eid);
1133		if (err < 0)
1134			return 0;
1135		err = parse_map1(map, n);
1136		if (err < 0)
1137			return err;
1138	}
1139
1140	return 0;
1141}
1142
1143/**
1144 * \brief Creates a new remap & map control handle
1145 * \param handlep Returns created control handle
1146 * \param name Name of control device
1147 * \param remap Remap configuration
1148 * \param map Map configuration
1149 * \param child child configuration root
1150 * \param mode Control handle mode
1151 * \retval zero on success otherwise a negative error code
1152 * \warning Using of this function might be dangerous in the sense
1153 *          of compatibility reasons. The prototype might be freely
1154 *          changed in future.
1155 */
1156int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *remap,
1157		       snd_config_t *map, snd_ctl_t *child, int mode)
1158{
1159	snd_ctl_remap_t *priv;
1160	snd_ctl_t *ctl;
1161	int result, err;
1162
1163	/* no-op, remove the plugin */
1164	if (!remap && !map)
1165		goto _noop;
1166
1167	priv = calloc(1, sizeof(*priv));
1168	if (priv == NULL)
1169		return -ENOMEM;
1170
1171	err = parse_remap(priv, remap);
1172	if (err < 0) {
1173		result = err;
1174		goto _err;
1175	}
1176
1177	err = parse_map(priv, map);
1178	if (err < 0) {
1179		result = err;
1180		goto _err;
1181	}
1182
1183	/* no-op check, remove the plugin */
1184	if (priv->map_items == 0 && priv->remap_items == 0) {
1185		remap_free(priv);
1186 _noop:
1187		free(child->name);
1188		child->name = name ? strdup(name) : NULL;
1189		if (name && !child->name)
1190			return -ENOMEM;
1191		*handlep = child;
1192		return 0;
1193	}
1194
1195	priv->map_read_queue = calloc(priv->map_items, sizeof(priv->map_read_queue[0]));
1196	if (priv->map_read_queue == NULL) {
1197		result = -ENOMEM;
1198		goto _err;
1199	}
1200
1201	priv->numid_remap_active = priv->map_items > 0;
1202
1203	priv->child = child;
1204	err = snd_ctl_new(&ctl, SND_CTL_TYPE_REMAP, name, mode);
1205	if (err < 0) {
1206		result = err;
1207		goto _err;
1208	}
1209	ctl->ops = &snd_ctl_remap_ops;
1210	ctl->private_data = priv;
1211	ctl->poll_fd = child->poll_fd;
1212
1213	*handlep = ctl;
1214	return 0;
1215
1216 _err:
1217	remap_free(priv);
1218	return result;
1219}
1220
1221/*! \page control_plugins
1222
1223\section control_plugins_remap Plugin: Remap & map
1224
1225This plugin can remap (rename) identifiers (except the numid part) for
1226a child control to another. The plugin can also merge the multiple
1227child controls to one or split one control to more.
1228
1229\code
1230ctl.name {
1231	type remap              # Route & Volume conversion PCM
1232	child STR               # Slave name
1233	# or
1234	child {                 # Slave definition
1235		type STR
1236		...
1237	}
1238	remap {
1239		# the ID strings are parsed in the amixer style like 'name="Headphone Playback Switch",index=2'
1240		SRC_ID1_STR DST_ID1_STR
1241		SRC_ID2_STR DST_ID2_STR
1242		...
1243	}
1244	map {
1245		# join two stereo controls to one
1246		CREATE_ID1_STR {
1247			SRC_ID1_STR {
1248				vindex.0 0	# source channel 0 to merged channel 0
1249				vindex.1 1
1250			}
1251			SRC_ID2_STR {
1252				vindex.2 0
1253				vindex.3 1	# source channel 1 to merged channel 3
1254			}
1255		}
1256		# split stereo to mono
1257		CREATE_ID2_STR {
1258			SRC_ID3_STR {
1259				vindex.0 0	# stereo to mono (first channel)
1260			}
1261		}
1262		CREATE_ID3_STR {
1263			SRC_ID4_STR {
1264				vindex.0 1	# stereo to mono (second channel)
1265			}
1266		}
1267	}
1268}
1269\endcode
1270
1271\subsection control_plugins_route_funcref Function reference
1272
1273<UL>
1274  <LI>snd_ctl_remap_open()
1275  <LI>_snd_ctl_remap_open()
1276</UL>
1277
1278*/
1279
1280/**
1281 * \brief Creates a new remap & map control plugin
1282 * \param handlep Returns created control handle
1283 * \param name Name of control
1284 * \param root Root configuration node
1285 * \param conf Configuration node with Route & Volume PCM description
1286 * \param mode Control handle mode
1287 * \retval zero on success otherwise a negative error code
1288 * \warning Using of this function might be dangerous in the sense
1289 *          of compatibility reasons. The prototype might be freely
1290 *          changed in future.
1291 */
1292int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd_config_t *conf, int mode)
1293{
1294	snd_config_iterator_t i, next;
1295	snd_config_t *child = NULL;
1296	snd_config_t *remap = NULL;
1297	snd_config_t *map = NULL;
1298	snd_ctl_t *cctl;
1299	int err;
1300
1301	snd_config_for_each(i, next, conf) {
1302		snd_config_t *n = snd_config_iterator_entry(i);
1303		const char *id;
1304		if (snd_config_get_id(n, &id) < 0)
1305			continue;
1306		if (_snd_conf_generic_id(id))
1307			continue;
1308		if (strcmp(id, "remap") == 0) {
1309			remap = n;
1310			continue;
1311		}
1312		if (strcmp(id, "map") == 0) {
1313			map = n;
1314			continue;
1315		}
1316		if (strcmp(id, "child") == 0) {
1317			child = n;
1318			continue;
1319		}
1320		SNDERR("Unknown field %s", id);
1321		return -EINVAL;
1322	}
1323	if (!child) {
1324		SNDERR("child is not defined");
1325		return -EINVAL;
1326	}
1327	err = _snd_ctl_open_child(&cctl, root, child, mode, conf);
1328	if (err < 0)
1329		return err;
1330	err = snd_ctl_remap_open(handlep, name, remap, map, cctl, mode);
1331	if (err < 0)
1332		snd_ctl_close(cctl);
1333	return err;
1334}
1335#ifndef DOC_HIDDEN
1336SND_DLSYM_BUILD_VERSION(_snd_ctl_remap_open, SND_CONTROL_DLSYM_VERSION);
1337#endif
1338