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