1/* 2 iecset - change IEC958 status bits on ALSA 3 Copyright (C) 2003 by Takashi Iwai <tiwai@suse.de> 4 5 This program is free software; you can redistribute it and/or 6 modify it under the terms of the GNU General Public License 7 as published by the Free Software Foundation; either version 2 8 of the License, or (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20#include "aconfig.h" 21#include <stdio.h> 22#include <ctype.h> 23#include <alsa/asoundlib.h> 24 25void dump_iec958(snd_aes_iec958_t *iec); 26 27static int get_bool(const char *str) 28{ 29 if (strncmp(str, "yes", 3) == 0 || 30 strncmp(str, "YES", 3) == 0 || 31 strncmp(str, "on", 2) == 0 || 32 strncmp(str, "ON", 2) == 0 || 33 strncmp(str, "true", 4) == 0 || 34 strncmp(str, "TRUE", 4) == 0 || 35 *str == '1') 36 return 1; 37 return 0; 38} 39 40enum { 41 CMD_BOOL, CMD_BOOL_INV, CMD_INT 42}; 43 44enum { 45 IDX_PRO, IDX_NOAUDIO, IDX_RATE, IDX_UNLOCK, IDX_SBITS, IDX_WORD, IDX_EMP, IDX_CAT, IDX_NOCOPY, IDX_ORIG, 46 IDX_LAST 47}; 48 49struct cmdtbl { 50 const char *name; 51 int idx; 52 int type; 53 const char *desc; 54}; 55 56static const struct cmdtbl cmds[] = { 57 { "pro", IDX_PRO, CMD_BOOL, 58 "professional (common)\n\toff = consumer mode, on = professional mode" }, 59 { "aud", IDX_NOAUDIO, CMD_BOOL_INV, 60 "audio (common)\n\ton = audio mode, off = non-audio mode" }, 61 { "rat", IDX_RATE, CMD_INT, 62 "rate (common)\n\tsample rate in Hz (0 = not indicated)" }, 63 { "emp", IDX_EMP, CMD_INT, 64 "emphasis (common)\n\t0 = none, 1 = 50/15us, 2 = CCITT" }, 65 { "loc", IDX_UNLOCK, CMD_BOOL_INV, 66 "lock (prof.)\n\toff = rate unlocked, on = rate locked" }, 67 { "sbi", IDX_SBITS, CMD_INT, 68 "sbits (prof.)\n\tsample bits 2 = 20bit, 4 = 24bit, 6 = undef" }, 69 { "wor", IDX_WORD, CMD_INT, 70 "wordlength (prof.)\n\t0=no, 2=22-18bit, 4=23-19bit, 5=24-20bit, 6=20-16bit" }, 71 { "cat", IDX_CAT, CMD_INT, 72 "category (consumer)\n\t0-0x7f" }, 73 { "cop", IDX_NOCOPY, CMD_BOOL_INV, 74 "copyright (consumer)\n\toff = non-copyright, on = copyright" }, 75 { "ori", IDX_ORIG, CMD_BOOL, 76 "original (consumer)\n\toff = 1st-gen, on = original" }, 77}; 78 79 80static void error(const char *s, int err) 81{ 82 fprintf(stderr, "%s: %s\n", s, snd_strerror(err)); 83} 84 85 86static void usage(void) 87{ 88 int i; 89 90 printf("Usage: iecset [options] [cmd arg...]\n"); 91 printf("Options:\n"); 92 printf(" -D device specifies the control device to use\n"); 93 printf(" -c card specifies the card number to use (equiv. with -Dhw:#)\n"); 94 printf(" -n number specifies the control index number (default = 0)\n"); 95 printf(" -x dump the dump the AESx hex code for IEC958 PCM parameters\n"); 96 printf(" -i read commands from stdin\n"); 97 printf("Commands:\n"); 98 for (i = 0; i < (int)(sizeof(cmds)/sizeof(cmds[0])); i++) { 99 printf(" %s\n", cmds[i].desc); 100 } 101} 102 103 104/* 105 * parse iecset commands 106 */ 107static void parse_command(int *parms, const char *c, const char *arg) 108{ 109 int i; 110 111 for (i = 0; i < (int)(sizeof(cmds)/sizeof(cmds[0])); i++) { 112 if (strncmp(c, cmds[i].name, strlen(cmds[i].name)) == 0) { 113 int val; 114 switch (cmds[i].type) { 115 case CMD_BOOL: 116 val = get_bool(arg); 117 break; 118 case CMD_BOOL_INV: 119 val = !get_bool(arg); 120 break; 121 case CMD_INT: 122 default: 123 val = (int)strtol(arg, NULL, 0); 124 break; 125 } 126 parms[cmds[i].idx] = val; 127 return; 128 } 129 } 130} 131 132static char *skipspace(char *line) 133{ 134 char *p; 135 for (p = line; *p && isspace(*p); p++) 136 ; 137 return p; 138} 139 140/* 141 * parse iecset commands from the file 142 */ 143static void parse_file(int *parms, FILE *fp) 144{ 145 char line[1024], *cmd, *arg; 146 while (fgets(line, sizeof(line), fp) != NULL) { 147 cmd = skipspace(line); 148 if (*cmd == '#' || ! *cmd) 149 continue; 150 for (arg = cmd; *arg && !isspace(*arg); arg++) 151 ; 152 if (! *arg) 153 continue; 154 *arg++ = 0; 155 arg = skipspace(arg); 156 if (! *arg) 157 continue; 158 parse_command(parms, cmd, arg); 159 } 160} 161 162/* update iec958 status values 163 * return non-zero if the values are modified 164 */ 165static int update_iec958_status(snd_aes_iec958_t *iec958, int *parms) 166{ 167 int changed = 0; 168 if (parms[IDX_PRO] >= 0) { 169 if (parms[IDX_PRO]) 170 iec958->status[0] |= IEC958_AES0_PROFESSIONAL; 171 else 172 iec958->status[0] &= ~IEC958_AES0_PROFESSIONAL; 173 changed = 1; 174 } 175 if (parms[IDX_NOAUDIO] >= 0) { 176 if (parms[IDX_NOAUDIO]) 177 iec958->status[0] |= IEC958_AES0_NONAUDIO; 178 else 179 iec958->status[0] &= ~IEC958_AES0_NONAUDIO; 180 changed = 1; 181 } 182 if (parms[IDX_RATE] >= 0) { 183 if (iec958->status[0] & IEC958_AES0_PROFESSIONAL) { 184 iec958->status[0] &= ~IEC958_AES0_PRO_FS; 185 switch (parms[IDX_RATE]) { 186 case 44100: 187 iec958->status[0] |= IEC958_AES0_PRO_FS_44100; 188 break; 189 case 48000: 190 iec958->status[0] |= IEC958_AES0_PRO_FS_48000; 191 break; 192 case 32000: 193 iec958->status[0] |= IEC958_AES0_PRO_FS_32000; 194 break; 195 } 196 } else { 197 iec958->status[3] &= ~IEC958_AES3_CON_FS; 198 switch (parms[IDX_RATE]) { 199 case 22050: 200 iec958->status[3] |= IEC958_AES3_CON_FS_22050; 201 break; 202 case 24000: 203 iec958->status[3] |= IEC958_AES3_CON_FS_24000; 204 break; 205 case 32000: 206 iec958->status[3] |= IEC958_AES3_CON_FS_32000; 207 break; 208 case 44100: 209 iec958->status[3] |= IEC958_AES3_CON_FS_44100; 210 break; 211 case 48000: 212 iec958->status[3] |= IEC958_AES3_CON_FS_48000; 213 break; 214 case 88200: 215 iec958->status[3] |= IEC958_AES3_CON_FS_88200;; 216 break; 217 case 96000: 218 iec958->status[3] |= IEC958_AES3_CON_FS_96000; 219 break; 220 case 176400: 221 iec958->status[3] |= IEC958_AES3_CON_FS_176400; 222 break; 223 case 192000: 224 iec958->status[3] |= IEC958_AES3_CON_FS_192000; 225 break; 226 case 768000: 227 iec958->status[3] |= IEC958_AES3_CON_FS_768000; 228 break; 229 default: 230 iec958->status[3] |= IEC958_AES3_CON_FS_NOTID; 231 break; 232 } 233 } 234 changed = 1; 235 } 236 if (parms[IDX_NOCOPY] >= 0) { 237 if (! (iec958->status[0] & IEC958_AES0_PROFESSIONAL)) { 238 if (parms[IDX_NOCOPY]) 239 iec958->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; 240 else 241 iec958->status[0] &= ~IEC958_AES0_CON_NOT_COPYRIGHT; 242 } 243 changed = 1; 244 } 245 if (parms[IDX_ORIG] >= 0) { 246 if (! (iec958->status[0] & IEC958_AES0_PROFESSIONAL)) { 247 if (parms[IDX_ORIG]) 248 iec958->status[1] |= IEC958_AES1_CON_ORIGINAL; 249 else 250 iec958->status[1] &= ~IEC958_AES1_CON_ORIGINAL; 251 } 252 changed = 1; 253 } 254 if (parms[IDX_EMP] >= 0) { 255 if (iec958->status[0] & IEC958_AES0_PROFESSIONAL) { 256 iec958->status[0] &= ~IEC958_AES0_PRO_EMPHASIS; 257 switch (parms[IDX_EMP]) { 258 case 0: 259 iec958->status[0] |= IEC958_AES0_PRO_EMPHASIS_NONE; 260 break; 261 case 1: 262 iec958->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015; 263 break; 264 case 2: 265 iec958->status[0] |= IEC958_AES0_PRO_EMPHASIS_CCITT; 266 break; 267 } 268 } else { 269 if (parms[IDX_EMP]) 270 iec958->status[0] |= IEC958_AES0_CON_EMPHASIS_5015; 271 else 272 iec958->status[0] &= ~IEC958_AES0_CON_EMPHASIS_5015; 273 } 274 changed = 1; 275 } 276 if (parms[IDX_UNLOCK] >= 0) { 277 if (iec958->status[0] & IEC958_AES0_PROFESSIONAL) { 278 if (parms[IDX_UNLOCK]) 279 iec958->status[0] |= IEC958_AES0_PRO_FREQ_UNLOCKED; 280 else 281 iec958->status[0] &= ~IEC958_AES0_PRO_FREQ_UNLOCKED; 282 } 283 changed = 1; 284 } 285 if (parms[IDX_SBITS] >= 0) { 286 if (iec958->status[0] & IEC958_AES0_PROFESSIONAL) { 287 iec958->status[2] &= ~IEC958_AES2_PRO_SBITS; 288 iec958->status[2] |= parms[IDX_SBITS] & 7; 289 } 290 changed = 1; 291 } 292 if (parms[IDX_WORD] >= 0) { 293 if (iec958->status[0] & IEC958_AES0_PROFESSIONAL) { 294 iec958->status[2] &= ~IEC958_AES2_PRO_WORDLEN; 295 iec958->status[2] |= (parms[IDX_WORD] & 7) << 3; 296 } 297 changed = 1; 298 } 299 if (parms[IDX_CAT] >= 0) { 300 if (! (iec958->status[0] & IEC958_AES0_PROFESSIONAL)) { 301 iec958->status[1] &= ~IEC958_AES1_CON_CATEGORY; 302 iec958->status[1] |= parms[IDX_CAT] & 0x7f; 303 } 304 changed = 1; 305 } 306 307 return changed; 308} 309 310 311int main(int argc, char **argv) 312{ 313 const char *dev = "default"; 314 const char *spdif_str = SND_CTL_NAME_IEC958("", PLAYBACK, DEFAULT); 315 int spdif_index = -1; 316 snd_ctl_t *ctl; 317 snd_ctl_elem_list_t *clist; 318 snd_ctl_elem_id_t *cid; 319 snd_ctl_elem_value_t *cval; 320 snd_aes_iec958_t iec958; 321 int from_stdin = 0; 322 int dumphex = 0; 323 int i, c, err; 324 unsigned int controls, cidx; 325 char tmpname[32]; 326 int parms[IDX_LAST]; 327 328 for (i = 0; i < IDX_LAST; i++) 329 parms[i] = -1; /* not set */ 330 331 while ((c = getopt(argc, argv, "D:c:n:xhi")) != -1) { 332 switch (c) { 333 case 'D': 334 dev = optarg; 335 break; 336 case 'c': 337 i = atoi(optarg); 338 if (i < 0 || i >= 32) { 339 fprintf(stderr, "invalid card index %d\n", i); 340 return 1; 341 } 342 sprintf(tmpname, "hw:%d", i); 343 dev = tmpname; 344 break; 345 case 'n': 346 spdif_index = atoi(optarg); 347 break; 348 case 'x': 349 dumphex = 1; 350 break; 351 case 'i': 352 from_stdin = 1; 353 break; 354 default: 355 usage(); 356 return 1; 357 } 358 } 359 360 if ((err = snd_ctl_open(&ctl, dev, 0)) < 0) { 361 error("snd_ctl_open", err); 362 return 1; 363 } 364 365 snd_ctl_elem_list_alloca(&clist); 366 if ((err = snd_ctl_elem_list(ctl, clist)) < 0) { 367 error("snd_ctl_elem_list", err); 368 return 1; 369 } 370 if ((err = snd_ctl_elem_list_alloc_space(clist, snd_ctl_elem_list_get_count(clist))) < 0) { 371 error("snd_ctl_elem_list_alloc_space", err); 372 return 1; 373 } 374 if ((err = snd_ctl_elem_list(ctl, clist)) < 0) { 375 error("snd_ctl_elem_list", err); 376 return 1; 377 } 378 379 controls = snd_ctl_elem_list_get_used(clist); 380 for (cidx = 0; cidx < controls; cidx++) { 381 if (!strcmp(snd_ctl_elem_list_get_name(clist, cidx), spdif_str)) 382 if (spdif_index < 0 || 383 (int)snd_ctl_elem_list_get_index(clist, cidx) == spdif_index) 384 break; 385 } 386 if (cidx >= controls) { 387 fprintf(stderr, "control \"%s\" (index %d) not found\n", 388 spdif_str, spdif_index); 389 return 1; 390 } 391 392 snd_ctl_elem_id_alloca(&cid); 393 snd_ctl_elem_list_get_id(clist, cidx, cid); 394 snd_ctl_elem_value_alloca(&cval); 395 snd_ctl_elem_value_set_id(cval, cid); 396 if ((err = snd_ctl_elem_read(ctl, cval)) < 0) { 397 error("snd_ctl_elem_read", err); 398 return 1; 399 } 400 401 snd_ctl_elem_value_get_iec958(cval, &iec958); 402 403 /* parse from stdin */ 404 if (from_stdin) 405 parse_file(parms, stdin); 406 407 /* parse commands */ 408 for (c = optind; c < argc - 1; c += 2) 409 parse_command(parms, argv[c], argv[c + 1]); 410 411 if (update_iec958_status(&iec958, parms)) { 412 /* store the values */ 413 snd_ctl_elem_value_set_iec958(cval, &iec958); 414 if ((err = snd_ctl_elem_write(ctl, cval)) < 0) { 415 error("snd_ctl_elem_write", err); 416 return 1; 417 } 418 if ((err = snd_ctl_elem_read(ctl, cval)) < 0) { 419 error("snd_ctl_elem_write", err); 420 return 1; 421 } 422 snd_ctl_elem_value_get_iec958(cval, &iec958); 423 } 424 425 if (dumphex) 426 printf("AES0=0x%02x,AES1=0x%02x,AES2=0x%02x,AES3=0x%02x\n", 427 iec958.status[0], iec958.status[1], iec958.status[2], iec958.status[3]); 428 else 429 dump_iec958(&iec958); 430 431 snd_ctl_close(ctl); 432 return 0; 433} 434