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