xref: /openbsd-src/usr.bin/audioctl/audioctl.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: audioctl.c,v 1.34 2016/08/31 09:48:26 jsg Exp $	*/
2 /*
3  * Copyright (c) 2016 Alexandre Ratchov <alex@caoua.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <sys/types.h>
18 #include <sys/ioctl.h>
19 #include <sys/audioio.h>
20 #include <fcntl.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <err.h>
27 
28 /*
29  * Default bytes per sample for the given bits per sample.
30  */
31 #define BPS(bits) (((bits) <= 8) ? 1 : (((bits) <= 16) ? 2 : 4))
32 
33 struct audio_device rname;
34 struct audio_status rstatus;
35 struct audio_swpar rpar, wpar;
36 struct audio_pos rpos;
37 
38 struct field {
39 	char *name;
40 	void *raddr, *waddr;
41 #define MODE	0
42 #define NUM	1
43 #define STR	2
44 #define ENC	3
45 	int type;
46 	int set;
47 } fields[] = {
48 	{"name",		&rname.name,		NULL,		STR},
49 	{"mode",		&rstatus.mode,		NULL,		MODE},
50 	{"pause",		&rstatus.pause,		NULL,		NUM},
51 	{"active",		&rstatus.active,	NULL,		NUM},
52 	{"nblks",		&rpar.nblks,		&wpar.nblks,	NUM},
53 	{"blksz",		&rpar.round,		&wpar.round,	NUM},
54 	{"rate",		&rpar.rate,		&wpar.rate,	NUM},
55 	{"encoding",		&rpar,			&wpar,		ENC},
56 	{"play.channels",	&rpar.pchan,		&wpar.pchan,	NUM},
57 	{"play.bytes",		&rpos.play_pos,		NULL,		NUM},
58 	{"play.errors",		&rpos.play_xrun,	NULL,		NUM},
59 	{"record.channels",	&rpar.rchan,		&wpar.rchan, 	NUM},
60 	{"record.bytes",	&rpos.rec_pos,		NULL,		NUM},
61 	{"record.errors",	&rpos.rec_xrun,		NULL,		NUM},
62 	{NULL,			NULL,			0}
63 };
64 
65 const char usagestr[] =
66 	"usage: audioctl [-f file]\n"
67 	"       audioctl [-n] [-f file] name ...\n"
68 	"       audioctl [-nq] [-f file] name=value ...\n";
69 
70 /*
71  * parse encoding string (examples: s8, u8, s16, s16le, s24be ...)
72  * and fill enconding fields of audio_swpar structure
73  */
74 int
75 strtoenc(struct audio_swpar *ap, char *p)
76 {
77 	/* expect "s" or "u" (signedness) */
78 	if (*p == 's')
79 		ap->sig = 1;
80 	else if (*p == 'u')
81 		ap->sig = 0;
82 	else
83 		return 0;
84 	p++;
85 
86 	/* expect 1-2 decimal digits (bits per sample) */
87 	ap->bits = 0;
88 	while (*p >= '0' && *p <= '9') {
89 		ap->bits = (ap->bits * 10) + *p++ - '0';
90 		if (ap->bits > 32)
91 			return 0;
92 	}
93 	if (ap->bits < 8)
94 		return 0;
95 
96 	/* set defaults as next tokens are optional */
97 	ap->bps = BPS(ap->bits);
98 	ap->le = (BYTE_ORDER == LITTLE_ENDIAN);
99 	ap->msb = 1;
100 	if (*p == '\0')
101 		return 1;
102 
103 	/* expect "le" or "be" (endianness) */
104 	if (p[0] == 'l' && p[1] == 'e')
105 		ap->le = 1;
106 	else if (p[0] == 'b' && p[1] == 'e')
107 		ap->le = 0;
108 	else
109 		return 0;
110 	p += 2;
111 	if (*p == '\0')
112 		return 1;
113 
114 	/* expect 1 decimal digit (number of bytes) */
115 	if (*p < '0' || *p > '9')
116 		return 0;
117 	ap->bps = *p - '0';
118 	if (ap->bps < ((ap->bits + 7) >> 3) || ap->bps > 4)
119 		return 0;
120 	if (*++p == '\0')
121 		return 1;
122 
123 	/* expect "msb" or "lsb" (alignment) */
124 	if (p[0] == 'm' && p[1] == 's' && p[2] == 'b')
125 		ap->msb = 1;
126 	else if (p[0] == 'l' && p[1] == 's' && p[2] == 'b')
127 		ap->msb = 0;
128 	else if (*p == '\0')
129 		return 1;
130 	p += 3;
131 	if (*p == '\0')
132 		return 1;
133 
134 	/* must be no additional junk */
135 	return 0;
136 }
137 
138 void
139 print_val(struct field *p, void *addr)
140 {
141 	int mode;
142 	struct audio_swpar *ap;
143 
144 	switch (p->type) {
145 	case NUM:
146 		printf("%u", *(unsigned int *)addr);
147 		break;
148 	case STR:
149 		printf("%s", (char *)addr);
150 		break;
151 	case MODE:
152 		mode = *(unsigned int *)addr;
153 		if (mode & AUMODE_PLAY)
154 			printf("play");
155 		if (mode & AUMODE_RECORD) {
156 			if (mode & AUMODE_PLAY)
157 				printf(",");
158 			printf("record");
159 		}
160 		break;
161 	case ENC:
162 		ap = addr;
163 		printf("%s%u", ap->sig ? "s" : "u", ap->bits);
164 		if (ap->bps == 1)
165 			break;
166 		printf("%s", ap->le ? "le" : "be");
167 		if (ap->bps != BPS(ap->bits) || ap->bits < ap->bps * 8) {
168 			printf("%u", ap->bps);
169 			if (ap->bits < ap->bps * 8)
170 				printf("%s", ap->msb ? "msb" : "lsb");
171 		}
172 	}
173 }
174 
175 void
176 parse_val(struct field *f, void *addr, char *p)
177 {
178 	const char *strerr;
179 
180 	switch (f->type) {
181 	case NUM:
182 		*(unsigned int *)addr = strtonum(p, 0, UINT_MAX, &strerr);
183 		if (strerr)
184 			errx(1, "%s: %s", p, strerr);
185 		break;
186 	case ENC:
187 		if (!strtoenc((struct audio_swpar *)addr, p))
188 			errx(1, "%s: bad encoding", p);
189 	}
190 }
191 
192 int
193 main(int argc, char **argv)
194 {
195 	struct field *f;
196 	char *lhs, *rhs, *path = "/dev/audioctl0";
197 	int fd, c, set = 0, print_names = 1, quiet = 0;
198 
199 	while ((c = getopt(argc, argv, "anf:q")) != -1) {
200 		switch (c) {
201 		case 'a':	/* ignored, compat */
202 			break;
203 		case 'n':
204 			print_names = 0;
205 			break;
206 		case 'f':
207 			path = optarg;
208 			break;
209 		case 'q':
210 			quiet = 1;
211 			break;
212 		default:
213 			fputs(usagestr, stderr);
214 			return 1;
215 		}
216 	}
217 	argc -= optind;
218 	argv += optind;
219 
220 	fd = open(path, O_RDWR);
221 	if (fd < 0)
222 		err(1, "%s", path);
223 	if (ioctl(fd, AUDIO_GETSTATUS, &rstatus) < 0)
224 		err(1, "AUDIO_GETSTATUS");
225 	if (ioctl(fd, AUDIO_GETDEV, &rname) < 0)
226 		err(1, "AUDIO_GETDEV");
227 	if (ioctl(fd, AUDIO_GETPAR, &rpar) < 0)
228 		err(1, "AUDIO_GETPAR");
229 	if (ioctl(fd, AUDIO_GETPOS, &rpos) < 0)
230 		err(1, "AUDIO_GETPOS");
231 	if (argc == 0) {
232 		for (f = fields; f->name != NULL; f++) {
233 			printf("%s=", f->name);
234 			print_val(f, f->raddr);
235 			printf("\n");
236 		}
237 	}
238 	AUDIO_INITPAR(&wpar);
239 	for (; argc > 0; argc--, argv++) {
240 		lhs = *argv;
241 		rhs = strchr(*argv, '=');
242 		if (rhs)
243 			*rhs++ = '\0';
244 		for (f = fields;; f++) {
245 			if (f->name == NULL)
246 				errx(1, "%s: unknown parameter", lhs);
247 			if (strcmp(f->name, lhs) == 0)
248 				break;
249 		}
250 		if (rhs) {
251 			if (f->waddr == NULL)
252 				errx(1, "%s: is read only", f->name);
253 			parse_val(f, f->waddr, rhs);
254 			f->set = 1;
255 			set = 1;
256 		} else {
257 			if (print_names)
258 				printf("%s=", f->name);
259 			print_val(f, f->raddr);
260 			printf("\n");
261 		}
262 	}
263 	if (!set)
264 		return 0;
265 	if (ioctl(fd, AUDIO_SETPAR, &wpar) < 0)
266 		err(1, "AUDIO_SETPAR");
267 	if (ioctl(fd, AUDIO_GETPAR, &wpar) < 0)
268 		err(1, "AUDIO_GETPAR");
269 	for (f = fields; f->name != NULL; f++) {
270 		if (!f->set || quiet)
271 			continue;
272 		if (print_names) {
273 			printf("%s: ", f->name);
274 			print_val(f, f->raddr);
275 			printf(" -> ");
276 		}
277 		print_val(f, f->waddr);
278 		printf("\n");
279 	}
280 	close(fd);
281 	return 0;
282 }
283