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