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
25 void dump_iec958(snd_aes_iec958_t *iec);
26
get_bool(const char *str)27 static 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
40 enum {
41 CMD_BOOL, CMD_BOOL_INV, CMD_INT
42 };
43
44 enum {
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
49 struct cmdtbl {
50 const char *name;
51 int idx;
52 int type;
53 const char *desc;
54 };
55
56 static 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
error(const char *s, int err)80 static void error(const char *s, int err)
81 {
82 fprintf(stderr, "%s: %s\n", s, snd_strerror(err));
83 }
84
85
usage(void)86 static 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 */
parse_command(int *parms, const char *c, const char *arg)107 static 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
skipspace(char *line)132 static 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 */
parse_file(int *parms, FILE *fp)143 static 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 */
update_iec958_status(snd_aes_iec958_t *iec958, int *parms)165 static 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
main(int argc, char **argv)311 int 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