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