1 /* $NetBSD: mixerctl.c,v 1.23 2009/04/12 14:17:25 lukem Exp $ */
4 * Copyright (c) 1997 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (augustss@NetBSD.org) and Chuck Cranor.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: mixerctl.c,v 1.23 2009/04/12 14:17:25 lukem Exp $");
43 #include <sys/types.h>
44 #include <sys/ioctl.h>
45 #include <sys/audioio.h>
57 mixer_devinfo_t
*infp
;
62 mixer_devinfo_t
*infos
;
65 catstr(char *p
, char *q
)
69 asprintf(&r
, "%s.%s", p
, q
);
79 for(i
= 0; fields
[i
].name
; i
++)
80 if (strcmp(fields
[i
].name
, name
) == 0)
86 prfield(struct field
*p
, const char *sep
, int prvalset
)
92 fprintf(out
, "%s%s", p
->name
, sep
);
95 case AUDIO_MIXER_ENUM
:
96 for(i
= 0; i
< p
->infp
->un
.e
.num_mem
; i
++)
97 if (p
->infp
->un
.e
.member
[i
].ord
== m
->un
.ord
)
99 p
->infp
->un
.e
.member
[i
].label
.name
);
102 for(i
= 0; i
< p
->infp
->un
.e
.num_mem
; i
++)
104 p
->infp
->un
.e
.member
[i
].label
.name
);
108 case AUDIO_MIXER_SET
:
109 for(n
= i
= 0; i
< p
->infp
->un
.s
.num_mem
; i
++)
110 if (m
->un
.mask
& p
->infp
->un
.s
.member
[i
].mask
)
111 fprintf(out
, "%s%s", n
++ ? "," : "",
112 p
->infp
->un
.s
.member
[i
].label
.name
);
115 for(i
= 0; i
< p
->infp
->un
.s
.num_mem
; i
++)
117 p
->infp
->un
.s
.member
[i
].label
.name
);
121 case AUDIO_MIXER_VALUE
:
122 if (m
->un
.value
.num_channels
== 1)
123 fprintf(out
, "%d", m
->un
.value
.level
[0]);
125 fprintf(out
, "%d,%d", m
->un
.value
.level
[0],
126 m
->un
.value
.level
[1]);
128 fprintf(out
, " %s", p
->infp
->un
.v
.units
.name
);
129 if (p
->infp
->un
.v
.delta
)
130 fprintf(out
, " delta=%d", p
->infp
->un
.v
.delta
);
135 errx(1, "Invalid format.");
140 rdfield(struct field
*p
, char *q
)
149 case AUDIO_MIXER_ENUM
:
150 for(i
= 0; i
< p
->infp
->un
.e
.num_mem
; i
++)
151 if (strcmp(p
->infp
->un
.e
.member
[i
].label
.name
, q
) == 0)
153 if (i
< p
->infp
->un
.e
.num_mem
)
154 m
->un
.ord
= p
->infp
->un
.e
.member
[i
].ord
;
156 warnx("Bad enum value %s", q
);
160 case AUDIO_MIXER_SET
:
162 for(v
= 0; q
&& *q
; q
= s
) {
166 for(i
= 0; i
< p
->infp
->un
.s
.num_mem
; i
++)
167 if (strcmp(p
->infp
->un
.s
.member
[i
].label
.name
,
170 if (i
< p
->infp
->un
.s
.num_mem
) {
171 mask
|= p
->infp
->un
.s
.member
[i
].mask
;
173 warnx("Bad set value %s", q
);
179 case AUDIO_MIXER_VALUE
:
180 if (m
->un
.value
.num_channels
== 1) {
181 if (sscanf(q
, "%d", &v
) == 1) {
182 m
->un
.value
.level
[0] = v
;
184 warnx("Bad number %s", q
);
188 if (sscanf(q
, "%d,%d", &v0
, &v1
) == 2) {
189 m
->un
.value
.level
[0] = v0
;
190 m
->un
.value
.level
[1] = v1
;
191 } else if (sscanf(q
, "%d", &v
) == 1) {
192 m
->un
.value
.level
[0] = m
->un
.value
.level
[1] = v
;
194 warnx("Bad numbers %s", q
);
200 errx(1, "Invalid format.");
207 incfield(struct field
*p
, int inc
)
214 case AUDIO_MIXER_ENUM
:
217 m
->un
.ord
= p
->infp
->un
.e
.num_mem
-1;
218 if (m
->un
.ord
>= p
->infp
->un
.e
.num_mem
)
221 case AUDIO_MIXER_SET
:
224 m
->un
.mask
= (1 << p
->infp
->un
.s
.num_mem
) - 1;
225 if (m
->un
.mask
>= (1 << p
->infp
->un
.s
.num_mem
))
227 warnx("Can't ++/-- %s", p
->name
);
229 case AUDIO_MIXER_VALUE
:
230 if (p
->infp
->un
.v
.delta
)
231 inc
*= p
->infp
->un
.v
.delta
;
232 for (i
= 0; i
< m
->un
.value
.num_channels
; i
++) {
233 v
= m
->un
.value
.level
[i
];
235 if (v
< AUDIO_MIN_GAIN
)
237 if (v
> AUDIO_MAX_GAIN
)
239 m
->un
.value
.level
[i
] = v
;
243 errx(1, "Invalid format.");
250 wrarg(int fd
, char *arg
, const char *sep
)
257 q
= strchr(arg
, '=');
261 if (l
> 2 && arg
[l
-2] == '+' && arg
[l
-1] == '+')
263 else if (l
> 2 && arg
[l
-2] == '-' && arg
[l
-1] == '-')
266 warnx("No `=' in %s", arg
);
270 } else if (q
> arg
&& (*(q
-1) == '+' || *(q
-1) == '-')) {
271 if (sscanf(q
+1, "%d", &incdec
) != 1) {
272 warnx("Bad number %s", q
+1);
284 warnx("field %s does not exist", arg
);
292 r
= incfield(p
, incdec
);
294 if (ioctl(fd
, AUDIO_MIXER_WRITE
, p
->valp
) < 0)
295 warn("AUDIO_MIXER_WRITE");
299 ioctl(fd
, AUDIO_MIXER_READ
, p
->valp
);
308 prarg(int fd
, char *arg
, const char *sep
)
314 warnx("field %s does not exist", arg
);
316 prfield(p
, sep
, vflag
), fprintf(out
, "\n");
320 main(int argc
, char **argv
)
322 int fd
, i
, j
, ch
, pos
;
323 int aflag
= 0, wflag
= 0;
325 const char *sep
= "=";
326 mixer_devinfo_t dinfo
;
329 file
= getenv("MIXERDEVICE");
335 while ((ch
= getopt(argc
, argv
, "ad:f:nvw")) != -1) {
349 case 'f': /* compatibility */
356 fprintf(out
, "%s [-d file] [-v] [-n] name ...\n", prog
);
357 fprintf(out
, "%s [-d file] [-v] [-n] -w name=value ...\n",prog
);
358 fprintf(out
, "%s [-d file] [-v] [-n] -a\n", prog
);
365 fd
= open(file
, O_RDWR
);
366 /* Try with mixer0. */
367 if (fd
< 0 && strcmp(file
, _PATH_MIXER
) == 0) {
369 fd
= open(file
, O_RDWR
);
375 for(ndev
= 0; ; ndev
++) {
377 if (ioctl(fd
, AUDIO_MIXER_DEVINFO
, &dinfo
) < 0)
380 rfields
= calloc(ndev
, sizeof *rfields
);
381 fields
= calloc(ndev
, sizeof *fields
);
382 infos
= calloc(ndev
, sizeof *infos
);
383 values
= calloc(ndev
, sizeof *values
);
385 for(i
= 0; i
< ndev
; i
++) {
387 ioctl(fd
, AUDIO_MIXER_DEVINFO
, &infos
[i
]);
390 for(i
= 0; i
< ndev
; i
++) {
391 rfields
[i
].name
= infos
[i
].label
.name
;
392 rfields
[i
].valp
= &values
[i
];
393 rfields
[i
].infp
= &infos
[i
];
396 for(i
= 0; i
< ndev
; i
++) {
398 values
[i
].type
= infos
[i
].type
;
399 if (infos
[i
].type
!= AUDIO_MIXER_CLASS
) {
400 values
[i
].un
.value
.num_channels
= 2;
401 if (ioctl(fd
, AUDIO_MIXER_READ
, &values
[i
]) < 0) {
402 values
[i
].un
.value
.num_channels
= 1;
403 if (ioctl(fd
, AUDIO_MIXER_READ
, &values
[i
]) < 0)
404 err(1, "AUDIO_MIXER_READ");
409 for(j
= i
= 0; i
< ndev
; i
++) {
410 if (infos
[i
].type
!= AUDIO_MIXER_CLASS
&&
411 infos
[i
].type
!= -1) {
412 fields
[j
++] = rfields
[i
];
413 for(pos
= infos
[i
].next
; pos
!= AUDIO_MIXER_LAST
;
414 pos
= infos
[pos
].next
) {
415 fields
[j
] = rfields
[pos
];
416 fields
[j
].name
= catstr(rfields
[i
].name
,
417 infos
[pos
].label
.name
);
418 infos
[pos
].type
= -1;
424 for(i
= 0; i
< j
; i
++) {
425 int cls
= fields
[i
].infp
->mixer_class
;
426 if (cls
>= 0 && cls
< ndev
)
427 fields
[i
].name
= catstr(infos
[cls
].label
.name
,
431 if (argc
== 0 && aflag
&& !wflag
) {
432 for(i
= 0; fields
[i
].name
; i
++) {
433 prfield(&fields
[i
], sep
, vflag
);
436 } else if (argc
> 0 && !aflag
) {
439 wrarg(fd
, *argv
, sep
);
441 prarg(fd
, *argv
, sep
);