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