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