xref: /third_party/alsa-utils/iecset/iecset.c (revision c72fcc34)
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