1 /** 2 * \file mixer/simple_none.c 3 * \brief Mixer Simple Element Class Interface 4 * \author Jaroslav Kysela <perex (at) perex.cz> 5 * \author Abramo Bagnara <abramo (at) alsa-project.org> 6 * \date 2001-2004 7 * 8 * Mixer simple element class interface. 9 */ 10 /* 11 * Mixer Interface - simple controls 12 * Copyright (c) 2000,2004 by Jaroslav Kysela <perex (at) perex.cz> 13 * Copyright (c) 2001 by Abramo Bagnara <abramo (at) alsa-project.org> 14 * 15 * 16 * This library is free software; you can redistribute it and/or modify 17 * it under the terms of the GNU Lesser General Public License as 18 * published by the Free Software Foundation; either version 2.1 of 19 * the License, or (at your option) any later version. 20 * 21 * This program is distributed in the hope that it will be useful, 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * GNU Lesser General Public License for more details. 25 * 26 * You should have received a copy of the GNU Lesser General Public 27 * License along with this library; if not, write to the Free Software 28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 29 * 30 */ 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <string.h> 36 #include <fcntl.h> 37 #include <sys/ioctl.h> 38 #include <assert.h> 39 #include <math.h> 40 #include <limits.h> 41 #include <alsa/asoundlib.h> 42 #include "mixer_simple.h" 43 44 #ifndef DOC_HIDDEN 45 46 #define MIXER_COMPARE_WEIGHT_SIMPLE_BASE 0 47 #define MIXER_COMPARE_WEIGHT_NEXT_BASE 10000000 48 #define MIXER_COMPARE_WEIGHT_NOT_FOUND 1000000000 49 50 typedef enum _selem_ctl_type { 51 CTL_SINGLE, 52 CTL_GLOBAL_ENUM, 53 CTL_GLOBAL_SWITCH, 54 CTL_GLOBAL_VOLUME, 55 CTL_GLOBAL_ROUTE, 56 CTL_PLAYBACK_ENUM, 57 CTL_PLAYBACK_SWITCH, 58 CTL_PLAYBACK_VOLUME, 59 CTL_PLAYBACK_ROUTE, 60 CTL_CAPTURE_ENUM, 61 CTL_CAPTURE_SWITCH, 62 CTL_CAPTURE_VOLUME, 63 CTL_CAPTURE_ROUTE, 64 CTL_CAPTURE_SOURCE, 65 CTL_LAST = CTL_CAPTURE_SOURCE, 66 } selem_ctl_type_t; 67 68 typedef struct _selem_ctl { 69 snd_hctl_elem_t *elem; 70 snd_ctl_elem_type_t type; 71 unsigned int inactive: 1; 72 unsigned int values; 73 long min, max; 74 } selem_ctl_t; 75 76 typedef struct _selem_none { 77 sm_selem_t selem; 78 selem_ctl_t ctls[CTL_LAST + 1]; 79 unsigned int capture_item; 80 struct selem_str { 81 unsigned int range: 1; /* Forced range */ 82 unsigned int db_initialized: 1; 83 unsigned int db_init_error: 1; 84 long min, max; 85 unsigned int channels; 86 long vol[32]; 87 unsigned int sw; 88 unsigned int *db_info; 89 } str[2]; 90 } selem_none_t; 91 92 static const struct mixer_name_table { 93 const char *longname; 94 const char *shortname; 95 } name_table[] = { 96 {"Tone Control - Switch", "Tone"}, 97 {"Tone Control - Bass", "Bass"}, 98 {"Tone Control - Treble", "Treble"}, 99 {"Synth Tone Control - Switch", "Synth Tone"}, 100 {"Synth Tone Control - Bass", "Synth Bass"}, 101 {"Synth Tone Control - Treble", "Synth Treble"}, 102 {0, 0}, 103 }; 104 105 #endif /* !DOC_HIDDEN */ 106 107 static const char *get_short_name(const char *lname) 108 { 109 const struct mixer_name_table *p; 110 for (p = name_table; p->longname; p++) { 111 if (!strcmp(lname, p->longname)) 112 return p->shortname; 113 } 114 return lname; 115 } 116 117 static int compare_mixer_priority_lookup(const char **name, const char * const *names, int coef) 118 { 119 int res; 120 121 for (res = 0; *names; names++, res += coef) { 122 if (!strncmp(*name, *names, strlen(*names))) { 123 *name += strlen(*names); 124 if (**name == ' ') 125 (*name)++; 126 return res+1; 127 } 128 } 129 return MIXER_COMPARE_WEIGHT_NOT_FOUND; 130 } 131 132 static int get_compare_weight(const char *name, unsigned int idx) 133 { 134 static const char *const names[] = { 135 "Master", 136 "Headphone", 137 "Tone", 138 "Bass", 139 "Treble", 140 "3D Control", 141 "PCM", 142 "Front", 143 "Surround", 144 "Center", 145 "LFE", 146 "Side", 147 "Synth", 148 "FM", 149 "Wave", 150 "Music", 151 "DSP", 152 "Line", 153 "CD", 154 "Mic", 155 "Video", 156 "Zoom Video", 157 "Phone", 158 "I2S", 159 "IEC958", 160 "PC Speaker", 161 "Aux", 162 "Mono", 163 "Playback", 164 "Capture", 165 "Mix", 166 NULL 167 }; 168 static const char *const names1[] = { 169 "-", 170 NULL, 171 }; 172 static const char *const names2[] = { 173 "Mono", 174 "Digital", 175 "Switch", 176 "Depth", 177 "Wide", 178 "Space", 179 "Level", 180 "Center", 181 "Output", 182 "Boost", 183 "Tone", 184 "Bass", 185 "Treble", 186 NULL, 187 }; 188 const char *name1; 189 int res, res1; 190 191 if ((res = compare_mixer_priority_lookup((const char **)&name, names, 1000)) == MIXER_COMPARE_WEIGHT_NOT_FOUND) 192 return MIXER_COMPARE_WEIGHT_NOT_FOUND; 193 if (*name == '\0') 194 goto __res; 195 for (name1 = name; *name1 != '\0'; name1++); 196 for (name1--; name1 != name && *name1 != ' '; name1--); 197 while (name1 != name && *name1 == ' ') 198 name1--; 199 if (name1 != name) { 200 for (; name1 != name && *name1 != ' '; name1--); 201 name = name1; 202 if ((res1 = compare_mixer_priority_lookup((const char **)&name, names1, 200)) == MIXER_COMPARE_WEIGHT_NOT_FOUND) 203 return res; 204 res += res1; 205 } else { 206 name = name1; 207 } 208 if ((res1 = compare_mixer_priority_lookup((const char **)&name, names2, 20)) == MIXER_COMPARE_WEIGHT_NOT_FOUND) 209 return res; 210 __res: 211 return MIXER_COMPARE_WEIGHT_SIMPLE_BASE + res + idx; 212 } 213 214 static long to_user(selem_none_t *s, int dir, selem_ctl_t *c, long value) 215 { 216 int64_t n; 217 if (c->max == c->min) 218 return s->str[dir].min; 219 n = (int64_t) (value - c->min) * (s->str[dir].max - s->str[dir].min); 220 return s->str[dir].min + (n + (c->max - c->min) / 2) / (c->max - c->min); 221 } 222 223 static long from_user(selem_none_t *s, int dir, selem_ctl_t *c, long value) 224 { 225 int64_t n; 226 if (s->str[dir].max == s->str[dir].min) 227 return c->min; 228 n = (int64_t) (value - s->str[dir].min) * (c->max - c->min); 229 return c->min + (n + (s->str[dir].max - s->str[dir].min) / 2) / (s->str[dir].max - s->str[dir].min); 230 } 231 232 static int elem_read_volume(selem_none_t *s, int dir, selem_ctl_type_t type) 233 { 234 snd_ctl_elem_value_t *ctl; 235 unsigned int idx; 236 int err; 237 selem_ctl_t *c = &s->ctls[type]; 238 snd_ctl_elem_value_alloca(&ctl); 239 if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) 240 return err; 241 for (idx = 0; idx < s->str[dir].channels; idx++) { 242 unsigned int idx1 = idx; 243 if (idx >= c->values) 244 idx1 = 0; 245 s->str[dir].vol[idx] = to_user(s, dir, c, snd_ctl_elem_value_get_integer(ctl, idx1)); 246 } 247 return 0; 248 } 249 250 static int elem_read_switch(selem_none_t *s, int dir, selem_ctl_type_t type) 251 { 252 snd_ctl_elem_value_t *ctl; 253 unsigned int idx; 254 int err; 255 selem_ctl_t *c = &s->ctls[type]; 256 snd_ctl_elem_value_alloca(&ctl); 257 if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) 258 return err; 259 for (idx = 0; idx < s->str[dir].channels; idx++) { 260 unsigned int idx1 = idx; 261 if (idx >= c->values) 262 idx1 = 0; 263 if (!snd_ctl_elem_value_get_integer(ctl, idx1)) 264 s->str[dir].sw &= ~(1 << idx); 265 } 266 return 0; 267 } 268 269 static int elem_read_route(selem_none_t *s, int dir, selem_ctl_type_t type) 270 { 271 snd_ctl_elem_value_t *ctl; 272 unsigned int idx; 273 int err; 274 selem_ctl_t *c = &s->ctls[type]; 275 snd_ctl_elem_value_alloca(&ctl); 276 if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) 277 return err; 278 for (idx = 0; idx < s->str[dir].channels; idx++) { 279 unsigned int idx1 = idx; 280 if (idx >= c->values) 281 idx1 = 0; 282 if (!snd_ctl_elem_value_get_integer(ctl, idx1 * c->values + idx1)) 283 s->str[dir].sw &= ~(1 << idx); 284 } 285 return 0; 286 } 287 288 static int elem_read_enum(selem_none_t *s) 289 { 290 snd_ctl_elem_value_t *ctl; 291 unsigned int idx; 292 int err; 293 int type; 294 selem_ctl_t *c; 295 type = CTL_GLOBAL_ENUM; 296 if ( (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) == (SM_CAP_CENUM | SM_CAP_PENUM) ) 297 type = CTL_GLOBAL_ENUM; 298 else if (s->selem.caps & SM_CAP_PENUM) 299 type = CTL_PLAYBACK_ENUM; 300 else if (s->selem.caps & SM_CAP_CENUM) 301 type = CTL_CAPTURE_ENUM; 302 c = &s->ctls[type]; 303 snd_ctl_elem_value_alloca(&ctl); 304 if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) 305 return err; 306 for (idx = 0; idx < s->str[0].channels; idx++) { 307 unsigned int idx1 = idx; 308 if (idx >= c->values) 309 idx1 = 0; 310 s->str[0].vol[idx] = snd_ctl_elem_value_get_enumerated(ctl, idx1); 311 } 312 return 0; 313 } 314 315 static int selem_read(snd_mixer_elem_t *elem) 316 { 317 selem_none_t *s; 318 unsigned int idx; 319 int err = 0; 320 long pvol[32], cvol[32]; 321 unsigned int psw, csw; 322 323 assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE); 324 s = snd_mixer_elem_get_private(elem); 325 326 memcpy(pvol, s->str[SM_PLAY].vol, sizeof(pvol)); 327 memset(&s->str[SM_PLAY].vol, 0, sizeof(s->str[SM_PLAY].vol)); 328 psw = s->str[SM_PLAY].sw; 329 s->str[SM_PLAY].sw = ~0U; 330 memcpy(cvol, s->str[SM_CAPT].vol, sizeof(cvol)); 331 memset(&s->str[SM_CAPT].vol, 0, sizeof(s->str[SM_CAPT].vol)); 332 csw = s->str[SM_CAPT].sw; 333 s->str[SM_CAPT].sw = ~0U; 334 335 if (s->ctls[CTL_GLOBAL_ENUM].elem) { 336 err = elem_read_enum(s); 337 if (err < 0) 338 return err; 339 goto __skip_cswitch; 340 } 341 342 if (s->ctls[CTL_CAPTURE_ENUM].elem) { 343 err = elem_read_enum(s); 344 if (err < 0) 345 return err; 346 goto __skip_cswitch; 347 } 348 349 if (s->ctls[CTL_PLAYBACK_ENUM].elem) { 350 err = elem_read_enum(s); 351 if (err < 0) 352 return err; 353 goto __skip_cswitch; 354 } 355 356 357 if (s->ctls[CTL_PLAYBACK_VOLUME].elem) 358 err = elem_read_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME); 359 else if (s->ctls[CTL_GLOBAL_VOLUME].elem) 360 err = elem_read_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME); 361 else if (s->ctls[CTL_SINGLE].elem && 362 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER) 363 err = elem_read_volume(s, SM_PLAY, CTL_SINGLE); 364 if (err < 0) 365 return err; 366 367 if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)) == 0) { 368 s->str[SM_PLAY].sw = 0; 369 goto __skip_pswitch; 370 } 371 if (s->ctls[CTL_PLAYBACK_SWITCH].elem) { 372 err = elem_read_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH); 373 if (err < 0) 374 return err; 375 } 376 if (s->ctls[CTL_GLOBAL_SWITCH].elem) { 377 err = elem_read_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH); 378 if (err < 0) 379 return err; 380 } 381 if (s->ctls[CTL_SINGLE].elem && 382 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) { 383 err = elem_read_switch(s, SM_PLAY, CTL_SINGLE); 384 if (err < 0) 385 return err; 386 } 387 if (s->ctls[CTL_PLAYBACK_ROUTE].elem) { 388 err = elem_read_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE); 389 if (err < 0) 390 return err; 391 } 392 if (s->ctls[CTL_GLOBAL_ROUTE].elem) { 393 err = elem_read_route(s, SM_PLAY, CTL_GLOBAL_ROUTE); 394 if (err < 0) 395 return err; 396 } 397 __skip_pswitch: 398 399 if (s->ctls[CTL_CAPTURE_VOLUME].elem) 400 err = elem_read_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME); 401 else if (s->ctls[CTL_GLOBAL_VOLUME].elem) 402 err = elem_read_volume(s, SM_CAPT, CTL_GLOBAL_VOLUME); 403 else if (s->ctls[CTL_SINGLE].elem && 404 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER) 405 err = elem_read_volume(s, SM_CAPT, CTL_SINGLE); 406 if (err < 0) 407 return err; 408 409 if ((s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)) == 0) { 410 s->str[SM_CAPT].sw = 0; 411 goto __skip_cswitch; 412 } 413 if (s->ctls[CTL_CAPTURE_SWITCH].elem) { 414 err = elem_read_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH); 415 if (err < 0) 416 return err; 417 } 418 if (s->ctls[CTL_GLOBAL_SWITCH].elem) { 419 err = elem_read_switch(s, SM_CAPT, CTL_GLOBAL_SWITCH); 420 if (err < 0) 421 return err; 422 } 423 if (s->ctls[CTL_SINGLE].elem && 424 s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_BOOLEAN) { 425 err = elem_read_switch(s, SM_CAPT, CTL_SINGLE); 426 if (err < 0) 427 return err; 428 } 429 if (s->ctls[CTL_CAPTURE_ROUTE].elem) { 430 err = elem_read_route(s, SM_CAPT, CTL_CAPTURE_ROUTE); 431 if (err < 0) 432 return err; 433 } 434 if (s->ctls[CTL_GLOBAL_ROUTE].elem) { 435 err = elem_read_route(s, SM_CAPT, CTL_GLOBAL_ROUTE); 436 if (err < 0) 437 return err; 438 } 439 if (s->ctls[CTL_CAPTURE_SOURCE].elem) { 440 snd_ctl_elem_value_t *ctl; 441 selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE]; 442 snd_ctl_elem_value_alloca(&ctl); 443 err = snd_hctl_elem_read(c->elem, ctl); 444 if (err < 0) 445 return err; 446 for (idx = 0; idx < s->str[SM_CAPT].channels; idx++) { 447 unsigned int idx1 = idx; 448 if (idx >= c->values) 449 idx1 = 0; 450 if (snd_ctl_elem_value_get_enumerated(ctl, idx1) != s->capture_item) 451 s->str[SM_CAPT].sw &= ~(1 << idx); 452 } 453 } 454 __skip_cswitch: 455 456 if (memcmp(pvol, s->str[SM_PLAY].vol, sizeof(pvol)) || 457 psw != s->str[SM_PLAY].sw || 458 memcmp(cvol, s->str[SM_CAPT].vol, sizeof(cvol)) || 459 csw != s->str[SM_CAPT].sw) 460 return 1; 461 return 0; 462 } 463 464 static int elem_write_volume(selem_none_t *s, int dir, selem_ctl_type_t type) 465 { 466 snd_ctl_elem_value_t *ctl; 467 unsigned int idx; 468 int err; 469 selem_ctl_t *c = &s->ctls[type]; 470 snd_ctl_elem_value_alloca(&ctl); 471 if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) 472 return err; 473 for (idx = 0; idx < c->values; idx++) 474 snd_ctl_elem_value_set_integer(ctl, idx, from_user(s, dir, c, s->str[dir].vol[idx])); 475 if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) 476 return err; 477 return 0; 478 } 479 480 static int elem_write_switch(selem_none_t *s, int dir, selem_ctl_type_t type) 481 { 482 snd_ctl_elem_value_t *ctl; 483 unsigned int idx; 484 int err; 485 selem_ctl_t *c = &s->ctls[type]; 486 snd_ctl_elem_value_alloca(&ctl); 487 if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) 488 return err; 489 for (idx = 0; idx < c->values; idx++) 490 snd_ctl_elem_value_set_integer(ctl, idx, !!(s->str[dir].sw & (1 << idx))); 491 if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) 492 return err; 493 return 0; 494 } 495 496 static int elem_write_switch_constant(selem_none_t *s, selem_ctl_type_t type, int val) 497 { 498 snd_ctl_elem_value_t *ctl; 499 unsigned int idx; 500 int err; 501 selem_ctl_t *c = &s->ctls[type]; 502 snd_ctl_elem_value_alloca(&ctl); 503 if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) 504 return err; 505 for (idx = 0; idx < c->values; idx++) 506 snd_ctl_elem_value_set_integer(ctl, idx, !!val); 507 if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) 508 return err; 509 return 0; 510 } 511 512 static int elem_write_route(selem_none_t *s, int dir, selem_ctl_type_t type) 513 { 514 snd_ctl_elem_value_t *ctl; 515 unsigned int idx; 516 int err; 517 selem_ctl_t *c = &s->ctls[type]; 518 snd_ctl_elem_value_alloca(&ctl); 519 if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) 520 return err; 521 for (idx = 0; idx < c->values * c->values; idx++) 522 snd_ctl_elem_value_set_integer(ctl, idx, 0); 523 for (idx = 0; idx < c->values; idx++) 524 snd_ctl_elem_value_set_integer(ctl, idx * c->values + idx, !!(s->str[dir].sw & (1 << idx))); 525 if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) 526 return err; 527 return 0; 528 } 529 530 static int elem_write_enum(selem_none_t *s) 531 { 532 snd_ctl_elem_value_t *ctl; 533 unsigned int idx; 534 int err; 535 int type; 536 selem_ctl_t *c; 537 type = CTL_GLOBAL_ENUM; 538 if ( (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM) ) == (SM_CAP_CENUM | SM_CAP_PENUM) ) 539 type = CTL_GLOBAL_ENUM; 540 else if (s->selem.caps & SM_CAP_PENUM) 541 type = CTL_PLAYBACK_ENUM; 542 else if (s->selem.caps & SM_CAP_CENUM) 543 type = CTL_CAPTURE_ENUM; 544 c = &s->ctls[type]; 545 snd_ctl_elem_value_alloca(&ctl); 546 if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) 547 return err; 548 for (idx = 0; idx < c->values; idx++) 549 snd_ctl_elem_value_set_enumerated(ctl, idx, (unsigned int)s->str[0].vol[idx]); 550 if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) 551 return err; 552 return 0; 553 } 554 555 static int selem_write_main(snd_mixer_elem_t *elem) 556 { 557 selem_none_t *s; 558 unsigned int idx; 559 int err; 560 561 assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE); 562 s = snd_mixer_elem_get_private(elem); 563 564 if (s->ctls[CTL_GLOBAL_ENUM].elem) 565 return elem_write_enum(s); 566 567 if (s->ctls[CTL_PLAYBACK_ENUM].elem) 568 return elem_write_enum(s); 569 570 if (s->ctls[CTL_CAPTURE_ENUM].elem) 571 return elem_write_enum(s); 572 573 if (s->ctls[CTL_SINGLE].elem) { 574 if (s->ctls[CTL_SINGLE].type == SND_CTL_ELEM_TYPE_INTEGER) 575 err = elem_write_volume(s, SM_PLAY, CTL_SINGLE); 576 else 577 err = elem_write_switch(s, SM_PLAY, CTL_SINGLE); 578 if (err < 0) 579 return err; 580 } 581 if (s->ctls[CTL_GLOBAL_VOLUME].elem) { 582 err = elem_write_volume(s, SM_PLAY, CTL_GLOBAL_VOLUME); 583 if (err < 0) 584 return err; 585 } 586 if (s->ctls[CTL_GLOBAL_SWITCH].elem) { 587 if (s->ctls[CTL_PLAYBACK_SWITCH].elem && s->ctls[CTL_CAPTURE_SWITCH].elem) 588 err = elem_write_switch_constant(s, CTL_GLOBAL_SWITCH, 1); 589 else 590 err = elem_write_switch(s, SM_PLAY, CTL_GLOBAL_SWITCH); 591 if (err < 0) 592 return err; 593 } 594 if (s->ctls[CTL_PLAYBACK_VOLUME].elem) { 595 err = elem_write_volume(s, SM_PLAY, CTL_PLAYBACK_VOLUME); 596 if (err < 0) 597 return err; 598 } 599 if (s->ctls[CTL_PLAYBACK_SWITCH].elem) { 600 err = elem_write_switch(s, SM_PLAY, CTL_PLAYBACK_SWITCH); 601 if (err < 0) 602 return err; 603 } 604 if (s->ctls[CTL_PLAYBACK_ROUTE].elem) { 605 err = elem_write_route(s, SM_PLAY, CTL_PLAYBACK_ROUTE); 606 if (err < 0) 607 return err; 608 } 609 if (s->ctls[CTL_CAPTURE_VOLUME].elem) { 610 err = elem_write_volume(s, SM_CAPT, CTL_CAPTURE_VOLUME); 611 if (err < 0) 612 return err; 613 } 614 if (s->ctls[CTL_CAPTURE_SWITCH].elem) { 615 err = elem_write_switch(s, SM_CAPT, CTL_CAPTURE_SWITCH); 616 if (err < 0) 617 return err; 618 } 619 if (s->ctls[CTL_CAPTURE_ROUTE].elem) { 620 err = elem_write_route(s, SM_CAPT, CTL_CAPTURE_ROUTE); 621 if (err < 0) 622 return err; 623 } 624 if (s->ctls[CTL_CAPTURE_SOURCE].elem) { 625 snd_ctl_elem_value_t *ctl; 626 selem_ctl_t *c = &s->ctls[CTL_CAPTURE_SOURCE]; 627 snd_ctl_elem_value_alloca(&ctl); 628 if ((err = snd_hctl_elem_read(c->elem, ctl)) < 0) 629 return err; 630 for (idx = 0; idx < c->values; idx++) { 631 if (s->str[SM_CAPT].sw & (1 << idx)) 632 snd_ctl_elem_value_set_enumerated(ctl, idx, s->capture_item); 633 } 634 if ((err = snd_hctl_elem_write(c->elem, ctl)) < 0) 635 return err; 636 /* update the element, don't remove */ 637 err = selem_read(elem); 638 if (err < 0) 639 return err; 640 } 641 return 0; 642 } 643 644 static int selem_write(snd_mixer_elem_t *elem) 645 { 646 int err; 647 648 err = selem_write_main(elem); 649 if (err < 0) 650 selem_read(elem); 651 return err; 652 } 653 654 static void selem_free(snd_mixer_elem_t *elem) 655 { 656 selem_none_t *simple = snd_mixer_elem_get_private(elem); 657 assert(snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE); 658 if (simple->selem.id) 659 snd_mixer_selem_id_free(simple->selem.id); 660 /* free db range information */ 661 free(simple->str[0].db_info); 662 free(simple->str[1].db_info); 663 free(simple); 664 } 665 666 static int simple_update(snd_mixer_elem_t *melem) 667 { 668 selem_none_t *simple; 669 unsigned int caps, pchannels, cchannels; 670 long pmin, pmax, cmin, cmax; 671 selem_ctl_t *ctl; 672 const char *name; 673 674 caps = 0; 675 pchannels = 0; 676 pmin = LONG_MAX; 677 pmax = LONG_MIN; 678 cchannels = 0; 679 cmin = LONG_MAX; 680 cmax = LONG_MIN; 681 assert(snd_mixer_elem_get_type(melem) == SND_MIXER_ELEM_SIMPLE); 682 simple = snd_mixer_elem_get_private(melem); 683 name = snd_mixer_selem_get_name(melem); 684 ctl = &simple->ctls[CTL_SINGLE]; 685 if (ctl->elem) { 686 pchannels = cchannels = ctl->values; 687 if (ctl->type == SND_CTL_ELEM_TYPE_INTEGER) { 688 caps |= SM_CAP_GVOLUME; 689 pmin = cmin = ctl->min; 690 pmax = cmax = ctl->max; 691 } else 692 caps |= SM_CAP_GSWITCH; 693 } 694 ctl = &simple->ctls[CTL_GLOBAL_SWITCH]; 695 if (ctl->elem) { 696 if (pchannels < ctl->values) 697 pchannels = ctl->values; 698 if (cchannels < ctl->values) 699 cchannels = ctl->values; 700 caps |= SM_CAP_GSWITCH; 701 } 702 ctl = &simple->ctls[CTL_GLOBAL_ROUTE]; 703 if (ctl->elem) { 704 if (pchannels < ctl->values) 705 pchannels = ctl->values; 706 if (cchannels < ctl->values) 707 cchannels = ctl->values; 708 caps |= SM_CAP_GSWITCH; 709 } 710 ctl = &simple->ctls[CTL_GLOBAL_VOLUME]; 711 if (ctl->elem) { 712 if (pchannels < ctl->values) 713 pchannels = ctl->values; 714 if (pmin > ctl->min) 715 pmin = ctl->min; 716 if (pmax < ctl->max) 717 pmax = ctl->max; 718 if (cchannels < ctl->values) 719 cchannels = ctl->values; 720 if (cmin > ctl->min) 721 cmin = ctl->min; 722 if (cmax < ctl->max) 723 cmax = ctl->max; 724 caps |= SM_CAP_GVOLUME; 725 } 726 ctl = &simple->ctls[CTL_PLAYBACK_SWITCH]; 727 if (ctl->elem) { 728 if (pchannels < ctl->values) 729 pchannels = ctl->values; 730 caps |= SM_CAP_PSWITCH; 731 caps &= ~SM_CAP_GSWITCH; 732 } 733 ctl = &simple->ctls[CTL_PLAYBACK_ROUTE]; 734 if (ctl->elem) { 735 if (pchannels < ctl->values) 736 pchannels = ctl->values; 737 caps |= SM_CAP_PSWITCH; 738 caps &= ~SM_CAP_GSWITCH; 739 } 740 ctl = &simple->ctls[CTL_CAPTURE_SWITCH]; 741 if (ctl->elem) { 742 if (cchannels < ctl->values) 743 cchannels = ctl->values; 744 caps |= SM_CAP_CSWITCH; 745 caps &= ~SM_CAP_GSWITCH; 746 } 747 ctl = &simple->ctls[CTL_CAPTURE_ROUTE]; 748 if (ctl->elem) { 749 if (cchannels < ctl->values) 750 cchannels = ctl->values; 751 caps |= SM_CAP_CSWITCH; 752 caps &= ~SM_CAP_GSWITCH; 753 } 754 ctl = &simple->ctls[CTL_PLAYBACK_VOLUME]; 755 if (ctl->elem) { 756 if (pchannels < ctl->values) 757 pchannels = ctl->values; 758 if (pmin > ctl->min) 759 pmin = ctl->min; 760 if (pmax < ctl->max) 761 pmax = ctl->max; 762 caps |= SM_CAP_PVOLUME; 763 caps &= ~SM_CAP_GVOLUME; 764 } 765 ctl = &simple->ctls[CTL_CAPTURE_VOLUME]; 766 if (ctl->elem) { 767 if (cchannels < ctl->values) 768 cchannels = ctl->values; 769 if (cmin > ctl->min) 770 cmin = ctl->min; 771 if (cmax < ctl->max) 772 cmax = ctl->max; 773 caps |= SM_CAP_CVOLUME; 774 caps &= ~SM_CAP_GVOLUME; 775 } 776 ctl = &simple->ctls[CTL_CAPTURE_SOURCE]; 777 if (ctl->elem) { 778 if (cchannels < ctl->values) 779 cchannels = ctl->values; 780 caps |= SM_CAP_CSWITCH | SM_CAP_CSWITCH_EXCL; 781 caps &= ~SM_CAP_GSWITCH; 782 } 783 ctl = &simple->ctls[CTL_GLOBAL_ENUM]; 784 if (ctl->elem) { 785 if (pchannels < ctl->values) 786 pchannels = ctl->values; 787 caps |= SM_CAP_PENUM | SM_CAP_CENUM; 788 } 789 ctl = &simple->ctls[CTL_PLAYBACK_ENUM]; 790 if (ctl->elem) { 791 if (pchannels < ctl->values) 792 pchannels = ctl->values; 793 caps |= SM_CAP_PENUM; 794 } 795 ctl = &simple->ctls[CTL_CAPTURE_ENUM]; 796 if (ctl->elem) { 797 if (pchannels < ctl->values) 798 pchannels = ctl->values; 799 caps |= SM_CAP_CENUM; 800 } 801 if (pchannels > 32) 802 pchannels = 32; 803 if (cchannels > 32) 804 cchannels = 32; 805 if (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH)) 806 caps |= SM_CAP_PSWITCH_JOIN; 807 if (caps & (SM_CAP_GVOLUME|SM_CAP_PVOLUME)) 808 caps |= SM_CAP_PVOLUME_JOIN; 809 if (caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH)) 810 caps |= SM_CAP_CSWITCH_JOIN; 811 if (caps & (SM_CAP_GVOLUME|SM_CAP_CVOLUME)) 812 caps |= SM_CAP_CVOLUME_JOIN; 813 if (pchannels > 1 || cchannels > 1) { 814 if (simple->ctls[CTL_SINGLE].elem && 815 simple->ctls[CTL_SINGLE].values > 1) { 816 if (caps & SM_CAP_GSWITCH) 817 caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN); 818 else 819 caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN); 820 } 821 if (simple->ctls[CTL_GLOBAL_ROUTE].elem || 822 (simple->ctls[CTL_GLOBAL_SWITCH].elem && 823 simple->ctls[CTL_GLOBAL_SWITCH].values > 1)) { 824 caps &= ~(SM_CAP_PSWITCH_JOIN|SM_CAP_CSWITCH_JOIN); 825 } 826 if (simple->ctls[CTL_GLOBAL_VOLUME].elem && 827 simple->ctls[CTL_GLOBAL_VOLUME].values > 1) { 828 caps &= ~(SM_CAP_PVOLUME_JOIN|SM_CAP_CVOLUME_JOIN); 829 } 830 } 831 if (pchannels > 1) { 832 if (simple->ctls[CTL_PLAYBACK_ROUTE].elem || 833 (simple->ctls[CTL_PLAYBACK_SWITCH].elem && 834 simple->ctls[CTL_PLAYBACK_SWITCH].values > 1)) { 835 caps &= ~SM_CAP_PSWITCH_JOIN; 836 } 837 if (simple->ctls[CTL_PLAYBACK_VOLUME].elem && 838 simple->ctls[CTL_PLAYBACK_VOLUME].values > 1) { 839 caps &= ~SM_CAP_PVOLUME_JOIN; 840 } 841 } 842 if (cchannels > 1) { 843 if (simple->ctls[CTL_CAPTURE_ROUTE].elem || 844 (simple->ctls[CTL_CAPTURE_SWITCH].elem && 845 simple->ctls[CTL_CAPTURE_SWITCH].values > 1) || 846 (simple->ctls[CTL_CAPTURE_SOURCE].elem && 847 simple->ctls[CTL_CAPTURE_SOURCE].values > 1)) { 848 caps &= ~SM_CAP_CSWITCH_JOIN; 849 } 850 if (simple->ctls[CTL_CAPTURE_VOLUME].elem && 851 simple->ctls[CTL_CAPTURE_VOLUME].values > 1) { 852 caps &= ~SM_CAP_CVOLUME_JOIN; 853 } 854 } 855 856 /* exceptions */ 857 if ((caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) && 858 (caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == (caps & SM_CAP_GSWITCH)) { 859 caps &= ~(SM_CAP_GSWITCH|SM_CAP_CSWITCH_JOIN|SM_CAP_CSWITCH_EXCL); 860 caps |= SM_CAP_PSWITCH; 861 } 862 863 if ((caps & SM_CAP_GSWITCH) && 864 (caps & (SM_CAP_PSWITCH|SM_CAP_CSWITCH)) == 0) 865 caps |= SM_CAP_PSWITCH|SM_CAP_CSWITCH; 866 867 if ((caps & SM_CAP_GVOLUME) && 868 (caps & (SM_CAP_PVOLUME|SM_CAP_CVOLUME)) == 0) 869 caps |= SM_CAP_PVOLUME|SM_CAP_CVOLUME; 870 871 simple->selem.caps = caps; 872 simple->str[SM_PLAY].channels = pchannels; 873 if (!simple->str[SM_PLAY].range) { 874 simple->str[SM_PLAY].min = pmin != LONG_MAX ? pmin : 0; 875 simple->str[SM_PLAY].max = pmax != LONG_MIN ? pmax : 0; 876 } 877 simple->str[SM_CAPT].channels = cchannels; 878 if (!simple->str[SM_CAPT].range) { 879 simple->str[SM_CAPT].min = cmin != LONG_MAX ? cmin : 0; 880 simple->str[SM_CAPT].max = cmax != LONG_MIN ? cmax : 0; 881 } 882 return 0; 883 } 884 885 #ifndef DOC_HIDDEN 886 static const struct suf { 887 const char *suffix; 888 selem_ctl_type_t type; 889 } suffixes[] = { 890 {" Playback Enum", CTL_PLAYBACK_ENUM}, 891 {" Playback Switch", CTL_PLAYBACK_SWITCH}, 892 {" Playback Route", CTL_PLAYBACK_ROUTE}, 893 {" Playback Volume", CTL_PLAYBACK_VOLUME}, 894 {" Capture Enum", CTL_CAPTURE_ENUM}, 895 {" Capture Switch", CTL_CAPTURE_SWITCH}, 896 {" Capture Route", CTL_CAPTURE_ROUTE}, 897 {" Capture Volume", CTL_CAPTURE_VOLUME}, 898 {" Enum", CTL_GLOBAL_ENUM}, 899 {" Switch", CTL_GLOBAL_SWITCH}, 900 {" Route", CTL_GLOBAL_ROUTE}, 901 {" Volume", CTL_GLOBAL_VOLUME}, 902 {NULL, 0} 903 }; 904 #endif 905 906 /* Return base length or 0 on failure */ 907 static int base_len(const char *name, selem_ctl_type_t *type) 908 { 909 const struct suf *p; 910 size_t nlen = strlen(name); 911 p = suffixes; 912 while (p->suffix) { 913 size_t slen = strlen(p->suffix); 914 size_t l; 915 if (nlen > slen) { 916 l = nlen - slen; 917 if (strncmp(name + l, p->suffix, slen) == 0 && 918 (l < 1 || name[l-1] != '-')) { /* 3D Control - Switch */ 919 *type = p->type; 920 return l; 921 } 922 } 923 p++; 924 } 925 926 /* Special case - handle "Input Source" as a capture route. 927 * Note that it's *NO* capture source. A capture source is split over 928 * sub-elements, and multiple capture-sources will result in an error. 929 * That's why some drivers use "Input Source" as a workaround. 930 * Hence, this is a workaround for a workaround to get the things 931 * straight back again. Sigh. 932 */ 933 if (!strcmp(name, "Input Source")) { 934 *type = CTL_CAPTURE_ROUTE; 935 return strlen(name); 936 } 937 938 return 0; 939 } 940 941 942 /* 943 * Simple Mixer Operations 944 */ 945 946 static int _snd_mixer_selem_set_volume(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, long value) 947 { 948 selem_none_t *s = snd_mixer_elem_get_private(elem); 949 if (s->selem.caps & SM_CAP_GVOLUME) 950 dir = SM_PLAY; 951 if ((unsigned int) channel >= s->str[dir].channels) 952 return 0; 953 if (value < s->str[dir].min || value > s->str[dir].max) 954 return 0; 955 if (s->selem.caps & 956 (dir == SM_PLAY ? SM_CAP_PVOLUME_JOIN : SM_CAP_CVOLUME_JOIN)) 957 channel = 0; 958 if (value != s->str[dir].vol[channel]) { 959 s->str[dir].vol[channel] = value; 960 return 1; 961 } 962 return 0; 963 } 964 965 static int _snd_mixer_selem_set_switch(snd_mixer_elem_t *elem, int dir, snd_mixer_selem_channel_id_t channel, int value) 966 { 967 selem_none_t *s = snd_mixer_elem_get_private(elem); 968 if ((unsigned int) channel >= s->str[dir].channels) 969 return 0; 970 if (s->selem.caps & 971 (dir == SM_PLAY ? SM_CAP_PSWITCH_JOIN : SM_CAP_CSWITCH_JOIN)) 972 channel = 0; 973 if (value) { 974 if (!(s->str[dir].sw & (1 << channel))) { 975 s->str[dir].sw |= 1 << channel; 976 return 1; 977 } 978 } else { 979 if (s->str[dir].sw & (1 << channel)) { 980 s->str[dir].sw &= ~(1 << channel); 981 return 1; 982 } 983 } 984 return 0; 985 } 986 987 static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val) 988 { 989 selem_none_t *s = snd_mixer_elem_get_private(elem); 990 991 switch (cmd) { 992 993 case SM_OPS_IS_ACTIVE: { 994 selem_ctl_type_t ctl; 995 for (ctl = CTL_SINGLE; ctl <= CTL_LAST; ctl++) 996 if (s->ctls[ctl].elem != NULL && s->ctls[ctl].inactive) 997 return 0; 998 return 1; 999 } 1000 1001 case SM_OPS_IS_MONO: 1002 return s->str[dir].channels == 1; 1003 1004 case SM_OPS_IS_CHANNEL: 1005 return (unsigned int) val < s->str[dir].channels; 1006 1007 case SM_OPS_IS_ENUMERATED: 1008 if (val == 1) { 1009 if (dir == SM_PLAY && (s->selem.caps & SM_CAP_PENUM) && !(s->selem.caps & SM_CAP_CENUM) ) 1010 return 1; 1011 if (dir == SM_CAPT && (s->selem.caps & SM_CAP_CENUM) && !(s->selem.caps & SM_CAP_PENUM) ) 1012 return 1; 1013 return 0; 1014 } 1015 if (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM) ) 1016 return 1; 1017 return 0; 1018 1019 case SM_OPS_IS_ENUMCNT: 1020 /* Both */ 1021 if ( (s->selem.caps & (SM_CAP_CENUM | SM_CAP_PENUM)) == (SM_CAP_CENUM | SM_CAP_PENUM) ) { 1022 if (! s->ctls[CTL_GLOBAL_ENUM].elem) 1023 return -EINVAL; 1024 return s->ctls[CTL_GLOBAL_ENUM].max; 1025 /* Only Playback */ 1026 } else if (s->selem.caps & SM_CAP_PENUM ) { 1027 if (! s->ctls[CTL_PLAYBACK_ENUM].elem) 1028 return -EINVAL; 1029 return s->ctls[CTL_PLAYBACK_ENUM].max; 1030 /* Only Capture */ 1031 } else if (s->selem.caps & SM_CAP_CENUM ) { 1032 if (! s->ctls[CTL_CAPTURE_ENUM].elem) 1033 return -EINVAL; 1034 return s->ctls[CTL_CAPTURE_ENUM].max; 1035 } 1036 1037 } 1038 1039 return 1; 1040 } 1041 1042 static int get_range_ops(snd_mixer_elem_t *elem, int dir, 1043 long *min, long *max) 1044 { 1045 selem_none_t *s = snd_mixer_elem_get_private(elem); 1046 *min = s->str[dir].min; 1047 *max = s->str[dir].max; 1048 return 0; 1049 } 1050 1051 static int set_range_ops(snd_mixer_elem_t *elem, int dir, 1052 long min, long max) 1053 { 1054 selem_none_t *s = snd_mixer_elem_get_private(elem); 1055 int err; 1056 1057 s->str[dir].range = 1; 1058 s->str[dir].min = min; 1059 s->str[dir].max = max; 1060 if ((err = selem_read(elem)) < 0) 1061 return err; 1062 return 0; 1063 } 1064 1065 static int get_volume_ops(snd_mixer_elem_t *elem, int dir, 1066 snd_mixer_selem_channel_id_t channel, long *value) 1067 { 1068 selem_none_t *s = snd_mixer_elem_get_private(elem); 1069 if (s->selem.caps & SM_CAP_GVOLUME) 1070 dir = SM_PLAY; 1071 if ((unsigned int) channel >= s->str[dir].channels) 1072 return -EINVAL; 1073 *value = s->str[dir].vol[channel]; 1074 return 0; 1075 } 1076 1077 static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec); 1078 1079 static int convert_to_dB(snd_hctl_elem_t *ctl, struct selem_str *rec, 1080 long volume, long *db_gain) 1081 { 1082 if (init_db_range(ctl, rec) < 0) 1083 return -EINVAL; 1084 return snd_tlv_convert_to_dB(rec->db_info, rec->min, rec->max, 1085 volume, db_gain); 1086 } 1087 1088 /* initialize dB range information, reading TLV via hcontrol 1089 */ 1090 static int init_db_range(snd_hctl_elem_t *ctl, struct selem_str *rec) 1091 { 1092 snd_ctl_elem_info_t *info; 1093 unsigned int *tlv = NULL; 1094 const unsigned int tlv_size = 4096; 1095 unsigned int *dbrec; 1096 int db_size; 1097 1098 if (rec->db_init_error) 1099 return -EINVAL; 1100 if (rec->db_initialized) 1101 return 0; 1102 1103 snd_ctl_elem_info_alloca(&info); 1104 if (snd_hctl_elem_info(ctl, info) < 0) 1105 goto error; 1106 if (! snd_ctl_elem_info_is_tlv_readable(info)) 1107 goto error; 1108 tlv = malloc(tlv_size); 1109 if (! tlv) 1110 return -ENOMEM; 1111 if (snd_hctl_elem_tlv_read(ctl, tlv, tlv_size) < 0) 1112 goto error; 1113 db_size = snd_tlv_parse_dB_info(tlv, tlv_size, &dbrec); 1114 if (db_size < 0) 1115 goto error; 1116 rec->db_info = malloc(db_size); 1117 if (!rec->db_info) 1118 goto error; 1119 memcpy(rec->db_info, dbrec, db_size); 1120 free(tlv); 1121 rec->db_initialized = 1; 1122 return 0; 1123 1124 error: 1125 free(tlv); 1126 rec->db_init_error = 1; 1127 return -EINVAL; 1128 } 1129 1130 /* get selem_ctl for TLV access */ 1131 static selem_ctl_t *get_selem_ctl(selem_none_t *s, int dir) 1132 { 1133 selem_ctl_t *c; 1134 if (dir == SM_PLAY) 1135 c = &s->ctls[CTL_PLAYBACK_VOLUME]; 1136 else if (dir == SM_CAPT) 1137 c = &s->ctls[CTL_CAPTURE_VOLUME]; 1138 else 1139 return NULL; 1140 if (! c->elem) { 1141 c = &s->ctls[CTL_GLOBAL_VOLUME]; 1142 if (! c->elem) 1143 return NULL; 1144 } 1145 if (c->type != SND_CTL_ELEM_TYPE_INTEGER) 1146 return NULL; 1147 return c; 1148 } 1149 1150 static int get_dB_range(snd_hctl_elem_t *ctl, struct selem_str *rec, 1151 long *min, long *max) 1152 { 1153 if (init_db_range(ctl, rec) < 0) 1154 return -EINVAL; 1155 1156 return snd_tlv_get_dB_range(rec->db_info, rec->min, rec->max, min, max); 1157 } 1158 1159 static int get_dB_range_ops(snd_mixer_elem_t *elem, int dir, 1160 long *min, long *max) 1161 { 1162 selem_none_t *s = snd_mixer_elem_get_private(elem); 1163 selem_ctl_t *c; 1164 1165 if (s->selem.caps & SM_CAP_GVOLUME) 1166 dir = SM_PLAY; 1167 c = get_selem_ctl(s, dir); 1168 if (! c) 1169 return -EINVAL; 1170 return get_dB_range(c->elem, &s->str[dir], min, max); 1171 } 1172 1173 static int convert_from_dB(snd_hctl_elem_t *ctl, struct selem_str *rec, 1174 long db_gain, long *value, int xdir) 1175 { 1176 if (init_db_range(ctl, rec) < 0) 1177 return -EINVAL; 1178 1179 return snd_tlv_convert_from_dB(rec->db_info, rec->min, rec->max, 1180 db_gain, value, xdir); 1181 } 1182 1183 static int ask_vol_dB_ops(snd_mixer_elem_t *elem, 1184 int dir, 1185 long value, 1186 long *dBvalue) 1187 { 1188 selem_none_t *s = snd_mixer_elem_get_private(elem); 1189 selem_ctl_t *c; 1190 1191 c = get_selem_ctl(s, dir); 1192 if (! c) 1193 return -EINVAL; 1194 int res = convert_to_dB(c->elem, &s->str[dir], value, dBvalue); 1195 return res; 1196 } 1197 1198 static int get_dB_ops(snd_mixer_elem_t *elem, 1199 int dir, 1200 snd_mixer_selem_channel_id_t channel, 1201 long *value) 1202 { 1203 selem_none_t *s = snd_mixer_elem_get_private(elem); 1204 selem_ctl_t *c; 1205 int err; 1206 long volume, db_gain; 1207 1208 if (s->selem.caps & SM_CAP_GVOLUME) 1209 dir = SM_PLAY; 1210 c = get_selem_ctl(s, dir); 1211 if (! c) 1212 return -EINVAL; 1213 if ((err = get_volume_ops(elem, dir, channel, &volume)) < 0) 1214 goto _err; 1215 if ((err = convert_to_dB(c->elem, &s->str[dir], volume, &db_gain)) < 0) 1216 goto _err; 1217 err = 0; 1218 *value = db_gain; 1219 _err: 1220 return err; 1221 } 1222 1223 static int get_switch_ops(snd_mixer_elem_t *elem, int dir, 1224 snd_mixer_selem_channel_id_t channel, int *value) 1225 { 1226 selem_none_t *s = snd_mixer_elem_get_private(elem); 1227 if (s->selem.caps & SM_CAP_GSWITCH) 1228 dir = SM_PLAY; 1229 if ((unsigned int) channel >= s->str[dir].channels) 1230 return -EINVAL; 1231 *value = !!(s->str[dir].sw & (1 << channel)); 1232 return 0; 1233 } 1234 1235 static int set_volume_ops(snd_mixer_elem_t *elem, int dir, 1236 snd_mixer_selem_channel_id_t channel, long value) 1237 { 1238 int changed; 1239 changed = _snd_mixer_selem_set_volume(elem, dir, channel, value); 1240 if (changed < 0) 1241 return changed; 1242 if (changed) 1243 return selem_write(elem); 1244 return 0; 1245 } 1246 1247 static int ask_dB_vol_ops(snd_mixer_elem_t *elem, int dir, 1248 long dbValue, long *value, int xdir) 1249 { 1250 selem_none_t *s = snd_mixer_elem_get_private(elem); 1251 selem_ctl_t *c; 1252 1253 if (s->selem.caps & SM_CAP_GVOLUME) 1254 dir = SM_PLAY; 1255 c = get_selem_ctl(s, dir); 1256 if (! c) 1257 return -EINVAL; 1258 return convert_from_dB(c->elem, &s->str[dir], dbValue, value, xdir); 1259 } 1260 1261 static int set_dB_ops(snd_mixer_elem_t *elem, int dir, 1262 snd_mixer_selem_channel_id_t channel, 1263 long db_gain, int xdir) 1264 { 1265 selem_none_t *s = snd_mixer_elem_get_private(elem); 1266 selem_ctl_t *c; 1267 long value; 1268 int err; 1269 1270 if (s->selem.caps & SM_CAP_GVOLUME) 1271 dir = SM_PLAY; 1272 c = get_selem_ctl(s, dir); 1273 if (! c) 1274 return -EINVAL; 1275 err = convert_from_dB(c->elem, &s->str[dir], db_gain, &value, xdir); 1276 if (err < 0) 1277 return err; 1278 return set_volume_ops(elem, dir, channel, value); 1279 } 1280 1281 static int set_switch_ops(snd_mixer_elem_t *elem, int dir, 1282 snd_mixer_selem_channel_id_t channel, int value) 1283 { 1284 int changed; 1285 selem_none_t *s = snd_mixer_elem_get_private(elem); 1286 if (s->selem.caps & SM_CAP_GSWITCH) 1287 dir = SM_PLAY; 1288 if (dir == SM_PLAY) { 1289 if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_PSWITCH))) 1290 return -EINVAL; 1291 } else { 1292 if (! (s->selem.caps & (SM_CAP_GSWITCH|SM_CAP_CSWITCH))) 1293 return -EINVAL; 1294 } 1295 changed = _snd_mixer_selem_set_switch(elem, dir, channel, value); 1296 if (changed < 0) 1297 return changed; 1298 if (changed) 1299 return selem_write(elem); 1300 return 0; 1301 } 1302 1303 static int enum_item_name_ops(snd_mixer_elem_t *elem, 1304 unsigned int item, 1305 size_t maxlen, char *buf) 1306 { 1307 selem_none_t *s = snd_mixer_elem_get_private(elem); 1308 snd_ctl_elem_info_t *info; 1309 snd_hctl_elem_t *helem; 1310 int type; 1311 1312 type = CTL_GLOBAL_ENUM; 1313 helem = s->ctls[type].elem; 1314 if (!helem) { 1315 type = CTL_PLAYBACK_ENUM; 1316 helem = s->ctls[type].elem; 1317 } 1318 if (!helem) { 1319 type = CTL_CAPTURE_ENUM; 1320 helem = s->ctls[type].elem; 1321 } 1322 assert(helem); 1323 if (item >= (unsigned int)s->ctls[type].max) 1324 return -EINVAL; 1325 snd_ctl_elem_info_alloca(&info); 1326 snd_hctl_elem_info(helem, info); 1327 snd_ctl_elem_info_set_item(info, item); 1328 snd_hctl_elem_info(helem, info); 1329 strncpy(buf, snd_ctl_elem_info_get_item_name(info), maxlen); 1330 return 0; 1331 } 1332 1333 static int get_enum_item_ops(snd_mixer_elem_t *elem, 1334 snd_mixer_selem_channel_id_t channel, 1335 unsigned int *itemp) 1336 { 1337 selem_none_t *s = snd_mixer_elem_get_private(elem); 1338 snd_ctl_elem_value_t *ctl; 1339 snd_hctl_elem_t *helem; 1340 int err; 1341 1342 if ((unsigned int) channel >= s->str[0].channels) 1343 return -EINVAL; 1344 helem = s->ctls[CTL_GLOBAL_ENUM].elem; 1345 if (!helem) helem = s->ctls[CTL_PLAYBACK_ENUM].elem; 1346 if (!helem) helem = s->ctls[CTL_CAPTURE_ENUM].elem; 1347 assert(helem); 1348 snd_ctl_elem_value_alloca(&ctl); 1349 err = snd_hctl_elem_read(helem, ctl); 1350 if (! err) 1351 *itemp = snd_ctl_elem_value_get_enumerated(ctl, channel); 1352 return err; 1353 } 1354 1355 static int set_enum_item_ops(snd_mixer_elem_t *elem, 1356 snd_mixer_selem_channel_id_t channel, 1357 unsigned int item) 1358 { 1359 selem_none_t *s = snd_mixer_elem_get_private(elem); 1360 snd_ctl_elem_value_t *ctl; 1361 snd_hctl_elem_t *helem; 1362 int err; 1363 int type; 1364 1365 if ((unsigned int) channel >= s->str[0].channels) { 1366 return -EINVAL; 1367 } 1368 type = CTL_GLOBAL_ENUM; 1369 helem = s->ctls[type].elem; 1370 if (!helem) { 1371 type = CTL_PLAYBACK_ENUM; 1372 helem = s->ctls[type].elem; 1373 } 1374 if (!helem) { 1375 type = CTL_CAPTURE_ENUM; 1376 helem = s->ctls[type].elem; 1377 } 1378 assert(helem); 1379 if (item >= (unsigned int)s->ctls[type].max) { 1380 return -EINVAL; 1381 } 1382 snd_ctl_elem_value_alloca(&ctl); 1383 err = snd_hctl_elem_read(helem, ctl); 1384 if (err < 0) { 1385 return err; 1386 } 1387 snd_ctl_elem_value_set_enumerated(ctl, channel, item); 1388 return snd_hctl_elem_write(helem, ctl); 1389 } 1390 1391 static struct sm_elem_ops simple_none_ops = { 1392 .is = is_ops, 1393 .get_range = get_range_ops, 1394 .get_dB_range = get_dB_range_ops, 1395 .set_range = set_range_ops, 1396 .ask_vol_dB = ask_vol_dB_ops, 1397 .ask_dB_vol = ask_dB_vol_ops, 1398 .get_volume = get_volume_ops, 1399 .get_dB = get_dB_ops, 1400 .set_volume = set_volume_ops, 1401 .set_dB = set_dB_ops, 1402 .get_switch = get_switch_ops, 1403 .set_switch = set_switch_ops, 1404 .enum_item_name = enum_item_name_ops, 1405 .get_enum_item = get_enum_item_ops, 1406 .set_enum_item = set_enum_item_ops 1407 }; 1408 1409 static int simple_add1(snd_mixer_class_t *class, const char *name, 1410 snd_hctl_elem_t *helem, selem_ctl_type_t type, 1411 unsigned int value) 1412 { 1413 snd_mixer_elem_t *melem; 1414 snd_mixer_selem_id_t *id; 1415 int new = 0; 1416 int err; 1417 snd_ctl_elem_info_t *info; 1418 selem_none_t *simple; 1419 const char *name1; 1420 snd_ctl_elem_type_t ctype; 1421 unsigned long values; 1422 1423 snd_ctl_elem_info_alloca(&info); 1424 err = snd_hctl_elem_info(helem, info); 1425 if (err < 0) 1426 return err; 1427 ctype = snd_ctl_elem_info_get_type(info); 1428 values = snd_ctl_elem_info_get_count(info); 1429 switch (type) { 1430 case CTL_SINGLE: 1431 if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) 1432 type = CTL_GLOBAL_ENUM; 1433 else if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN && 1434 ctype != SND_CTL_ELEM_TYPE_INTEGER) 1435 return 0; 1436 break; 1437 case CTL_GLOBAL_ROUTE: 1438 case CTL_PLAYBACK_ROUTE: 1439 case CTL_CAPTURE_ROUTE: 1440 { 1441 unsigned int n; 1442 if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) { 1443 if (type == CTL_PLAYBACK_ROUTE) 1444 type = CTL_PLAYBACK_ENUM; 1445 else if (type == CTL_CAPTURE_ROUTE) 1446 type = CTL_CAPTURE_ENUM; 1447 else 1448 type = CTL_GLOBAL_ENUM; 1449 break; 1450 } 1451 if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN) 1452 return 0; 1453 n = sqrt((double)values); 1454 if (n * n != values) 1455 return 0; 1456 values = n; 1457 break; 1458 } 1459 case CTL_GLOBAL_SWITCH: 1460 case CTL_PLAYBACK_SWITCH: 1461 case CTL_CAPTURE_SWITCH: 1462 if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) { 1463 if (type == CTL_PLAYBACK_SWITCH) 1464 type = CTL_PLAYBACK_ENUM; 1465 else if (type == CTL_CAPTURE_SWITCH) 1466 type = CTL_CAPTURE_ENUM; 1467 else 1468 type = CTL_GLOBAL_ENUM; 1469 break; 1470 } 1471 if (ctype != SND_CTL_ELEM_TYPE_BOOLEAN) 1472 return 0; 1473 break; 1474 case CTL_GLOBAL_VOLUME: 1475 case CTL_PLAYBACK_VOLUME: 1476 case CTL_CAPTURE_VOLUME: 1477 if (ctype == SND_CTL_ELEM_TYPE_ENUMERATED) { 1478 if (type == CTL_PLAYBACK_VOLUME) 1479 type = CTL_PLAYBACK_ENUM; 1480 else if (type == CTL_CAPTURE_VOLUME) 1481 type = CTL_CAPTURE_ENUM; 1482 else 1483 type = CTL_GLOBAL_ENUM; 1484 break; 1485 } 1486 if (ctype != SND_CTL_ELEM_TYPE_INTEGER) 1487 return 0; 1488 break; 1489 case CTL_CAPTURE_SOURCE: 1490 if (ctype != SND_CTL_ELEM_TYPE_ENUMERATED) 1491 return 0; 1492 break; 1493 case CTL_GLOBAL_ENUM: 1494 case CTL_PLAYBACK_ENUM: 1495 case CTL_CAPTURE_ENUM: 1496 if (ctype != SND_CTL_ELEM_TYPE_ENUMERATED) 1497 return 0; 1498 break; 1499 default: 1500 assert(0); 1501 break; 1502 } 1503 name1 = get_short_name(name); 1504 if (snd_mixer_selem_id_malloc(&id)) 1505 return -ENOMEM; 1506 snd_mixer_selem_id_set_name(id, name1); 1507 snd_mixer_selem_id_set_index(id, snd_hctl_elem_get_index(helem)); 1508 melem = snd_mixer_find_selem(snd_mixer_class_get_mixer(class), id); 1509 if (!melem) { 1510 simple = calloc(1, sizeof(*simple)); 1511 if (!simple) { 1512 snd_mixer_selem_id_free(id); 1513 return -ENOMEM; 1514 } 1515 simple->selem.id = id; 1516 simple->selem.ops = &simple_none_ops; 1517 err = snd_mixer_elem_new(&melem, SND_MIXER_ELEM_SIMPLE, 1518 get_compare_weight(snd_mixer_selem_id_get_name(simple->selem.id), snd_mixer_selem_id_get_index(simple->selem.id)), 1519 simple, selem_free); 1520 if (err < 0) { 1521 snd_mixer_selem_id_free(id); 1522 free(simple); 1523 return err; 1524 } 1525 new = 1; 1526 } else { 1527 simple = snd_mixer_elem_get_private(melem); 1528 snd_mixer_selem_id_free(id); 1529 } 1530 if (simple->ctls[type].elem) { 1531 SNDERR("helem (%s,'%s',%u,%u,%u) appears twice or more", 1532 snd_ctl_elem_iface_name(snd_hctl_elem_get_interface(helem)), 1533 snd_hctl_elem_get_name(helem), 1534 snd_hctl_elem_get_index(helem), 1535 snd_hctl_elem_get_device(helem), 1536 snd_hctl_elem_get_subdevice(helem)); 1537 err = -EINVAL; 1538 goto __error; 1539 } 1540 simple->ctls[type].elem = helem; 1541 simple->ctls[type].type = snd_ctl_elem_info_get_type(info); 1542 simple->ctls[type].inactive = snd_ctl_elem_info_is_inactive(info); 1543 simple->ctls[type].values = values; 1544 if ( (type == CTL_GLOBAL_ENUM) || 1545 (type == CTL_PLAYBACK_ENUM) || 1546 (type == CTL_CAPTURE_ENUM) ) { 1547 simple->ctls[type].min = 0; 1548 simple->ctls[type].max = snd_ctl_elem_info_get_items(info); 1549 } else { 1550 if (ctype == SND_CTL_ELEM_TYPE_INTEGER) { 1551 simple->ctls[type].min = snd_ctl_elem_info_get_min(info); 1552 simple->ctls[type].max = snd_ctl_elem_info_get_max(info); 1553 } 1554 } 1555 switch (type) { 1556 case CTL_CAPTURE_SOURCE: 1557 simple->capture_item = value; 1558 break; 1559 default: 1560 break; 1561 } 1562 err = snd_mixer_elem_attach(melem, helem); 1563 if (err < 0) 1564 goto __error; 1565 err = simple_update(melem); 1566 if (err < 0) { 1567 if (new) 1568 goto __error; 1569 return err; 1570 } 1571 if (new) 1572 err = snd_mixer_elem_add(melem, class); 1573 else 1574 err = snd_mixer_elem_info(melem); 1575 if (err < 0) 1576 return err; 1577 err = selem_read(melem); 1578 if (err < 0) 1579 return err; 1580 if (err) 1581 err = snd_mixer_elem_value(melem); 1582 return err; 1583 __error: 1584 if (new) 1585 snd_mixer_elem_free(melem); 1586 return -EINVAL; 1587 } 1588 1589 static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem) 1590 { 1591 const char *name = snd_hctl_elem_get_name(helem); 1592 size_t len; 1593 selem_ctl_type_t type = CTL_SINGLE; /* to shut up warning */ 1594 if (snd_hctl_elem_get_interface(helem) != SND_CTL_ELEM_IFACE_MIXER) 1595 return 0; 1596 if (strcmp(name, "Capture Source") == 0) { 1597 snd_ctl_elem_info_t *info; 1598 unsigned int k, items; 1599 int err; 1600 snd_ctl_elem_info_alloca(&info); 1601 err = snd_hctl_elem_info(helem, info); 1602 assert(err >= 0); 1603 if (snd_ctl_elem_info_get_type(info) != SND_CTL_ELEM_TYPE_ENUMERATED) 1604 return 0; 1605 items = snd_ctl_elem_info_get_items(info); 1606 for (k = 0; k < items; ++k) { 1607 const char *n; 1608 snd_ctl_elem_info_set_item(info, k); 1609 err = snd_hctl_elem_info(helem, info); 1610 if (err < 0) 1611 return err; 1612 n = snd_ctl_elem_info_get_item_name(info); 1613 err = simple_add1(class, n, helem, CTL_CAPTURE_SOURCE, k); 1614 if (err < 0) 1615 return err; 1616 } 1617 return 0; 1618 } 1619 len = base_len(name, &type); 1620 if (len == 0) { 1621 return simple_add1(class, name, helem, CTL_SINGLE, 0); 1622 } else { 1623 char ename[128]; 1624 if (len >= sizeof(ename)) 1625 len = sizeof(ename) - 1; 1626 memcpy(ename, name, len); 1627 ename[len] = 0; 1628 /* exception: Capture Volume and Capture Switch */ 1629 if (type == CTL_GLOBAL_VOLUME && !strcmp(ename, "Capture")) 1630 type = CTL_CAPTURE_VOLUME; 1631 else if (type == CTL_GLOBAL_SWITCH && !strcmp(ename, "Capture")) 1632 type = CTL_CAPTURE_SWITCH; 1633 return simple_add1(class, ename, helem, type, 0); 1634 } 1635 } 1636 1637 static int simple_event_remove(snd_hctl_elem_t *helem, 1638 snd_mixer_elem_t *melem) 1639 { 1640 selem_none_t *simple = snd_mixer_elem_get_private(melem); 1641 int err; 1642 int k; 1643 for (k = 0; k <= CTL_LAST; k++) { 1644 if (simple->ctls[k].elem == helem) 1645 break; 1646 } 1647 assert(k <= CTL_LAST); 1648 simple->ctls[k].elem = NULL; 1649 err = snd_mixer_elem_detach(melem, helem); 1650 if (err < 0) 1651 return err; 1652 if (snd_mixer_elem_empty(melem)) 1653 return snd_mixer_elem_remove(melem); 1654 err = simple_update(melem); 1655 return snd_mixer_elem_info(melem); 1656 } 1657 1658 static int simple_event(snd_mixer_class_t *class, unsigned int mask, 1659 snd_hctl_elem_t *helem, snd_mixer_elem_t *melem) 1660 { 1661 int err; 1662 if (mask == SND_CTL_EVENT_MASK_REMOVE) 1663 return simple_event_remove(helem, melem); 1664 if (mask & SND_CTL_EVENT_MASK_ADD) { 1665 err = simple_event_add(class, helem); 1666 if (err < 0) 1667 return err; 1668 } 1669 if (mask & SND_CTL_EVENT_MASK_INFO) { 1670 err = simple_event_remove(helem, melem); 1671 if (err < 0) 1672 return err; 1673 err = simple_event_add(class, helem); 1674 if (err < 0) 1675 return err; 1676 return 0; 1677 } 1678 if (mask & SND_CTL_EVENT_MASK_VALUE) { 1679 err = selem_read(melem); 1680 if (err < 0) 1681 return err; 1682 if (err) { 1683 err = snd_mixer_elem_value(melem); 1684 if (err < 0) 1685 return err; 1686 } 1687 } 1688 return 0; 1689 } 1690 1691 /** 1692 * \brief Register mixer simple element class - none abstraction 1693 * \param mixer Mixer handle 1694 * \param options Options container 1695 * \param classp Pointer to returned mixer simple element class handle (or NULL) 1696 * \return 0 on success otherwise a negative error code 1697 */ 1698 int snd_mixer_simple_none_register(snd_mixer_t *mixer, 1699 struct snd_mixer_selem_regopt *options ATTRIBUTE_UNUSED, 1700 snd_mixer_class_t **classp) 1701 { 1702 snd_mixer_class_t *class; 1703 int err; 1704 1705 if (snd_mixer_class_malloc(&class)) 1706 return -ENOMEM; 1707 snd_mixer_class_set_event(class, simple_event); 1708 snd_mixer_class_set_compare(class, snd_mixer_selem_compare); 1709 err = snd_mixer_class_register(class, mixer); 1710 if (err < 0) { 1711 free(class); 1712 return err; 1713 } 1714 if (classp) 1715 *classp = class; 1716 return 0; 1717 } 1718