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