xref: /aosp_15_r20/external/tinyalsa/tinymix.c (revision d0c94b832dfb3062bf15d9baaf64123fc670b06c)
1*d0c94b83SXin Li /* tinymix.c
2*d0c94b83SXin Li **
3*d0c94b83SXin Li ** Copyright 2011, The Android Open Source Project
4*d0c94b83SXin Li **
5*d0c94b83SXin Li ** Redistribution and use in source and binary forms, with or without
6*d0c94b83SXin Li ** modification, are permitted provided that the following conditions are met:
7*d0c94b83SXin Li **     * Redistributions of source code must retain the above copyright
8*d0c94b83SXin Li **       notice, this list of conditions and the following disclaimer.
9*d0c94b83SXin Li **     * Redistributions in binary form must reproduce the above copyright
10*d0c94b83SXin Li **       notice, this list of conditions and the following disclaimer in the
11*d0c94b83SXin Li **       documentation and/or other materials provided with the distribution.
12*d0c94b83SXin Li **     * Neither the name of The Android Open Source Project nor the names of
13*d0c94b83SXin Li **       its contributors may be used to endorse or promote products derived
14*d0c94b83SXin Li **       from this software without specific prior written permission.
15*d0c94b83SXin Li **
16*d0c94b83SXin Li ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17*d0c94b83SXin Li ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*d0c94b83SXin Li ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*d0c94b83SXin Li ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20*d0c94b83SXin Li ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*d0c94b83SXin Li ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22*d0c94b83SXin Li ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23*d0c94b83SXin Li ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*d0c94b83SXin Li ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*d0c94b83SXin Li ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26*d0c94b83SXin Li ** DAMAGE.
27*d0c94b83SXin Li */
28*d0c94b83SXin Li 
29*d0c94b83SXin Li #include <tinyalsa/asoundlib.h>
30*d0c94b83SXin Li #include <errno.h>
31*d0c94b83SXin Li #include <stdio.h>
32*d0c94b83SXin Li #include <stdlib.h>
33*d0c94b83SXin Li #include <ctype.h>
34*d0c94b83SXin Li #include <string.h>
35*d0c94b83SXin Li #include <getopt.h>
36*d0c94b83SXin Li #include <errno.h>
37*d0c94b83SXin Li 
38*d0c94b83SXin Li static void tinymix_list_controls(struct mixer *mixer);
39*d0c94b83SXin Li static int tinymix_detail_control(struct mixer *mixer, const char *control,
40*d0c94b83SXin Li                                   int prefix, int print_all);
41*d0c94b83SXin Li static int tinymix_set_value(struct mixer *mixer, const char *control,
42*d0c94b83SXin Li                              char **values, unsigned int num_values);
43*d0c94b83SXin Li static void tinymix_print_enum(struct mixer_ctl *ctl, const char *space,
44*d0c94b83SXin Li                                int print_all);
45*d0c94b83SXin Li 
46*d0c94b83SXin Li static const char *tinymix_short_options = "D:atvh";
47*d0c94b83SXin Li static struct option tinymix_long_options[] = {
48*d0c94b83SXin Li     {"device",	   required_argument, 0, 'D'},
49*d0c94b83SXin Li     {"all-values", no_argument,       0, 'a'},
50*d0c94b83SXin Li     {"tabs-only",  no_argument,       0, 't'},
51*d0c94b83SXin Li     {"value-only", no_argument,       0, 'v'},
52*d0c94b83SXin Li     {"help",       no_argument,       0, 'h'},
53*d0c94b83SXin Li     {0,            0,                 0, 0}
54*d0c94b83SXin Li };
55*d0c94b83SXin Li 
56*d0c94b83SXin Li static int g_tabs_only = 0;
57*d0c94b83SXin Li static int g_all_values = 0;
58*d0c94b83SXin Li static int g_value_only = 0;
59*d0c94b83SXin Li 
usage(void)60*d0c94b83SXin Li static void usage (void) {
61*d0c94b83SXin Li     fprintf(stderr,
62*d0c94b83SXin Li "tinymix [options] [control name/#] [value to set]\n"
63*d0c94b83SXin Li "    options:\n"
64*d0c94b83SXin Li "    --device|-D <card#>   - use the given card # instead of 0.\n"
65*d0c94b83SXin Li "    --all-values|-a       - show all possible values/ranges for control.\n"
66*d0c94b83SXin Li "    --tabs-only|-t        - separate all output columns/values with tabs.\n"
67*d0c94b83SXin Li "    --value-only|-v       - show only the value for the selected control.\n"
68*d0c94b83SXin Li             );
69*d0c94b83SXin Li }
70*d0c94b83SXin Li 
main(int argc,char ** argv)71*d0c94b83SXin Li int main(int argc, char **argv)
72*d0c94b83SXin Li {
73*d0c94b83SXin Li     struct mixer *mixer;
74*d0c94b83SXin Li     int card = 0;
75*d0c94b83SXin Li     int ret = 0;
76*d0c94b83SXin Li 
77*d0c94b83SXin Li     while (1) {
78*d0c94b83SXin Li         int option_index = 0;
79*d0c94b83SXin Li         int option_char = 0;
80*d0c94b83SXin Li 
81*d0c94b83SXin Li         option_char = getopt_long(argc, argv, tinymix_short_options,
82*d0c94b83SXin Li                                   tinymix_long_options, &option_index);
83*d0c94b83SXin Li         if (option_char == -1)
84*d0c94b83SXin Li             break;
85*d0c94b83SXin Li 
86*d0c94b83SXin Li         switch (option_char) {
87*d0c94b83SXin Li         case 'D':
88*d0c94b83SXin Li             card = atoi(optarg);
89*d0c94b83SXin Li             break;
90*d0c94b83SXin Li         case 'a':
91*d0c94b83SXin Li             g_all_values = 1;
92*d0c94b83SXin Li             break;
93*d0c94b83SXin Li         case 't':
94*d0c94b83SXin Li             g_tabs_only = 1;
95*d0c94b83SXin Li             break;
96*d0c94b83SXin Li         case 'v':
97*d0c94b83SXin Li             g_value_only = 1;
98*d0c94b83SXin Li             break;
99*d0c94b83SXin Li         case 'h':
100*d0c94b83SXin Li             usage();
101*d0c94b83SXin Li             return 0;
102*d0c94b83SXin Li         default:
103*d0c94b83SXin Li             usage();
104*d0c94b83SXin Li             return EINVAL;
105*d0c94b83SXin Li         }
106*d0c94b83SXin Li     }
107*d0c94b83SXin Li 
108*d0c94b83SXin Li     mixer = mixer_open(card);
109*d0c94b83SXin Li     if (!mixer) {
110*d0c94b83SXin Li         fprintf(stderr, "Failed to open mixer\n");
111*d0c94b83SXin Li         return ENODEV;
112*d0c94b83SXin Li     }
113*d0c94b83SXin Li 
114*d0c94b83SXin Li     if (argc == optind) {
115*d0c94b83SXin Li         printf("Mixer name: '%s'\n", mixer_get_name(mixer));
116*d0c94b83SXin Li         tinymix_list_controls(mixer);
117*d0c94b83SXin Li     } else if (argc == optind + 1) {
118*d0c94b83SXin Li         ret = tinymix_detail_control(mixer, argv[optind], !g_value_only, !g_value_only);
119*d0c94b83SXin Li     } else if (argc >= optind + 2) {
120*d0c94b83SXin Li         ret = tinymix_set_value(mixer, argv[optind], &argv[optind + 1], argc - optind - 1);
121*d0c94b83SXin Li     }
122*d0c94b83SXin Li 
123*d0c94b83SXin Li     mixer_close(mixer);
124*d0c94b83SXin Li 
125*d0c94b83SXin Li     return ret;
126*d0c94b83SXin Li }
127*d0c94b83SXin Li 
isnumber(const char * str)128*d0c94b83SXin Li static int isnumber(const char *str) {
129*d0c94b83SXin Li     char *end;
130*d0c94b83SXin Li 
131*d0c94b83SXin Li     if (str == NULL || strlen(str) == 0)
132*d0c94b83SXin Li         return 0;
133*d0c94b83SXin Li 
134*d0c94b83SXin Li     strtol(str, &end, 0);
135*d0c94b83SXin Li     return strlen(end) == 0;
136*d0c94b83SXin Li }
137*d0c94b83SXin Li 
tinymix_list_controls(struct mixer * mixer)138*d0c94b83SXin Li static void tinymix_list_controls(struct mixer *mixer)
139*d0c94b83SXin Li {
140*d0c94b83SXin Li     struct mixer_ctl *ctl;
141*d0c94b83SXin Li     const char *name, *type;
142*d0c94b83SXin Li     unsigned int num_ctls, num_values;
143*d0c94b83SXin Li     unsigned int i;
144*d0c94b83SXin Li 
145*d0c94b83SXin Li     num_ctls = mixer_get_num_ctls(mixer);
146*d0c94b83SXin Li 
147*d0c94b83SXin Li     printf("Number of controls: %u\n", num_ctls);
148*d0c94b83SXin Li 
149*d0c94b83SXin Li     if (g_tabs_only)
150*d0c94b83SXin Li         printf("ctl\ttype\tnum\tname\tvalue");
151*d0c94b83SXin Li     else
152*d0c94b83SXin Li         printf("ctl\ttype\tnum\t%-40s value\n", "name");
153*d0c94b83SXin Li     if (g_all_values)
154*d0c94b83SXin Li         printf("\trange/values\n");
155*d0c94b83SXin Li     else
156*d0c94b83SXin Li         printf("\n");
157*d0c94b83SXin Li     for (i = 0; i < num_ctls; i++) {
158*d0c94b83SXin Li         ctl = mixer_get_ctl(mixer, i);
159*d0c94b83SXin Li 
160*d0c94b83SXin Li         name = mixer_ctl_get_name(ctl);
161*d0c94b83SXin Li         type = mixer_ctl_get_type_string(ctl);
162*d0c94b83SXin Li         num_values = mixer_ctl_get_num_values(ctl);
163*d0c94b83SXin Li         if (g_tabs_only)
164*d0c94b83SXin Li             printf("%d\t%s\t%d\t%s\t", i, type, num_values, name);
165*d0c94b83SXin Li         else
166*d0c94b83SXin Li             printf("%d\t%s\t%d\t%-40s ", i, type, num_values, name);
167*d0c94b83SXin Li         tinymix_detail_control(mixer, name, 0, g_all_values);
168*d0c94b83SXin Li     }
169*d0c94b83SXin Li }
170*d0c94b83SXin Li 
tinymix_print_enum(struct mixer_ctl * ctl,const char * space,int print_all)171*d0c94b83SXin Li static void tinymix_print_enum(struct mixer_ctl *ctl, const char *space,
172*d0c94b83SXin Li                                int print_all)
173*d0c94b83SXin Li {
174*d0c94b83SXin Li     unsigned int num_enums;
175*d0c94b83SXin Li     unsigned int i;
176*d0c94b83SXin Li     const char *string;
177*d0c94b83SXin Li     int control_value = mixer_ctl_get_value(ctl, 0);
178*d0c94b83SXin Li 
179*d0c94b83SXin Li     if (print_all) {
180*d0c94b83SXin Li         num_enums = mixer_ctl_get_num_enums(ctl);
181*d0c94b83SXin Li         for (i = 0; i < num_enums; i++) {
182*d0c94b83SXin Li             string = mixer_ctl_get_enum_string(ctl, i);
183*d0c94b83SXin Li             printf("%s%s%s",
184*d0c94b83SXin Li                    control_value == (int)i ? ">" : "", string,
185*d0c94b83SXin Li                    (i < num_enums - 1) ? space : "");
186*d0c94b83SXin Li         }
187*d0c94b83SXin Li     }
188*d0c94b83SXin Li     else {
189*d0c94b83SXin Li         string = mixer_ctl_get_enum_string(ctl, control_value);
190*d0c94b83SXin Li         printf("%s", string);
191*d0c94b83SXin Li     }
192*d0c94b83SXin Li }
193*d0c94b83SXin Li 
tinymix_detail_control(struct mixer * mixer,const char * control,int prefix,int print_all)194*d0c94b83SXin Li static int tinymix_detail_control(struct mixer *mixer, const char *control,
195*d0c94b83SXin Li                                   int prefix, int print_all)
196*d0c94b83SXin Li {
197*d0c94b83SXin Li     struct mixer_ctl *ctl;
198*d0c94b83SXin Li     enum mixer_ctl_type type;
199*d0c94b83SXin Li     unsigned int num_values;
200*d0c94b83SXin Li     unsigned int i;
201*d0c94b83SXin Li     int min, max;
202*d0c94b83SXin Li     int ret;
203*d0c94b83SXin Li     char *buf = NULL;
204*d0c94b83SXin Li     size_t len;
205*d0c94b83SXin Li     unsigned int tlv_header_size = 0;
206*d0c94b83SXin Li     const char *space = g_tabs_only ? "\t" : " ";
207*d0c94b83SXin Li 
208*d0c94b83SXin Li     if (isnumber(control))
209*d0c94b83SXin Li         ctl = mixer_get_ctl(mixer, atoi(control));
210*d0c94b83SXin Li     else
211*d0c94b83SXin Li         ctl = mixer_get_ctl_by_name(mixer, control);
212*d0c94b83SXin Li 
213*d0c94b83SXin Li     if (!ctl) {
214*d0c94b83SXin Li         fprintf(stderr, "Invalid mixer control: %s\n", control);
215*d0c94b83SXin Li         return ENOENT;
216*d0c94b83SXin Li     }
217*d0c94b83SXin Li 
218*d0c94b83SXin Li     type = mixer_ctl_get_type(ctl);
219*d0c94b83SXin Li     num_values = mixer_ctl_get_num_values(ctl);
220*d0c94b83SXin Li 
221*d0c94b83SXin Li     if (type == MIXER_CTL_TYPE_BYTE) {
222*d0c94b83SXin Li         if (mixer_ctl_is_access_tlv_rw(ctl)) {
223*d0c94b83SXin Li             tlv_header_size = TLV_HEADER_SIZE;
224*d0c94b83SXin Li         }
225*d0c94b83SXin Li         buf = calloc(1, num_values + tlv_header_size);
226*d0c94b83SXin Li         if (buf == NULL) {
227*d0c94b83SXin Li             fprintf(stderr, "Failed to alloc mem for bytes %d\n", num_values);
228*d0c94b83SXin Li             return ENOENT;
229*d0c94b83SXin Li         }
230*d0c94b83SXin Li 
231*d0c94b83SXin Li         len = num_values;
232*d0c94b83SXin Li         ret = mixer_ctl_get_array(ctl, buf, len + tlv_header_size);
233*d0c94b83SXin Li         if (ret < 0) {
234*d0c94b83SXin Li             fprintf(stderr, "Failed to mixer_ctl_get_array\n");
235*d0c94b83SXin Li             free(buf);
236*d0c94b83SXin Li             return ENOENT;
237*d0c94b83SXin Li         }
238*d0c94b83SXin Li     }
239*d0c94b83SXin Li 
240*d0c94b83SXin Li     if (prefix)
241*d0c94b83SXin Li         printf("%s:%s", mixer_ctl_get_name(ctl), space);
242*d0c94b83SXin Li 
243*d0c94b83SXin Li     for (i = 0; i < num_values; i++) {
244*d0c94b83SXin Li         switch (type)
245*d0c94b83SXin Li         {
246*d0c94b83SXin Li         case MIXER_CTL_TYPE_INT:
247*d0c94b83SXin Li             printf("%d", mixer_ctl_get_value(ctl, i));
248*d0c94b83SXin Li             break;
249*d0c94b83SXin Li         case MIXER_CTL_TYPE_BOOL:
250*d0c94b83SXin Li             printf("%s", mixer_ctl_get_value(ctl, i) ? "On" : "Off");
251*d0c94b83SXin Li             break;
252*d0c94b83SXin Li         case MIXER_CTL_TYPE_ENUM:
253*d0c94b83SXin Li             tinymix_print_enum(ctl, space, print_all);
254*d0c94b83SXin Li             break;
255*d0c94b83SXin Li         case MIXER_CTL_TYPE_BYTE:
256*d0c94b83SXin Li             /* skip printing TLV header if exists */
257*d0c94b83SXin Li             printf(" %02x", buf[i + tlv_header_size]);
258*d0c94b83SXin Li             break;
259*d0c94b83SXin Li         default:
260*d0c94b83SXin Li             printf("unknown");
261*d0c94b83SXin Li             break;
262*d0c94b83SXin Li         }
263*d0c94b83SXin Li 
264*d0c94b83SXin Li         if (i < num_values - 1)
265*d0c94b83SXin Li             printf("%s", space);
266*d0c94b83SXin Li     }
267*d0c94b83SXin Li 
268*d0c94b83SXin Li     if (print_all) {
269*d0c94b83SXin Li         if (type == MIXER_CTL_TYPE_INT) {
270*d0c94b83SXin Li             min = mixer_ctl_get_range_min(ctl);
271*d0c94b83SXin Li             max = mixer_ctl_get_range_max(ctl);
272*d0c94b83SXin Li             printf("%s(dsrange %d->%d)", space, min, max);
273*d0c94b83SXin Li         }
274*d0c94b83SXin Li     }
275*d0c94b83SXin Li 
276*d0c94b83SXin Li     free(buf);
277*d0c94b83SXin Li 
278*d0c94b83SXin Li     printf("\n");
279*d0c94b83SXin Li     return 0;
280*d0c94b83SXin Li }
281*d0c94b83SXin Li 
tinymix_set_byte_ctl(struct mixer_ctl * ctl,char ** values,unsigned int num_values)282*d0c94b83SXin Li static void tinymix_set_byte_ctl(struct mixer_ctl *ctl,
283*d0c94b83SXin Li     char **values, unsigned int num_values)
284*d0c94b83SXin Li {
285*d0c94b83SXin Li     int ret;
286*d0c94b83SXin Li     char *buf;
287*d0c94b83SXin Li     char *end;
288*d0c94b83SXin Li     unsigned int i;
289*d0c94b83SXin Li     long n;
290*d0c94b83SXin Li     unsigned int *tlv, tlv_size;
291*d0c94b83SXin Li     unsigned int tlv_header_size = 0;
292*d0c94b83SXin Li 
293*d0c94b83SXin Li     if (mixer_ctl_is_access_tlv_rw(ctl)) {
294*d0c94b83SXin Li         tlv_header_size = TLV_HEADER_SIZE;
295*d0c94b83SXin Li     }
296*d0c94b83SXin Li 
297*d0c94b83SXin Li     tlv_size = num_values + tlv_header_size;
298*d0c94b83SXin Li 
299*d0c94b83SXin Li     buf = calloc(1, tlv_size);
300*d0c94b83SXin Li     if (buf == NULL) {
301*d0c94b83SXin Li         fprintf(stderr, "set_byte_ctl: Failed to alloc mem for bytes %d\n", num_values);
302*d0c94b83SXin Li         exit(EXIT_FAILURE);
303*d0c94b83SXin Li     }
304*d0c94b83SXin Li 
305*d0c94b83SXin Li     tlv = (unsigned int *)buf;
306*d0c94b83SXin Li     tlv[0] = 0;
307*d0c94b83SXin Li     tlv[1] = num_values;
308*d0c94b83SXin Li 
309*d0c94b83SXin Li     for (i = 0; i < num_values; i++) {
310*d0c94b83SXin Li         errno = 0;
311*d0c94b83SXin Li         n = strtol(values[i], &end, 0);
312*d0c94b83SXin Li         if (*end) {
313*d0c94b83SXin Li             fprintf(stderr, "%s not an integer\n", values[i]);
314*d0c94b83SXin Li             goto fail;
315*d0c94b83SXin Li         }
316*d0c94b83SXin Li         if (errno) {
317*d0c94b83SXin Li             fprintf(stderr, "strtol: %s: %s\n", values[i],
318*d0c94b83SXin Li                 strerror(errno));
319*d0c94b83SXin Li             goto fail;
320*d0c94b83SXin Li         }
321*d0c94b83SXin Li         if (n < 0 || n > 0xff) {
322*d0c94b83SXin Li             fprintf(stderr, "%s should be between [0, 0xff]\n",
323*d0c94b83SXin Li                 values[i]);
324*d0c94b83SXin Li             goto fail;
325*d0c94b83SXin Li         }
326*d0c94b83SXin Li         /* start filling after the TLV header */
327*d0c94b83SXin Li         buf[i + tlv_header_size] = n;
328*d0c94b83SXin Li     }
329*d0c94b83SXin Li 
330*d0c94b83SXin Li     ret = mixer_ctl_set_array(ctl, buf, tlv_size);
331*d0c94b83SXin Li     if (ret < 0) {
332*d0c94b83SXin Li         fprintf(stderr, "Failed to set binary control\n");
333*d0c94b83SXin Li         goto fail;
334*d0c94b83SXin Li     }
335*d0c94b83SXin Li 
336*d0c94b83SXin Li     free(buf);
337*d0c94b83SXin Li     return;
338*d0c94b83SXin Li 
339*d0c94b83SXin Li fail:
340*d0c94b83SXin Li     free(buf);
341*d0c94b83SXin Li     exit(EXIT_FAILURE);
342*d0c94b83SXin Li }
343*d0c94b83SXin Li 
tinymix_set_value(struct mixer * mixer,const char * control,char ** values,unsigned int num_values)344*d0c94b83SXin Li static int tinymix_set_value(struct mixer *mixer, const char *control,
345*d0c94b83SXin Li                              char **values, unsigned int num_values)
346*d0c94b83SXin Li {
347*d0c94b83SXin Li     struct mixer_ctl *ctl;
348*d0c94b83SXin Li     enum mixer_ctl_type type;
349*d0c94b83SXin Li     unsigned int num_ctl_values;
350*d0c94b83SXin Li     unsigned int i;
351*d0c94b83SXin Li 
352*d0c94b83SXin Li     if (isnumber(control))
353*d0c94b83SXin Li         ctl = mixer_get_ctl(mixer, atoi(control));
354*d0c94b83SXin Li     else
355*d0c94b83SXin Li         ctl = mixer_get_ctl_by_name(mixer, control);
356*d0c94b83SXin Li 
357*d0c94b83SXin Li     if (!ctl) {
358*d0c94b83SXin Li         fprintf(stderr, "Invalid mixer control: %s\n", control);
359*d0c94b83SXin Li         return ENOENT;
360*d0c94b83SXin Li     }
361*d0c94b83SXin Li 
362*d0c94b83SXin Li     type = mixer_ctl_get_type(ctl);
363*d0c94b83SXin Li     num_ctl_values = mixer_ctl_get_num_values(ctl);
364*d0c94b83SXin Li 
365*d0c94b83SXin Li     if (type == MIXER_CTL_TYPE_BYTE) {
366*d0c94b83SXin Li         tinymix_set_byte_ctl(ctl, values, num_values);
367*d0c94b83SXin Li         return ENOENT;
368*d0c94b83SXin Li     }
369*d0c94b83SXin Li 
370*d0c94b83SXin Li     if (isnumber(values[0])) {
371*d0c94b83SXin Li         if (num_values == 1) {
372*d0c94b83SXin Li             /* Set all values the same */
373*d0c94b83SXin Li             int value = atoi(values[0]);
374*d0c94b83SXin Li 
375*d0c94b83SXin Li             for (i = 0; i < num_ctl_values; i++) {
376*d0c94b83SXin Li                 if (mixer_ctl_set_value(ctl, i, value)) {
377*d0c94b83SXin Li                     fprintf(stderr, "Error: invalid value\n");
378*d0c94b83SXin Li                     return EINVAL;
379*d0c94b83SXin Li                 }
380*d0c94b83SXin Li             }
381*d0c94b83SXin Li         } else {
382*d0c94b83SXin Li             /* Set multiple values */
383*d0c94b83SXin Li             if (num_values > num_ctl_values) {
384*d0c94b83SXin Li                 fprintf(stderr,
385*d0c94b83SXin Li                         "Error: %u values given, but control only takes %u\n",
386*d0c94b83SXin Li                         num_values, num_ctl_values);
387*d0c94b83SXin Li                 return EINVAL;
388*d0c94b83SXin Li             }
389*d0c94b83SXin Li             for (i = 0; i < num_values; i++) {
390*d0c94b83SXin Li                 if (mixer_ctl_set_value(ctl, i, atoi(values[i]))) {
391*d0c94b83SXin Li                     fprintf(stderr, "Error: invalid value for index %d\n", i);
392*d0c94b83SXin Li                     return EINVAL;
393*d0c94b83SXin Li                 }
394*d0c94b83SXin Li             }
395*d0c94b83SXin Li         }
396*d0c94b83SXin Li     } else {
397*d0c94b83SXin Li         if (type == MIXER_CTL_TYPE_ENUM) {
398*d0c94b83SXin Li             if (num_values != 1) {
399*d0c94b83SXin Li                 fprintf(stderr, "Enclose strings in quotes and try again\n");
400*d0c94b83SXin Li                 return EINVAL;
401*d0c94b83SXin Li             }
402*d0c94b83SXin Li             if (mixer_ctl_set_enum_by_string(ctl, values[0])) {
403*d0c94b83SXin Li                 fprintf(stderr, "Error: invalid enum value\n");
404*d0c94b83SXin Li                 return EINVAL;
405*d0c94b83SXin Li             }
406*d0c94b83SXin Li         } else {
407*d0c94b83SXin Li             fprintf(stderr, "Error: only enum types can be set with strings\n");
408*d0c94b83SXin Li             return EINVAL;
409*d0c94b83SXin Li         }
410*d0c94b83SXin Li     }
411*d0c94b83SXin Li 
412*d0c94b83SXin Li     return 0;
413*d0c94b83SXin Li }
414