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