control.c (12208B)
1 /* 2 * A simple PCM loopback utility 3 * Copyright (c) 2010 by Jaroslav Kysela <perex@perex.cz> 4 * 5 * Author: Jaroslav Kysela <perex@perex.cz> 6 * 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22 */ 23 24 #include <ctype.h> 25 #include <syslog.h> 26 #include <alsa/asoundlib.h> 27 #include "alsaloop.h" 28 29 static char *id_str(snd_ctl_elem_id_t *id) 30 { 31 static char str[128]; 32 33 sprintf(str, "%i,%s,%i,%i,%s,%i", 34 snd_ctl_elem_id_get_numid(id), 35 snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(id)), 36 snd_ctl_elem_id_get_device(id), 37 snd_ctl_elem_id_get_subdevice(id), 38 snd_ctl_elem_id_get_name(id), 39 snd_ctl_elem_id_get_index(id)); 40 return str; 41 } 42 43 int control_parse_id(const char *str, snd_ctl_elem_id_t *id) 44 { 45 int c, size, numid; 46 char *ptr; 47 48 while (*str == ' ' || *str == '\t') 49 str++; 50 if (!(*str)) 51 return -EINVAL; 52 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); /* default */ 53 while (*str) { 54 if (!strncasecmp(str, "numid=", 6)) { 55 str += 6; 56 numid = atoi(str); 57 if (numid <= 0) { 58 logit(LOG_CRIT, "Invalid numid %d\n", numid); 59 return -EINVAL; 60 } 61 snd_ctl_elem_id_set_numid(id, atoi(str)); 62 while (isdigit(*str)) 63 str++; 64 } else if (!strncasecmp(str, "iface=", 6)) { 65 str += 6; 66 if (!strncasecmp(str, "card", 4)) { 67 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD); 68 str += 4; 69 } else if (!strncasecmp(str, "mixer", 5)) { 70 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); 71 str += 5; 72 } else if (!strncasecmp(str, "pcm", 3)) { 73 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM); 74 str += 3; 75 } else if (!strncasecmp(str, "rawmidi", 7)) { 76 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_RAWMIDI); 77 str += 7; 78 } else if (!strncasecmp(str, "timer", 5)) { 79 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_TIMER); 80 str += 5; 81 } else if (!strncasecmp(str, "sequencer", 9)) { 82 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_SEQUENCER); 83 str += 9; 84 } else { 85 return -EINVAL; 86 } 87 } else if (!strncasecmp(str, "name=", 5)) { 88 char buf[64]; 89 str += 5; 90 ptr = buf; 91 size = 0; 92 if (*str == '\'' || *str == '\"') { 93 c = *str++; 94 while (*str && *str != c) { 95 if (size < (int)sizeof(buf)) { 96 *ptr++ = *str; 97 size++; 98 } 99 str++; 100 } 101 if (*str == c) 102 str++; 103 } else { 104 while (*str && *str != ',') { 105 if (size < (int)sizeof(buf)) { 106 *ptr++ = *str; 107 size++; 108 } 109 str++; 110 } 111 } 112 *ptr = '\0'; 113 snd_ctl_elem_id_set_name(id, buf); 114 } else if (!strncasecmp(str, "index=", 6)) { 115 str += 6; 116 snd_ctl_elem_id_set_index(id, atoi(str)); 117 while (isdigit(*str)) 118 str++; 119 } else if (!strncasecmp(str, "device=", 7)) { 120 str += 7; 121 snd_ctl_elem_id_set_device(id, atoi(str)); 122 while (isdigit(*str)) 123 str++; 124 } else if (!strncasecmp(str, "subdevice=", 10)) { 125 str += 10; 126 snd_ctl_elem_id_set_subdevice(id, atoi(str)); 127 while (isdigit(*str)) 128 str++; 129 } 130 if (*str == ',') { 131 str++; 132 } else { 133 if (*str) 134 return -EINVAL; 135 } 136 } 137 return 0; 138 } 139 140 int control_id_match(snd_ctl_elem_id_t *id1, snd_ctl_elem_id_t *id2) 141 { 142 if (snd_ctl_elem_id_get_interface(id1) != 143 snd_ctl_elem_id_get_interface(id2)) 144 return 0; 145 if (snd_ctl_elem_id_get_device(id1) != 146 snd_ctl_elem_id_get_device(id2)) 147 return 0; 148 if (snd_ctl_elem_id_get_subdevice(id1) != 149 snd_ctl_elem_id_get_subdevice(id2)) 150 return 0; 151 if (strcmp(snd_ctl_elem_id_get_name(id1), 152 snd_ctl_elem_id_get_name(id2)) != 0) 153 return 0; 154 if (snd_ctl_elem_id_get_index(id1) != 155 snd_ctl_elem_id_get_index(id2)) 156 return 0; 157 return 1; 158 } 159 160 static int control_init1(struct loopback_handle *lhandle, 161 struct loopback_control *ctl) 162 { 163 int err; 164 165 snd_ctl_elem_info_set_id(ctl->info, ctl->id); 166 snd_ctl_elem_value_set_id(ctl->value, ctl->id); 167 if (lhandle->ctl == NULL) { 168 logit(LOG_WARNING, "Unable to read control info for '%s'\n", id_str(ctl->id)); 169 return -EIO; 170 } 171 err = snd_ctl_elem_info(lhandle->ctl, ctl->info); 172 if (err < 0) { 173 logit(LOG_WARNING, "Unable to read control info '%s': %s\n", id_str(ctl->id), snd_strerror(err)); 174 return err; 175 } 176 err = snd_ctl_elem_read(lhandle->ctl, ctl->value); 177 if (err < 0) { 178 logit(LOG_WARNING, "Unable to read control value (init1) '%s': %s\n", id_str(ctl->id), snd_strerror(err)); 179 return err; 180 } 181 return 0; 182 } 183 184 static int copy_value(struct loopback_control *dst, 185 struct loopback_control *src) 186 { 187 snd_ctl_elem_type_t type; 188 unsigned int count; 189 int i; 190 191 type = snd_ctl_elem_info_get_type(dst->info); 192 count = snd_ctl_elem_info_get_count(dst->info); 193 switch (type) { 194 case SND_CTL_ELEM_TYPE_BOOLEAN: 195 for (i = 0; i < count; i++) 196 snd_ctl_elem_value_set_boolean(dst->value, 197 i, snd_ctl_elem_value_get_boolean(src->value, i)); 198 break; 199 case SND_CTL_ELEM_TYPE_INTEGER: 200 for (i = 0; i < count; i++) { 201 snd_ctl_elem_value_set_integer(dst->value, 202 i, snd_ctl_elem_value_get_integer(src->value, i)); 203 } 204 break; 205 default: 206 logit(LOG_CRIT, "Unable to copy control value for type %s\n", snd_ctl_elem_type_name(type)); 207 return -EINVAL; 208 } 209 return 0; 210 } 211 212 static int oss_set(struct loopback *loop, 213 struct loopback_ossmixer *ossmix, 214 int enable) 215 { 216 char buf[128], file[128]; 217 int fd; 218 219 if (loop->capt->card_number < 0) 220 return 0; 221 if (!enable) { 222 sprintf(buf, "%s \"\" 0\n", ossmix->oss_id); 223 } else { 224 sprintf(buf, "%s \"%s\" %i\n", ossmix->oss_id, ossmix->alsa_id, ossmix->alsa_index); 225 } 226 sprintf(file, "/proc/asound/card%i/oss_mixer", loop->capt->card_number); 227 if (verbose) 228 snd_output_printf(loop->output, "%s: Initialize OSS volume %s: %s", loop->id, file, buf); 229 fd = open(file, O_WRONLY); 230 if (fd >= 0 && write(fd, buf, strlen(buf)) == strlen(buf)) { 231 close(fd); 232 return 0; 233 } 234 if (fd >= 0) 235 close(fd); 236 logit(LOG_INFO, "%s: Unable to initialize OSS Mixer ID '%s'\n", loop->id, ossmix->oss_id); 237 return -1; 238 } 239 240 static int control_init2(struct loopback *loop, 241 struct loopback_mixer *mix) 242 { 243 snd_ctl_elem_type_t type; 244 unsigned int count; 245 int err; 246 247 snd_ctl_elem_info_copy(mix->dst.info, mix->src.info); 248 snd_ctl_elem_info_set_id(mix->dst.info, mix->dst.id); 249 snd_ctl_elem_value_clear(mix->dst.value); 250 snd_ctl_elem_value_set_id(mix->dst.value, mix->dst.id); 251 type = snd_ctl_elem_info_get_type(mix->dst.info); 252 count = snd_ctl_elem_info_get_count(mix->dst.info); 253 snd_ctl_elem_remove(loop->capt->ctl, mix->dst.id); 254 switch (type) { 255 case SND_CTL_ELEM_TYPE_BOOLEAN: 256 err = snd_ctl_elem_add_boolean(loop->capt->ctl, 257 mix->dst.id, count); 258 copy_value(&mix->dst, &mix->src); 259 break; 260 case SND_CTL_ELEM_TYPE_INTEGER: 261 err = snd_ctl_elem_add_integer(loop->capt->ctl, 262 mix->dst.id, count, 263 snd_ctl_elem_info_get_min(mix->dst.info), 264 snd_ctl_elem_info_get_max(mix->dst.info), 265 snd_ctl_elem_info_get_step(mix->dst.info)); 266 copy_value(&mix->dst, &mix->src); 267 break; 268 default: 269 logit(LOG_CRIT, "Unable to handle control type %s\n", snd_ctl_elem_type_name(type)); 270 err = -EINVAL; 271 break; 272 } 273 if (err < 0) { 274 logit(LOG_CRIT, "Unable to create control '%s': %s\n", id_str(mix->dst.id), snd_strerror(err)); 275 return err; 276 } 277 err = snd_ctl_elem_unlock(loop->capt->ctl, mix->dst.id); 278 if (err < 0) { 279 logit(LOG_CRIT, "Unable to unlock control info '%s': %s\n", id_str(mix->dst.id), snd_strerror(err)); 280 return err; 281 } 282 err = snd_ctl_elem_info(loop->capt->ctl, mix->dst.info); 283 if (err < 0) { 284 logit(LOG_CRIT, "Unable to read control info '%s': %s\n", id_str(mix->dst.id), snd_strerror(err)); 285 return err; 286 } 287 if (snd_ctl_elem_info_is_tlv_writable(mix->dst.info)) { 288 unsigned int tlv[64]; 289 err = snd_ctl_elem_tlv_read(loop->play->ctl, 290 mix->src.id, 291 tlv, sizeof(tlv)); 292 if (err < 0) { 293 logit(LOG_CRIT, "Unable to read TLV for '%s': %s\n", id_str(mix->src.id), snd_strerror(err)); 294 tlv[0] = tlv[1] = 0; 295 } 296 err = snd_ctl_elem_tlv_write(loop->capt->ctl, 297 mix->dst.id, 298 tlv); 299 if (err < 0) { 300 logit(LOG_CRIT, "Unable to write TLV for '%s': %s\n", id_str(mix->src.id), snd_strerror(err)); 301 return err; 302 } 303 } 304 err = snd_ctl_elem_write(loop->capt->ctl, mix->dst.value); 305 if (err < 0) { 306 logit(LOG_CRIT, "Unable to write control value '%s': %s\n", id_str(mix->dst.id), snd_strerror(err)); 307 return err; 308 } 309 return 0; 310 } 311 312 int control_init(struct loopback *loop) 313 { 314 struct loopback_mixer *mix; 315 struct loopback_ossmixer *ossmix; 316 int err; 317 318 for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next) 319 oss_set(loop, ossmix, 0); 320 for (mix = loop->controls; mix; mix = mix->next) { 321 err = control_init1(loop->play, &mix->src); 322 if (err < 0) { 323 logit(LOG_WARNING, "%s: Disabling playback control '%s'\n", loop->id, id_str(mix->src.id)); 324 mix->skip = 1; 325 continue; 326 } 327 err = control_init2(loop, mix); 328 if (err < 0) 329 return err; 330 } 331 for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next) { 332 err = oss_set(loop, ossmix, 1); 333 if (err < 0) { 334 ossmix->skip = 1; 335 logit(LOG_WARNING, "%s: Disabling OSS mixer ID '%s'\n", loop->id, ossmix->oss_id); 336 } 337 } 338 return 0; 339 } 340 341 int control_done(struct loopback *loop) 342 { 343 struct loopback_mixer *mix; 344 struct loopback_ossmixer *ossmix; 345 int err; 346 347 if (loop->capt->ctl == NULL) 348 return 0; 349 for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next) { 350 err = oss_set(loop, ossmix, 0); 351 if (err < 0) 352 logit(LOG_WARNING, "%s: Unable to remove OSS control '%s'\n", loop->id, ossmix->oss_id); 353 } 354 for (mix = loop->controls; mix; mix = mix->next) { 355 if (mix->skip) 356 continue; 357 err = snd_ctl_elem_remove(loop->capt->ctl, mix->dst.id); 358 if (err < 0) 359 logit(LOG_WARNING, "%s: Unable to remove control '%s': %s\n", loop->id, id_str(mix->dst.id), snd_strerror(err)); 360 } 361 return 0; 362 } 363 364 static int control_event1(struct loopback *loop, 365 struct loopback_mixer *mix, 366 snd_ctl_event_t *ev, 367 int capture) 368 { 369 unsigned int mask = snd_ctl_event_elem_get_mask(ev); 370 int err; 371 372 if (mask == SND_CTL_EVENT_MASK_REMOVE) 373 return 0; 374 if ((mask & SND_CTL_EVENT_MASK_VALUE) == 0) 375 return 0; 376 if (!capture) { 377 snd_ctl_elem_value_set_id(mix->src.value, mix->src.id); 378 err = snd_ctl_elem_read(loop->play->ctl, mix->src.value); 379 if (err < 0) { 380 logit(LOG_CRIT, "Unable to read control value (event1) '%s': %s\n", id_str(mix->src.id), snd_strerror(err)); 381 return err; 382 } 383 copy_value(&mix->dst, &mix->src); 384 err = snd_ctl_elem_write(loop->capt->ctl, mix->dst.value); 385 if (err < 0) { 386 logit(LOG_CRIT, "Unable to write control value (event1) '%s': %s\n", id_str(mix->dst.id), snd_strerror(err)); 387 return err; 388 } 389 } else { 390 err = snd_ctl_elem_read(loop->capt->ctl, mix->dst.value); 391 if (err < 0) { 392 logit(LOG_CRIT, "Unable to read control value (event2) '%s': %s\n", id_str(mix->dst.id), snd_strerror(err)); 393 return err; 394 } 395 copy_value(&mix->src, &mix->dst); 396 err = snd_ctl_elem_write(loop->play->ctl, mix->src.value); 397 if (err < 0) { 398 logit(LOG_CRIT, "Unable to write control value (event2) '%s': %s\n", id_str(mix->src.id), snd_strerror(err)); 399 return err; 400 } 401 } 402 return 0; 403 } 404 405 int control_event(struct loopback_handle *lhandle, snd_ctl_event_t *ev) 406 { 407 snd_ctl_elem_id_t *id2; 408 struct loopback_mixer *mix; 409 int capt = lhandle == lhandle->loopback->capt; 410 int err; 411 412 snd_ctl_elem_id_alloca(&id2); 413 snd_ctl_event_elem_get_id(ev, id2); 414 for (mix = lhandle->loopback->controls; mix; mix = mix->next) { 415 if (mix->skip) 416 continue; 417 if (control_id_match(id2, capt ? mix->dst.id : mix->src.id)) { 418 err = control_event1(lhandle->loopback, mix, ev, capt); 419 if (err < 0) 420 return err; 421 } 422 } 423 return 0; 424 }