Print this page
1031 Minor audioctl save-controls issue with newlines in hardware info
10620 audioctl: don't return failure because of illegal enum value
10617 audiohd: don't create record-source control if there is none
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/audio/audioctl/audioctl.c
+++ new/usr/src/cmd/audio/audioctl/audioctl.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 26 #include <stdio.h>
27 27 #include <stdlib.h>
28 28 #include <errno.h>
29 29 #include <string.h>
30 30 #include <strings.h>
31 31 #include <locale.h>
32 32 #include <libintl.h>
33 33 #include <stdarg.h>
34 34 #include <stddef.h>
35 35 #include <sys/types.h>
36 36 #include <sys/stat.h>
37 37 #include <sys/mkdev.h>
38 38 #include <fcntl.h>
39 39 #include <unistd.h>
40 40 #include <ctype.h>
41 41 #include <sys/param.h>
42 42 #include <sys/soundcard.h>
43 43 #include <libdevinfo.h>
44 44
45 45 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
46 46 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
47 47 #endif
48 48
49 49 #define _(s) gettext(s)
50 50
51 51 #define MAXLINE 1024
52 52
53 53 #define AUDIO_CTRL_STEREO_LEFT(v) ((uint8_t)((v) & 0xff))
54 54 #define AUDIO_CTRL_STEREO_RIGHT(v) ((uint8_t)(((v) >> 8) & 0xff))
55 55 #define AUDIO_CTRL_STEREO_VAL(l, r) (((l) & 0xff) | (((r) & 0xff) << 8))
56 56
57 57 /*
58 58 * These are borrowed from sys/audio/audio_common.h, where the values
59 59 * are protected by _KERNEL.
60 60 */
61 61 #define AUDIO_MN_TYPE_NBITS (4)
62 62 #define AUDIO_MN_TYPE_MASK ((1U << AUDIO_MN_TYPE_NBITS) - 1)
63 63 #define AUDIO_MINOR_MIXER (0)
64 64
65 65
66 66 /*
67 67 * Column display information
68 68 * All are related to the types enumerated in col_t and any change should be
69 69 * reflected in the corresponding indices and offsets for all the variables
70 70 * accordingly. Most tweaks to the display can be done by adjusting the
71 71 * values here.
72 72 */
73 73
74 74 /* types of columns displayed */
75 75 typedef enum { COL_DV = 0, COL_NM, COL_VAL, COL_SEL} col_t;
76 76
77 77 /* corresponding sizes of columns; does not include trailing null */
78 78 #define COL_DV_SZ 16
79 79 #define COL_NM_SZ 24
80 80 #define COL_VAL_SZ 10
81 81 #define COL_SEL_SZ 20
82 82 #define COL_MAX_SZ 64
83 83
84 84 /* corresponding sizes of columns, indexed by col_t value */
85 85 static int col_sz[] = {
86 86 COL_DV_SZ, COL_NM_SZ, COL_VAL_SZ, COL_SEL_SZ
87 87 };
88 88
89 89 /* used by callers of the printing function */
90 90 typedef struct col_prt {
91 91 char *col_dv;
92 92 char *col_nm;
93 93 char *col_val;
94 94 char *col_sel;
95 95 } col_prt_t;
96 96
97 97 /* columns displayed in order with vopt = 0 */
98 98 static int col_dpy[] = {COL_NM, COL_VAL};
99 99 static int col_dpy_len = sizeof (col_dpy) / sizeof (*col_dpy);
100 100
101 101 /* tells the printing function what members to use; follows col_dpy[] */
102 102 static size_t col_dpy_prt[] = {
103 103 offsetof(col_prt_t, col_nm),
104 104 offsetof(col_prt_t, col_val),
105 105 };
106 106
107 107 /* columns displayed in order with vopt = 1 */
108 108 static int col_dpy_vopt[] = { COL_DV, COL_NM, COL_VAL, COL_SEL};
109 109 static int col_dpy_vopt_len = sizeof (col_dpy_vopt) / sizeof (*col_dpy_vopt);
110 110
111 111 /* tells the printing function what members to use; follows col_dpy_vopt[] */
112 112 static size_t col_dpy_prt_vopt[] = {
113 113 offsetof(col_prt_t, col_dv),
114 114 offsetof(col_prt_t, col_nm),
115 115 offsetof(col_prt_t, col_val),
116 116 offsetof(col_prt_t, col_sel)
117 117 };
118 118
119 119 /* columns displayed in order with tofile = 1 */
120 120 static int col_dpy_tofile[] = { COL_NM, COL_VAL};
121 121 static int col_dpy_tofile_len = sizeof (col_dpy_tofile) /
122 122 sizeof (*col_dpy_tofile);
123 123
124 124 /* tells the printing function what members to use; follows col_dpy_tofile[] */
125 125 static size_t col_dpy_prt_tofile[] = {
126 126 offsetof(col_prt_t, col_nm),
127 127 offsetof(col_prt_t, col_val)
128 128 };
129 129
130 130
131 131 /*
132 132 * mixer and control accounting
133 133 */
134 134
135 135 typedef struct cinfo {
136 136 oss_mixext ci;
137 137 oss_mixer_enuminfo *enump;
138 138 } cinfo_t;
139 139
140 140 typedef struct device {
141 141 oss_card_info card;
142 142 oss_mixerinfo mixer;
143 143
144 144 int cmax;
145 145 cinfo_t *controls;
146 146
147 147 int mfd;
148 148 dev_t devt;
149 149
150 150 struct device *nextp;
151 151 } device_t;
152 152
153 153 static device_t *devices = NULL;
154 154
155 155 /*PRINTFLIKE1*/
156 156 static void
157 157 msg(char *fmt, ...)
158 158 {
159 159 va_list ap;
160 160
161 161 va_start(ap, fmt);
162 162 (void) vprintf(fmt, ap);
163 163 va_end(ap);
164 164 }
165 165
166 166 /*PRINTFLIKE1*/
167 167 static void
168 168 warn(char *fmt, ...)
169 169 {
170 170 va_list ap;
171 171
172 172 va_start(ap, fmt);
173 173 (void) vfprintf(stderr, fmt, ap);
174 174 va_end(ap);
175 175 }
176 176
177 177 static void
178 178 free_device(device_t *d)
179 179 {
180 180 int i;
181 181 device_t **dpp;
182 182
183 183 dpp = &devices;
184 184 while ((*dpp) && ((*dpp) != d)) {
185 185 dpp = &((*dpp)->nextp);
186 186 }
187 187 if (*dpp) {
188 188 *dpp = d->nextp;
189 189 }
190 190 for (i = 0; i < d->cmax; i++) {
191 191 if (d->controls[i].enump != NULL)
192 192 free(d->controls[i].enump);
193 193 }
194 194
195 195 if (d->mfd >= 0)
196 196 (void) close(d->mfd);
197 197
198 198 free(d);
199 199 }
200 200
201 201 static void
202 202 free_devices(void)
203 203 {
204 204 device_t *d = devices;
205 205
206 206 while ((d = devices) != NULL) {
207 207 free_device(d);
208 208 }
209 209
210 210 devices = NULL;
211 211 }
212 212
213 213
214 214 /*
215 215 * adds to the end of global devices and returns a pointer to the new entry
216 216 */
217 217 static device_t *
218 218 alloc_device(void)
219 219 {
220 220 device_t *p;
221 221 device_t *d = calloc(1, sizeof (*d));
222 222
223 223 d->card.card = -1;
224 224 d->mixer.dev = -1;
225 225 d->mfd = -1;
226 226
227 227 if (devices == NULL) {
228 228 devices = d;
229 229 } else {
230 230 for (p = devices; p->nextp != NULL; p = p->nextp) {}
231 231
232 232 p->nextp = d;
233 233 }
234 234 return (d);
235 235 }
236 236
237 237
238 238 /*
239 239 * cinfop->enump needs to be present
240 240 * idx should be: >= 0 to < cinfop->ci.maxvalue
241 241 */
242 242 static char *
243 243 get_enum_str(cinfo_t *cinfop, int idx)
244 244 {
245 245 int sz = sizeof (*cinfop->ci.enum_present) * 8;
246 246
247 247 if (cinfop->ci.enum_present[idx / sz] & (1 << (idx % sz)))
248 248 return (cinfop->enump->strings + cinfop->enump->strindex[idx]);
249 249
250 250 return (NULL);
251 251 }
252 252
253 253
254 254 /*
255 255 * caller fills in d->mixer.devnode; func fills in the rest
256 256 */
257 257 static int
258 258 get_device_info(device_t *d)
259 259 {
260 260 int fd = -1;
261 261 int i;
262 262 cinfo_t *ci;
263 263
264 264 if ((fd = open(d->mixer.devnode, O_RDWR)) < 0) {
265 265 perror(_("Error opening device"));
266 266 return (errno);
267 267 }
268 268 d->mfd = fd;
269 269
270 270 d->cmax = -1;
271 271 if (ioctl(fd, SNDCTL_MIX_NREXT, &d->cmax) < 0) {
272 272 perror(_("Error getting control count"));
273 273 return (errno);
274 274 }
275 275
276 276 d->controls = calloc(d->cmax, sizeof (*d->controls));
277 277
278 278 for (i = 0; i < d->cmax; i++) {
279 279 ci = &d->controls[i];
280 280
281 281 ci->ci.dev = -1;
282 282 ci->ci.ctrl = i;
283 283
284 284 if (ioctl(fd, SNDCTL_MIX_EXTINFO, &ci->ci) < 0) {
285 285 perror(_("Error getting control info"));
286 286 return (errno);
287 287 }
288 288
289 289 if (ci->ci.type == MIXT_ENUM) {
290 290 ci->enump = calloc(1, sizeof (*ci->enump));
291 291 ci->enump->dev = -1;
292 292 ci->enump->ctrl = ci->ci.ctrl;
293 293
294 294 if (ioctl(fd, SNDCTL_MIX_ENUMINFO, ci->enump) < 0) {
295 295 perror(_("Error getting enum info"));
296 296 return (errno);
297 297 }
298 298 }
299 299 }
300 300
301 301 return (0);
302 302 }
303 303
304 304
305 305 static int
306 306 load_devices(void)
307 307 {
308 308 int rv = -1;
309 309 int fd = -1;
310 310 int i;
311 311 oss_sysinfo si;
312 312 device_t *d;
313 313
314 314 if (devices != NULL) {
315 315 /* already loaded */
316 316 return (0);
317 317 }
318 318
319 319 if ((fd = open("/dev/mixer", O_RDWR)) < 0) {
320 320 rv = errno;
321 321 warn(_("Error opening mixer\n"));
322 322 goto OUT;
323 323 }
324 324
325 325 if (ioctl(fd, SNDCTL_SYSINFO, &si) < 0) {
326 326 rv = errno;
327 327 perror(_("Error getting system information"));
328 328 goto OUT;
329 329 }
330 330
331 331 for (i = 0; i < si.nummixers; i++) {
332 332
333 333 struct stat sbuf;
334 334
335 335 d = alloc_device();
336 336 d->mixer.dev = i;
337 337
338 338 if (ioctl(fd, SNDCTL_MIXERINFO, &d->mixer) != 0) {
339 339 continue;
340 340 }
341 341
342 342 d->card.card = d->mixer.card_number;
343 343
344 344 if ((ioctl(fd, SNDCTL_CARDINFO, &d->card) != 0) ||
345 345 (stat(d->mixer.devnode, &sbuf) != 0) ||
346 346 ((sbuf.st_mode & S_IFCHR) == 0)) {
347 347 warn(_("Device present: %s\n"), d->mixer.devnode);
348 348 free_device(d);
349 349 continue;
350 350 }
351 351 d->devt = makedev(major(sbuf.st_rdev),
352 352 minor(sbuf.st_rdev) & ~(AUDIO_MN_TYPE_MASK));
353 353
354 354 if ((rv = get_device_info(d)) != 0) {
355 355 free_device(d);
356 356 goto OUT;
357 357 }
358 358 }
359 359
360 360 rv = 0;
361 361
362 362 OUT:
363 363 if (fd >= 0)
364 364 (void) close(fd);
365 365 return (rv);
366 366 }
367 367
368 368
369 369 static int
370 370 ctype_valid(int type)
371 371 {
372 372 switch (type) {
373 373 case MIXT_ONOFF:
374 374 case MIXT_ENUM:
375 375 case MIXT_MONOSLIDER:
376 376 case MIXT_STEREOSLIDER:
377 377 return (1);
378 378 default:
379 379 return (0);
380 380 }
381 381 }
382 382
383 383
384 384 static void
385 385 print_control_line(FILE *sfp, col_prt_t *colp, int vopt)
386 386 {
387 387 int i;
388 388 size_t *col_prtp;
389 389 int *col_dpyp;
390 390 int col_cnt;
391 391 int col_type;
392 392 int width;
393 393 char *colstr;
394 394 char cbuf[COL_MAX_SZ + 1];
395 395 char line[128];
396 396 char *colsep = " ";
397 397
398 398 if (sfp != NULL) {
399 399 col_prtp = col_dpy_prt_tofile;
400 400 col_dpyp = col_dpy_tofile;
401 401 col_cnt = col_dpy_tofile_len;
402 402 } else if (vopt) {
403 403 col_prtp = col_dpy_prt_vopt;
404 404 col_dpyp = col_dpy_vopt;
405 405 col_cnt = col_dpy_vopt_len;
406 406 } else {
407 407 col_prtp = col_dpy_prt;
408 408 col_dpyp = col_dpy;
409 409 col_cnt = col_dpy_len;
410 410 }
411 411
412 412 line[0] = '\0';
413 413
414 414 for (i = 0; i < col_cnt; i++) {
415 415 col_type = col_dpyp[i];
416 416 width = col_sz[col_type];
417 417 colstr = *(char **)(((size_t)colp) + col_prtp[i]);
418 418
419 419 (void) snprintf(cbuf, sizeof (cbuf), "%- *s",
420 420 width > 0 ? width : 1,
421 421 (colstr == NULL) ? "" : colstr);
422 422
423 423 (void) strlcat(line, cbuf, sizeof (line));
424 424 if (i < col_cnt - 1)
425 425 (void) strlcat(line, colsep, sizeof (line));
426 426 }
427 427
428 428 (void) fprintf(sfp ? sfp : stdout, "%s\n", line);
429 429 }
430 430
431 431
432 432 static void
433 433 print_header(FILE *sfp, int vopt)
434 434 {
435 435 col_prt_t col;
436 436
437 437 if (sfp) {
438 438 col.col_nm = _("#CONTROL");
439 439 col.col_val = _("VALUE");
440 440 } else {
441 441 col.col_dv = _("DEVICE");
442 442 col.col_nm = _("CONTROL");
443 443 col.col_val = _("VALUE");
444 444 col.col_sel = _("POSSIBLE");
445 445 }
446 446 print_control_line(sfp, &col, vopt);
447 447 }
448 448
449 449
450 450 static int
451 451 print_control(FILE *sfp, device_t *d, cinfo_t *cinfop, int vopt)
452 452 {
453 453 int mfd = d->mfd;
454 454 char *devnm = d->card.shortname;
455 455 oss_mixer_value cval;
456 456 char *str;
457 457 int i;
458 458 int idx = -1;
459 459 int rv = -1;
460 460 char valbuf[COL_VAL_SZ + 1];
461 461 char selbuf[COL_SEL_SZ + 1];
462 462 col_prt_t col;
463 463
464 464 cval.dev = -1;
465 465 cval.ctrl = cinfop->ci.ctrl;
466 466
467 467 if (ctype_valid(cinfop->ci.type)) {
468 468 if (ioctl(mfd, SNDCTL_MIX_READ, &cval) < 0) {
469 469 rv = errno;
470 470 perror(_("Error reading control\n"));
471 471 return (rv);
472 472 }
473 473 } else {
474 474 return (0);
475 475 }
476 476
477 477 /*
478 478 * convert the control value into a string
479 479 */
480 480 switch (cinfop->ci.type) {
481 481 case MIXT_ONOFF:
482 482 (void) snprintf(valbuf, sizeof (valbuf), "%s",
483 483 cval.value ? _("on") : _("off"));
484 484 break;
485 485
486 486 case MIXT_MONOSLIDER:
487 487 (void) snprintf(valbuf, sizeof (valbuf), "%d",
488 488 cval.value & 0xff);
489 489 break;
490 490
491 491 case MIXT_STEREOSLIDER:
↓ open down ↓ |
491 lines elided |
↑ open up ↑ |
492 492 (void) snprintf(valbuf, sizeof (valbuf), "%d:%d",
493 493 (int)AUDIO_CTRL_STEREO_LEFT(cval.value),
494 494 (int)AUDIO_CTRL_STEREO_RIGHT(cval.value));
495 495 break;
496 496
497 497 case MIXT_ENUM:
498 498 str = get_enum_str(cinfop, cval.value);
499 499 if (str == NULL) {
500 500 warn(_("Bad enum index %d for control '%s'\n"),
501 501 cval.value, cinfop->ci.extname);
502 - return (EINVAL);
502 + /*
503 + * Apparently the driver gave us bogus data for this
504 + * control, so we will ignore it.
505 + *
506 + * Don't confuse the user by returning an error status.
507 + */
508 + return (0);
503 509 }
504 510
505 511 (void) snprintf(valbuf, sizeof (valbuf), "%s", str);
506 512 break;
507 513
508 514 default:
509 515 return (0);
510 516 }
511 517
512 518 /*
513 519 * possible control values (range/selection)
514 520 */
515 521 switch (cinfop->ci.type) {
516 522 case MIXT_ONOFF:
517 523 (void) snprintf(selbuf, sizeof (selbuf), _("on,off"));
518 524 break;
519 525
520 526 case MIXT_MONOSLIDER:
521 527 (void) snprintf(selbuf, sizeof (selbuf), "%d-%d",
522 528 cinfop->ci.minvalue, cinfop->ci.maxvalue);
523 529 break;
524 530 case MIXT_STEREOSLIDER:
525 531 (void) snprintf(selbuf, sizeof (selbuf), "%d-%d:%d-%d",
526 532 cinfop->ci.minvalue, cinfop->ci.maxvalue,
527 533 cinfop->ci.minvalue, cinfop->ci.maxvalue);
528 534 break;
529 535
530 536 case MIXT_ENUM:
531 537 /*
532 538 * display the first choice on the same line, then display
533 539 * the rest on multiple lines
534 540 */
535 541 selbuf[0] = 0;
536 542 for (i = 0; i < cinfop->ci.maxvalue; i++) {
537 543 str = get_enum_str(cinfop, i);
538 544 if (str == NULL)
539 545 continue;
540 546
541 547 if ((strlen(str) + 1 + strlen(selbuf)) >=
542 548 sizeof (selbuf)) {
543 549 break;
544 550 }
545 551 if (strlen(selbuf)) {
546 552 (void) strlcat(selbuf, ",", sizeof (selbuf));
547 553 }
548 554
549 555 (void) strlcat(selbuf, str, sizeof (selbuf));
550 556 }
551 557 idx = i;
552 558 break;
553 559
554 560 default:
555 561 (void) snprintf(selbuf, sizeof (selbuf), "-");
556 562 }
557 563
558 564 col.col_dv = devnm;
559 565 col.col_nm = strlen(cinfop->ci.extname) ?
560 566 cinfop->ci.extname : cinfop->ci.id;
561 567 while (strchr(col.col_nm, '_') != NULL) {
562 568 col.col_nm = strchr(col.col_nm, '_') + 1;
563 569 }
564 570 col.col_val = valbuf;
565 571 col.col_sel = selbuf;
566 572 print_control_line(sfp, &col, vopt);
567 573
568 574 /* non-verbose mode prints don't display the enum values */
569 575 if ((!vopt) || (sfp != NULL)) {
570 576 return (0);
571 577 }
572 578
573 579 /* print leftover enum value selections */
574 580 while ((idx >= 0) && (idx < cinfop->ci.maxvalue)) {
575 581 selbuf[0] = 0;
576 582 for (i = idx; i < cinfop->ci.maxvalue; i++) {
577 583 str = get_enum_str(cinfop, i);
578 584 if (str == NULL)
579 585 continue;
580 586
581 587 if ((strlen(str) + 1 + strlen(selbuf)) >=
582 588 sizeof (selbuf)) {
583 589 break;
584 590 }
585 591 if (strlen(selbuf)) {
586 592 (void) strlcat(selbuf, ",", sizeof (selbuf));
587 593 }
588 594
589 595 (void) strlcat(selbuf, str, sizeof (selbuf));
590 596 }
591 597 idx = i;
592 598 col.col_dv = NULL;
593 599 col.col_nm = NULL;
594 600 col.col_val = NULL;
595 601 col.col_sel = selbuf;
596 602 print_control_line(sfp, &col, vopt);
597 603 }
598 604
599 605 return (0);
600 606 }
601 607
602 608
603 609 static int
604 610 set_device_control(device_t *d, cinfo_t *cinfop, char *wstr, int vopt)
605 611 {
606 612 int mfd = d->mfd;
607 613 oss_mixer_value cval;
608 614 int wlen = strlen(wstr);
609 615 int lval, rval;
610 616 char *lstr, *rstr;
611 617 char *str;
612 618 int i;
613 619 int rv = -1;
614 620
615 621 cval.dev = -1;
616 622 cval.ctrl = cinfop->ci.ctrl;
617 623 cval.value = 0;
618 624
619 625 switch (cinfop->ci.type) {
620 626 case MIXT_ONOFF:
621 627 cval.value = (strncmp(_("on"), wstr, wlen) == 0) ? 1 : 0;
622 628 break;
623 629
624 630 case MIXT_MONOSLIDER:
625 631 cval.value = atoi(wstr);
626 632 break;
627 633
628 634 case MIXT_STEREOSLIDER:
629 635 lstr = wstr;
630 636 rstr = strchr(wstr, ':');
631 637 if (rstr != NULL) {
632 638 *rstr = '\0';
633 639 rstr++;
634 640
635 641 rval = atoi(rstr);
636 642 lval = atoi(lstr);
637 643
638 644 rstr--;
639 645 *rstr = ':';
640 646 } else {
641 647 lval = atoi(lstr);
642 648 rval = lval;
643 649 }
644 650
645 651 cval.value = AUDIO_CTRL_STEREO_VAL(lval, rval);
646 652 break;
647 653
648 654 case MIXT_ENUM:
649 655 for (i = 0; i < cinfop->ci.maxvalue; i++) {
650 656 str = get_enum_str(cinfop, i);
651 657 if (str == NULL)
652 658 continue;
653 659
654 660 if (strncmp(wstr, str, wlen) == 0) {
655 661 cval.value = i;
656 662 break;
657 663 }
658 664 }
659 665
660 666 if (i >= cinfop->ci.maxvalue) {
661 667 warn(_("Invalid enumeration value\n"));
662 668 return (EINVAL);
663 669 }
664 670 break;
665 671
666 672 default:
667 673 warn(_("Unsupported control type: %d\n"), cinfop->ci.type);
668 674 return (EINVAL);
669 675 }
670 676
671 677 if (vopt) {
672 678 msg(_("%s: '%s' set to '%s'\n"), d->card.shortname,
673 679 cinfop->ci.extname, wstr);
674 680 }
675 681
676 682 if (ioctl(mfd, SNDCTL_MIX_WRITE, &cval) < 0) {
677 683 rv = errno;
678 684 perror(_("Error writing control"));
679 685 return (rv);
680 686 }
681 687
682 688 rv = 0;
683 689 return (rv);
684 690 }
685 691
686 692
687 693 static void
688 694 help(void)
689 695 {
690 696 #define HELP_STR _( \
691 697 "audioctl list-devices\n" \
692 698 " list all audio devices\n" \
693 699 "\n" \
694 700 "audioctl show-device [ -v ] [ -d <device> ]\n" \
695 701 " display information about an audio device\n" \
696 702 "\n" \
697 703 "audioctl show-control [ -v ] [ -d <device> ] [ <control> ... ]\n" \
698 704 " get the value of a specific control (all if not specified)\n" \
699 705 "\n" \
700 706 "audioctl set-control [ -v ] [ -d <device> ] <control> <value>\n" \
701 707 " set the value of a specific control\n" \
702 708 "\n" \
703 709 "audioctl save-controls [ -d <device> ] [ -f ] <file>\n" \
704 710 " save all control settings for the device to a file\n" \
705 711 "\n" \
706 712 "audioctl load-controls [ -d <device> ] <file>\n" \
707 713 " restore previously saved control settings to device\n" \
708 714 "\n" \
709 715 "audioctl help\n" \
710 716 " show this message.\n")
711 717
712 718 (void) fprintf(stderr, HELP_STR);
713 719 }
714 720
715 721 dev_t
716 722 device_devt(char *name)
717 723 {
718 724 struct stat sbuf;
719 725
720 726 if ((stat(name, &sbuf) != 0) ||
721 727 ((sbuf.st_mode & S_IFCHR) == 0)) {
722 728 /* Not a device node! */
723 729 return (0);
724 730 }
725 731
726 732 return (makedev(major(sbuf.st_rdev),
727 733 minor(sbuf.st_rdev) & ~(AUDIO_MN_TYPE_MASK)));
728 734 }
729 735
730 736 static device_t *
731 737 find_device(char *name)
732 738 {
733 739 dev_t devt;
734 740 device_t *d;
735 741
736 742 /*
737 743 * User may have specified:
738 744 *
739 745 * /dev/dsp[<num>]
740 746 * /dev/mixer[<num>]
741 747 * /dev/audio[<num>9]
742 748 * /dev/audioctl[<num>]
743 749 * /dev/sound/<num>{,ctl,dsp,mixer}
744 750 * /dev/sound/<driver>:<num>{,ctl,dsp,mixer}
745 751 *
746 752 * We can canonicalize these by looking at the dev_t though.
747 753 */
748 754
749 755 if (load_devices() != 0) {
750 756 return (NULL);
751 757 }
752 758
753 759 if (name == NULL)
754 760 name = getenv("AUDIODEV");
755 761
756 762 if ((name == NULL) ||
757 763 (strcmp(name, "/dev/mixer") == 0)) {
758 764 /* /dev/mixer node doesn't point to real hw */
759 765 name = "/dev/dsp";
760 766 }
761 767
762 768 if (*name == '/') {
763 769 /* if we have a full path, convert to the devt */
764 770 if ((devt = device_devt(name)) == 0) {
765 771 warn(_("No such audio device.\n"));
766 772 return (NULL);
767 773 }
768 774 name = NULL;
769 775 }
770 776
771 777 for (d = devices; d != NULL; d = d->nextp) {
772 778 oss_card_info *card = &d->card;
773 779
774 780 if ((name) && (strcmp(name, card->shortname) == 0)) {
775 781 return (d);
776 782 }
777 783 if (devt == d->devt) {
778 784 return (d);
779 785 }
780 786 }
781 787
782 788 warn(_("No such audio device.\n"));
783 789 return (NULL);
784 790 }
785 791
786 792 int
787 793 do_list_devices(int argc, char **argv)
788 794 {
789 795 int optc;
790 796 int verbose = 0;
791 797 device_t *d;
792 798
793 799 while ((optc = getopt(argc, argv, "v")) != EOF) {
794 800 switch (optc) {
795 801 case 'v':
796 802 verbose++;
797 803 break;
798 804 default:
799 805 help();
800 806 return (-1);
801 807 }
802 808 }
803 809 argc -= optind;
804 810 argv += optind;
805 811 if (argc != 0) {
806 812 help();
807 813 return (-1);
808 814 }
809 815
810 816 if (load_devices() != 0) {
811 817 return (-1);
812 818 }
813 819
814 820 for (d = devices; d != NULL; d = d->nextp) {
815 821
816 822 if ((d->mixer.enabled == 0) && (!verbose))
817 823 continue;
818 824
819 825 if (verbose) {
820 826 msg(_("%s (%s)\n"), d->card.shortname,
821 827 d->mixer.devnode);
822 828 } else {
823 829 msg(_("%s\n"), d->card.shortname);
824 830 }
↓ open down ↓ |
312 lines elided |
↑ open up ↑ |
825 831 }
826 832
827 833 return (0);
828 834 }
829 835
830 836 int
831 837 do_show_device(int argc, char **argv)
832 838 {
833 839 int optc;
834 840 char *devname = NULL;
841 + char *p, *n;
842 + const char *m;
835 843 device_t *d;
836 844
837 845 while ((optc = getopt(argc, argv, "d:v")) != EOF) {
838 846 switch (optc) {
839 847 case 'd':
840 848 devname = optarg;
841 849 break;
842 850 case 'v':
843 851 break;
844 852 default:
845 853 help();
846 854 return (-1);
847 855 }
848 856 }
849 857 argc -= optind;
850 858 argv += optind;
851 859 if (argc != 0) {
852 860 help();
853 861 return (-1);
↓ open down ↓ |
9 lines elided |
↑ open up ↑ |
854 862 }
855 863
856 864 if ((d = find_device(devname)) == NULL) {
857 865 return (ENODEV);
858 866 }
859 867
860 868 msg(_("Device: %s\n"), d->mixer.devnode);
861 869 msg(_(" Name = %s\n"), d->card.shortname);
862 870 msg(_(" Config = %s\n"), d->card.longname);
863 871
864 - if (strlen(d->card.hw_info)) {
865 - msg(_(" HW Info = %s"), d->card.hw_info);
872 + for (m = " HW Info = %s\n", p = d->card.hw_info;
873 + p != NULL && strlen(p) != 0;
874 + m = "\t %s\n", p = n) {
875 + if ((n = strchr(p, '\n')) != NULL)
876 + *n++ = '\0';
877 + msg(_(m), p);
866 878 }
867 879
868 880 return (0);
869 881 }
870 882
871 883 int
872 884 do_show_control(int argc, char **argv)
873 885 {
874 886 int optc;
875 887 int rval = 0;
876 888 int verbose = 0;
877 889 device_t *d;
878 890 char *devname = NULL;
879 891 int i;
880 892 int j;
881 893 int rv;
882 894 char *n;
883 895 cinfo_t *cinfop;
884 896
885 897 while ((optc = getopt(argc, argv, "d:v")) != EOF) {
886 898 switch (optc) {
887 899 case 'd':
888 900 devname = optarg;
889 901 break;
890 902 case 'v':
891 903 verbose++;
892 904 break;
893 905 default:
894 906 help();
895 907 return (-1);
896 908 }
897 909 }
898 910 argc -= optind;
899 911 argv += optind;
900 912
901 913 if ((d = find_device(devname)) == NULL) {
902 914 return (ENODEV);
903 915 }
904 916
905 917 print_header(NULL, verbose);
906 918 if (argc == 0) {
907 919 /* do them all! */
908 920 for (i = 0; i < d->cmax; i++) {
909 921
910 922 cinfop = &d->controls[i];
911 923 rv = print_control(NULL, d, cinfop, verbose);
912 924 rval = rval ? rval : rv;
913 925 }
914 926 return (rval);
915 927 }
916 928
917 929 for (i = 0; i < argc; i++) {
918 930 for (j = 0; j < d->cmax; j++) {
919 931 cinfop = &d->controls[j];
920 932 n = strrchr(cinfop->ci.extname, '_');
921 933 n = n ? n + 1 : cinfop->ci.extname;
922 934 if (strcmp(argv[i], n) == 0) {
923 935 rv = print_control(NULL, d, cinfop, verbose);
924 936 rval = rval ? rval : rv;
925 937 break;
926 938 }
927 939 }
928 940 /* Didn't find requested control */
929 941 if (j == d->cmax) {
930 942 warn(_("No such control: %s\n"), argv[i]);
931 943 rval = rval ? rval : ENODEV;
932 944 }
933 945 }
934 946
935 947 return (rval);
936 948 }
937 949
938 950 int
939 951 do_set_control(int argc, char **argv)
940 952 {
941 953 int optc;
942 954 int rval = 0;
943 955 int verbose = 0;
944 956 device_t *d;
945 957 char *devname = NULL;
946 958 char *cname;
947 959 char *value;
948 960 int i;
949 961 int found;
950 962 int rv;
951 963 char *n;
952 964 cinfo_t *cinfop;
953 965
954 966 while ((optc = getopt(argc, argv, "d:v")) != EOF) {
955 967 switch (optc) {
956 968 case 'd':
957 969 devname = optarg;
958 970 break;
959 971 case 'v':
960 972 verbose = 1;
961 973 break;
962 974 default:
963 975 help();
964 976 return (-1);
965 977 }
966 978 }
967 979 argc -= optind;
968 980 argv += optind;
969 981
970 982 if (argc != 2) {
971 983 help();
972 984 return (-1);
973 985 }
974 986 cname = argv[0];
975 987 value = argv[1];
976 988
977 989 if ((d = find_device(devname)) == NULL) {
978 990 return (ENODEV);
979 991 }
980 992
981 993 for (i = 0, found = 0; i < d->cmax; i++) {
982 994 cinfop = &d->controls[i];
983 995 n = strrchr(cinfop->ci.extname, '_');
984 996 n = n ? n + 1 : cinfop->ci.extname;
985 997 if (strcmp(cname, n) != 0) {
986 998 continue;
987 999 }
988 1000 found = 1;
989 1001 rv = set_device_control(d, cinfop, value, verbose);
990 1002 rval = rval ? rval : rv;
991 1003 }
992 1004 if (!found) {
993 1005 warn(_("No such control: %s\n"), cname);
994 1006 }
995 1007
996 1008 return (rval);
997 1009 }
998 1010
999 1011 int
1000 1012 do_save_controls(int argc, char **argv)
1001 1013 {
1002 1014 int optc;
↓ open down ↓ |
127 lines elided |
↑ open up ↑ |
1003 1015 int rval = 0;
1004 1016 device_t *d;
1005 1017 char *devname = NULL;
1006 1018 char *fname;
1007 1019 int i;
1008 1020 int rv;
1009 1021 cinfo_t *cinfop;
1010 1022 FILE *fp;
1011 1023 int fd;
1012 1024 int mode;
1025 + char *p, *n;
1026 + const char *m;
1013 1027
1014 1028 mode = O_WRONLY | O_CREAT | O_EXCL;
1015 1029
1016 1030 while ((optc = getopt(argc, argv, "d:f")) != EOF) {
1017 1031 switch (optc) {
1018 1032 case 'd':
1019 1033 devname = optarg;
1020 1034 break;
1021 1035 case 'f':
1022 1036 mode &= ~O_EXCL;
1023 1037 mode |= O_TRUNC;
1024 1038 break;
1025 1039 default:
1026 1040 help();
1027 1041 return (-1);
1028 1042 }
1029 1043 }
1030 1044 argc -= optind;
1031 1045 argv += optind;
1032 1046
1033 1047 if (argc != 1) {
1034 1048 help();
1035 1049 return (-1);
1036 1050 }
1037 1051 fname = argv[0];
1038 1052
1039 1053 if ((d = find_device(devname)) == NULL) {
1040 1054 return (ENODEV);
1041 1055 }
1042 1056
1043 1057 if ((fd = open(fname, mode, 0666)) < 0) {
1044 1058 perror(_("Failed to create file"));
1045 1059 return (errno);
1046 1060 }
1047 1061
1048 1062 if ((fp = fdopen(fd, "w")) == NULL) {
↓ open down ↓ |
26 lines elided |
↑ open up ↑ |
1049 1063 perror(_("Unable to open file\n"));
1050 1064 (void) close(fd);
1051 1065 (void) unlink(fname);
1052 1066 return (errno);
1053 1067 }
1054 1068
1055 1069 (void) fprintf(fp, "# Device: %s\n", d->mixer.devnode);
1056 1070 (void) fprintf(fp, "# Name = %s\n", d->card.shortname);
1057 1071 (void) fprintf(fp, "# Config = %s\n", d->card.longname);
1058 1072
1059 - if (strlen(d->card.hw_info)) {
1060 - (void) fprintf(fp, "# HW Info = %s", d->card.hw_info);
1073 + for (m = "# HW Info = %s\n", p = d->card.hw_info;
1074 + p != NULL && strlen(p) != 0;
1075 + m = "#\t %s\n", p = n) {
1076 + if ((n = strchr(p, '\n')) != NULL)
1077 + *n++ = '\0';
1078 + fprintf(fp, m, p);
1061 1079 }
1062 1080 (void) fprintf(fp, "#\n");
1063 1081
1064 1082 print_header(fp, 0);
1065 1083
1066 1084 for (i = 0; i < d->cmax; i++) {
1067 1085 cinfop = &d->controls[i];
1068 1086 rv = print_control(fp, d, cinfop, 0);
1069 1087 rval = rval ? rval : rv;
1070 1088 }
1071 1089
1072 1090 (void) fclose(fp);
1073 1091
1074 1092 return (rval);
1075 1093 }
1076 1094
1077 1095 int
1078 1096 do_load_controls(int argc, char **argv)
1079 1097 {
1080 1098 int optc;
1081 1099 int rval = 0;
1082 1100 device_t *d;
1083 1101 char *devname = NULL;
1084 1102 char *fname;
1085 1103 char *cname;
1086 1104 char *value;
1087 1105 int i;
1088 1106 int rv;
1089 1107 cinfo_t *cinfop;
1090 1108 FILE *fp;
1091 1109 char linebuf[MAXLINE];
1092 1110 int lineno = 0;
1093 1111 int found;
1094 1112
1095 1113 while ((optc = getopt(argc, argv, "d:")) != EOF) {
1096 1114 switch (optc) {
1097 1115 case 'd':
1098 1116 devname = optarg;
1099 1117 break;
1100 1118 default:
1101 1119 help();
1102 1120 return (-1);
1103 1121 }
1104 1122 }
1105 1123 argc -= optind;
1106 1124 argv += optind;
1107 1125
1108 1126 if (argc != 1) {
1109 1127 help();
1110 1128 return (-1);
1111 1129 }
1112 1130 fname = argv[0];
1113 1131
1114 1132 if ((d = find_device(devname)) == NULL) {
1115 1133 return (ENODEV);
1116 1134 }
1117 1135
1118 1136 if ((fp = fopen(fname, "r")) == NULL) {
1119 1137 perror(_("Unable to open file"));
1120 1138 return (errno);
1121 1139 }
1122 1140
1123 1141 while (fgets(linebuf, sizeof (linebuf), fp) != NULL) {
1124 1142 lineno++;
1125 1143 if (linebuf[strlen(linebuf) - 1] != '\n') {
1126 1144 warn(_("Warning: line too long at line %d\n"), lineno);
1127 1145 /* read in the rest of the line and discard it */
1128 1146 while (fgets(linebuf, sizeof (linebuf), fp) != NULL &&
1129 1147 (linebuf[strlen(linebuf) - 1] != '\n')) {
1130 1148 continue;
1131 1149 }
1132 1150 continue;
1133 1151 }
1134 1152
1135 1153 /* we have a good line ... */
1136 1154 cname = strtok(linebuf, " \t\n");
1137 1155 /* skip comments and blank lines */
1138 1156 if ((cname == NULL) || (cname[0] == '#')) {
1139 1157 continue;
1140 1158 }
1141 1159 value = strtok(NULL, " \t\n");
1142 1160 if ((value == NULL) || (*cname == 0)) {
1143 1161 warn(_("Warning: missing value at line %d\n"), lineno);
1144 1162 continue;
1145 1163 }
1146 1164
1147 1165 for (i = 0, found = 0; i < d->cmax; i++) {
1148 1166 /* save and restore requires an exact match */
1149 1167 cinfop = &d->controls[i];
1150 1168 if (strcmp(cinfop->ci.extname, cname) != 0) {
1151 1169 continue;
1152 1170 }
1153 1171 found = 1;
1154 1172 rv = set_device_control(d, cinfop, value, 0);
1155 1173 rval = rval ? rval : rv;
1156 1174 }
1157 1175 if (!found) {
1158 1176 warn(_("No such control: %s\n"), cname);
1159 1177 }
1160 1178 }
1161 1179 (void) fclose(fp);
1162 1180
1163 1181 return (rval);
1164 1182 }
1165 1183
1166 1184 int
1167 1185 mixer_walker(di_devlink_t dlink, void *arg)
1168 1186 {
1169 1187 const char *link;
1170 1188 int num;
1171 1189 int fd;
1172 1190 int verbose = *(int *)arg;
1173 1191 int num_offset;
1174 1192
1175 1193 num_offset = sizeof ("/dev/mixer") - 1;
1176 1194
1177 1195 link = di_devlink_path(dlink);
1178 1196
1179 1197 if ((link == NULL) ||
1180 1198 (strncmp(link, "/dev/mixer", num_offset) != 0) ||
1181 1199 (!isdigit(link[num_offset]))) {
1182 1200 return (DI_WALK_CONTINUE);
1183 1201 }
1184 1202
1185 1203 num = atoi(link + num_offset);
1186 1204 if ((fd = open(link, O_RDWR)) < 0) {
1187 1205 if (verbose) {
1188 1206 if (errno == ENOENT) {
1189 1207 msg(_("Device %s not present.\n"), link);
1190 1208 } else {
1191 1209 msg(_("Unable to open device %s: %s\n"),
1192 1210 link, strerror(errno));
1193 1211 }
1194 1212 }
1195 1213 return (DI_WALK_CONTINUE);
1196 1214 }
1197 1215
1198 1216 if (verbose) {
1199 1217 msg(_("Initializing link %s: "), link);
1200 1218 }
1201 1219 if (ioctl(fd, SNDCTL_SUN_SEND_NUMBER, &num) != 0) {
1202 1220 if (verbose) {
1203 1221 msg(_("failed: %s\n"), strerror(errno));
1204 1222 }
1205 1223 } else {
1206 1224 if (verbose) {
1207 1225 msg(_("done.\n"));
1208 1226 }
1209 1227 }
1210 1228 (void) close(fd);
1211 1229 return (DI_WALK_CONTINUE);
1212 1230 }
1213 1231
1214 1232 int
1215 1233 do_init_devices(int argc, char **argv)
1216 1234 {
1217 1235 int optc;
1218 1236 di_devlink_handle_t dlh;
1219 1237 int verbose = 0;
1220 1238
1221 1239 while ((optc = getopt(argc, argv, "v")) != EOF) {
1222 1240 switch (optc) {
1223 1241 case 'v':
1224 1242 verbose = 1;
1225 1243 break;
1226 1244 default:
1227 1245 help();
1228 1246 return (-1);
1229 1247 }
1230 1248 }
1231 1249 argc -= optind;
1232 1250 argv += optind;
1233 1251
1234 1252 if (argc != 0) {
1235 1253 help();
1236 1254 return (-1);
1237 1255 }
1238 1256
1239 1257 dlh = di_devlink_init(NULL, 0);
1240 1258 if (dlh == NULL) {
1241 1259 perror(_("Unable to initialize devlink handle"));
1242 1260 return (-1);
1243 1261 }
1244 1262
1245 1263 if (di_devlink_walk(dlh, "^mixer", NULL, 0, &verbose,
1246 1264 mixer_walker) != 0) {
1247 1265 perror(_("Unable to walk devlinks"));
1248 1266 return (-1);
1249 1267 }
1250 1268 return (0);
1251 1269 }
1252 1270
1253 1271 int
1254 1272 main(int argc, char **argv)
1255 1273 {
1256 1274 int rv = 0;
1257 1275 int opt;
1258 1276
1259 1277 (void) setlocale(LC_ALL, "");
1260 1278 (void) textdomain(TEXT_DOMAIN);
1261 1279
1262 1280 while ((opt = getopt(argc, argv, "h")) != EOF) {
1263 1281 switch (opt) {
1264 1282 case 'h':
1265 1283 help();
1266 1284 rv = 0;
1267 1285 goto OUT;
1268 1286 default:
1269 1287 rv = EINVAL;
1270 1288 break;
1271 1289 }
1272 1290 }
1273 1291
1274 1292 if (rv) {
1275 1293 goto OUT;
1276 1294 }
1277 1295
1278 1296 argc -= optind;
1279 1297 argv += optind;
1280 1298
1281 1299 if (argc < 1) {
1282 1300 help();
1283 1301 rv = EINVAL;
1284 1302 } else if (strcmp(argv[0], "help") == 0) {
1285 1303 help();
1286 1304 rv = 0;
1287 1305 } else if (strcmp(argv[0], "list-devices") == 0) {
1288 1306 rv = do_list_devices(argc, argv);
1289 1307 } else if (strcmp(argv[0], "show-device") == 0) {
1290 1308 rv = do_show_device(argc, argv);
1291 1309 } else if (strcmp(argv[0], "show-control") == 0) {
1292 1310 rv = do_show_control(argc, argv);
1293 1311 } else if (strcmp(argv[0], "set-control") == 0) {
1294 1312 rv = do_set_control(argc, argv);
1295 1313 } else if (strcmp(argv[0], "load-controls") == 0) {
1296 1314 rv = do_load_controls(argc, argv);
1297 1315 } else if (strcmp(argv[0], "save-controls") == 0) {
1298 1316 rv = do_save_controls(argc, argv);
1299 1317 } else if (strcmp(argv[0], "init-devices") == 0) {
1300 1318 rv = do_init_devices(argc, argv);
1301 1319 } else {
1302 1320 help();
1303 1321 rv = EINVAL;
1304 1322 }
1305 1323
1306 1324 OUT:
1307 1325 free_devices();
1308 1326 return (rv);
1309 1327 }
↓ open down ↓ |
239 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX