xref: /aosp_15_r20/external/tinyalsa_new/utils/tinymix.c (revision 02e95f1a335b55495d41ca67eaf42361f13704fa)
1*02e95f1aSMarcin Radomski /* tinymix.c
2*02e95f1aSMarcin Radomski **
3*02e95f1aSMarcin Radomski ** Copyright 2011, The Android Open Source Project
4*02e95f1aSMarcin Radomski **
5*02e95f1aSMarcin Radomski ** Redistribution and use in source and binary forms, with or without
6*02e95f1aSMarcin Radomski ** modification, are permitted provided that the following conditions are met:
7*02e95f1aSMarcin Radomski **     * Redistributions of source code must retain the above copyright
8*02e95f1aSMarcin Radomski **       notice, this list of conditions and the following disclaimer.
9*02e95f1aSMarcin Radomski **     * Redistributions in binary form must reproduce the above copyright
10*02e95f1aSMarcin Radomski **       notice, this list of conditions and the following disclaimer in the
11*02e95f1aSMarcin Radomski **       documentation and/or other materials provided with the distribution.
12*02e95f1aSMarcin Radomski **     * Neither the name of The Android Open Source Project nor the names of
13*02e95f1aSMarcin Radomski **       its contributors may be used to endorse or promote products derived
14*02e95f1aSMarcin Radomski **       from this software without specific prior written permission.
15*02e95f1aSMarcin Radomski **
16*02e95f1aSMarcin Radomski ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17*02e95f1aSMarcin Radomski ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*02e95f1aSMarcin Radomski ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*02e95f1aSMarcin Radomski ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20*02e95f1aSMarcin Radomski ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*02e95f1aSMarcin Radomski ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22*02e95f1aSMarcin Radomski ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23*02e95f1aSMarcin Radomski ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*02e95f1aSMarcin Radomski ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*02e95f1aSMarcin Radomski ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26*02e95f1aSMarcin Radomski ** DAMAGE.
27*02e95f1aSMarcin Radomski */
28*02e95f1aSMarcin Radomski 
29*02e95f1aSMarcin Radomski #include <tinyalsa/asoundlib.h>
30*02e95f1aSMarcin Radomski #include <errno.h>
31*02e95f1aSMarcin Radomski #include <stdio.h>
32*02e95f1aSMarcin Radomski #include <stdlib.h>
33*02e95f1aSMarcin Radomski #include <ctype.h>
34*02e95f1aSMarcin Radomski #include <string.h>
35*02e95f1aSMarcin Radomski #include <limits.h>
36*02e95f1aSMarcin Radomski 
37*02e95f1aSMarcin Radomski #define OPTPARSE_IMPLEMENTATION
38*02e95f1aSMarcin Radomski #include "optparse.h"
39*02e95f1aSMarcin Radomski 
40*02e95f1aSMarcin Radomski static void list_controls(struct mixer *mixer, int print_all);
41*02e95f1aSMarcin Radomski 
42*02e95f1aSMarcin Radomski static void print_control_values(struct mixer_ctl *control);
43*02e95f1aSMarcin Radomski static void print_control_values_by_name_or_id(struct mixer *mixer, const char *name_or_id);
44*02e95f1aSMarcin Radomski 
45*02e95f1aSMarcin Radomski static int set_values(struct mixer *mixer, const char *control,
46*02e95f1aSMarcin Radomski                              char **values, unsigned int num_values);
47*02e95f1aSMarcin Radomski 
48*02e95f1aSMarcin Radomski static void print_enum(struct mixer_ctl *ctl);
49*02e95f1aSMarcin Radomski 
usage(void)50*02e95f1aSMarcin Radomski void usage(void)
51*02e95f1aSMarcin Radomski {
52*02e95f1aSMarcin Radomski     printf("usage: tinymix [options] <command>\n");
53*02e95f1aSMarcin Radomski     printf("options:\n");
54*02e95f1aSMarcin Radomski     printf("\t-h, --help               : prints this help message and exits\n");
55*02e95f1aSMarcin Radomski     printf("\t-v, --version            : prints this version of tinymix and exits\n");
56*02e95f1aSMarcin Radomski     printf("\t-D, --card NUMBER        : specifies the card number of the mixer\n");
57*02e95f1aSMarcin Radomski     printf("\n");
58*02e95f1aSMarcin Radomski     printf("commands:\n");
59*02e95f1aSMarcin Radomski     printf("\tget NAME|ID              : prints the values of a control\n");
60*02e95f1aSMarcin Radomski     printf("\tset NAME|ID VALUE(S) ... : sets the value of a control\n");
61*02e95f1aSMarcin Radomski     printf("\t\tVALUE(S): integers, percents, and relative values\n");
62*02e95f1aSMarcin Radomski     printf("\t\t\tIntegers: 0, 100, -100 ...\n");
63*02e95f1aSMarcin Radomski     printf("\t\t\tPercents: 0%%, 100%% ...\n");
64*02e95f1aSMarcin Radomski     printf("\t\t\tRelative values: 1+, 1-, 1%%+, 2%%+ ...\n");
65*02e95f1aSMarcin Radomski     printf("\tcontrols                 : lists controls of the mixer\n");
66*02e95f1aSMarcin Radomski     printf("\tcontents                 : lists controls of the mixer and their contents\n");
67*02e95f1aSMarcin Radomski }
68*02e95f1aSMarcin Radomski 
version(void)69*02e95f1aSMarcin Radomski void version(void)
70*02e95f1aSMarcin Radomski {
71*02e95f1aSMarcin Radomski     printf("tinymix version 2.0 (tinyalsa version %s)\n", TINYALSA_VERSION_STRING);
72*02e95f1aSMarcin Radomski }
73*02e95f1aSMarcin Radomski 
is_command(char * arg)74*02e95f1aSMarcin Radomski static int is_command(char *arg) {
75*02e95f1aSMarcin Radomski     return strcmp(arg, "get") == 0 || strcmp(arg, "set") == 0 ||
76*02e95f1aSMarcin Radomski             strcmp(arg, "controls") == 0 || strcmp(arg, "contents") == 0;
77*02e95f1aSMarcin Radomski }
78*02e95f1aSMarcin Radomski 
find_command_position(int argc,char ** argv)79*02e95f1aSMarcin Radomski static int find_command_position(int argc, char **argv)
80*02e95f1aSMarcin Radomski {
81*02e95f1aSMarcin Radomski     for (int i = 0; i < argc; ++i) {
82*02e95f1aSMarcin Radomski         if (is_command(argv[i])) {
83*02e95f1aSMarcin Radomski             return i;
84*02e95f1aSMarcin Radomski         }
85*02e95f1aSMarcin Radomski     }
86*02e95f1aSMarcin Radomski     return -1;
87*02e95f1aSMarcin Radomski }
88*02e95f1aSMarcin Radomski 
main(int argc,char ** argv)89*02e95f1aSMarcin Radomski int main(int argc, char **argv)
90*02e95f1aSMarcin Radomski {
91*02e95f1aSMarcin Radomski     int card = 0;
92*02e95f1aSMarcin Radomski     struct optparse opts;
93*02e95f1aSMarcin Radomski     static struct optparse_long long_options[] = {
94*02e95f1aSMarcin Radomski         { "card",    'D', OPTPARSE_REQUIRED },
95*02e95f1aSMarcin Radomski         { "version", 'v', OPTPARSE_NONE     },
96*02e95f1aSMarcin Radomski         { "help",    'h', OPTPARSE_NONE     },
97*02e95f1aSMarcin Radomski         { 0, 0, 0 }
98*02e95f1aSMarcin Radomski     };
99*02e95f1aSMarcin Radomski 
100*02e95f1aSMarcin Radomski     // optparse_long may change the order of the argv list. Duplicate one for parsing the options.
101*02e95f1aSMarcin Radomski     char **argv_options_list = (char **) calloc(argc + 1, sizeof(char *));
102*02e95f1aSMarcin Radomski     if (!argv_options_list) {
103*02e95f1aSMarcin Radomski         fprintf(stderr, "Failed to allocate options list\n");
104*02e95f1aSMarcin Radomski         return EXIT_FAILURE;
105*02e95f1aSMarcin Radomski     }
106*02e95f1aSMarcin Radomski 
107*02e95f1aSMarcin Radomski     for (int i = 0; i < argc; ++i) {
108*02e95f1aSMarcin Radomski         argv_options_list[i] = argv[i];
109*02e95f1aSMarcin Radomski     }
110*02e95f1aSMarcin Radomski 
111*02e95f1aSMarcin Radomski     optparse_init(&opts, argv_options_list);
112*02e95f1aSMarcin Radomski     /* Detect the end of the options. */
113*02e95f1aSMarcin Radomski     int c;
114*02e95f1aSMarcin Radomski     while ((c = optparse_long(&opts, long_options, NULL)) != -1) {
115*02e95f1aSMarcin Radomski         switch (c) {
116*02e95f1aSMarcin Radomski         case 'D':
117*02e95f1aSMarcin Radomski             card = atoi(opts.optarg);
118*02e95f1aSMarcin Radomski             break;
119*02e95f1aSMarcin Radomski         case 'h':
120*02e95f1aSMarcin Radomski             usage();
121*02e95f1aSMarcin Radomski             free(argv_options_list);
122*02e95f1aSMarcin Radomski             return EXIT_SUCCESS;
123*02e95f1aSMarcin Radomski         case 'v':
124*02e95f1aSMarcin Radomski             version();
125*02e95f1aSMarcin Radomski             free(argv_options_list);
126*02e95f1aSMarcin Radomski             return EXIT_SUCCESS;
127*02e95f1aSMarcin Radomski         case '?':
128*02e95f1aSMarcin Radomski         default:
129*02e95f1aSMarcin Radomski             break;
130*02e95f1aSMarcin Radomski         }
131*02e95f1aSMarcin Radomski     }
132*02e95f1aSMarcin Radomski     free(argv_options_list);
133*02e95f1aSMarcin Radomski 
134*02e95f1aSMarcin Radomski     struct mixer *mixer = mixer_open(card);
135*02e95f1aSMarcin Radomski     if (!mixer) {
136*02e95f1aSMarcin Radomski         fprintf(stderr, "Failed to open mixer\n");
137*02e95f1aSMarcin Radomski         return EXIT_FAILURE;
138*02e95f1aSMarcin Radomski     }
139*02e95f1aSMarcin Radomski 
140*02e95f1aSMarcin Radomski     int command_position = find_command_position(argc, argv);
141*02e95f1aSMarcin Radomski     if (command_position < 0) {
142*02e95f1aSMarcin Radomski         usage();
143*02e95f1aSMarcin Radomski         mixer_close(mixer);
144*02e95f1aSMarcin Radomski         return EXIT_FAILURE;
145*02e95f1aSMarcin Radomski     }
146*02e95f1aSMarcin Radomski 
147*02e95f1aSMarcin Radomski     char *cmd = argv[command_position];
148*02e95f1aSMarcin Radomski     if (strcmp(cmd, "get") == 0) {
149*02e95f1aSMarcin Radomski         if (command_position + 1 >= argc) {
150*02e95f1aSMarcin Radomski             fprintf(stderr, "no control specified\n");
151*02e95f1aSMarcin Radomski             mixer_close(mixer);
152*02e95f1aSMarcin Radomski             return EXIT_FAILURE;
153*02e95f1aSMarcin Radomski         }
154*02e95f1aSMarcin Radomski         print_control_values_by_name_or_id(mixer, argv[command_position + 1]);
155*02e95f1aSMarcin Radomski         printf("\n");
156*02e95f1aSMarcin Radomski     } else if (strcmp(cmd, "set") == 0) {
157*02e95f1aSMarcin Radomski         if (command_position + 1 >= argc) {
158*02e95f1aSMarcin Radomski             fprintf(stderr, "no control specified\n");
159*02e95f1aSMarcin Radomski             mixer_close(mixer);
160*02e95f1aSMarcin Radomski             return EXIT_FAILURE;
161*02e95f1aSMarcin Radomski         }
162*02e95f1aSMarcin Radomski         if (command_position + 2 >= argc) {
163*02e95f1aSMarcin Radomski             fprintf(stderr, "no value(s) specified\n");
164*02e95f1aSMarcin Radomski             mixer_close(mixer);
165*02e95f1aSMarcin Radomski             return EXIT_FAILURE;
166*02e95f1aSMarcin Radomski         }
167*02e95f1aSMarcin Radomski         int res = set_values(mixer,
168*02e95f1aSMarcin Radomski                              argv[command_position + 1],
169*02e95f1aSMarcin Radomski                              &argv[command_position + 2],
170*02e95f1aSMarcin Radomski                              argc - command_position - 2);
171*02e95f1aSMarcin Radomski         if (res != 0) {
172*02e95f1aSMarcin Radomski             mixer_close(mixer);
173*02e95f1aSMarcin Radomski             return EXIT_FAILURE;
174*02e95f1aSMarcin Radomski         }
175*02e95f1aSMarcin Radomski     } else if (strcmp(cmd, "controls") == 0) {
176*02e95f1aSMarcin Radomski         list_controls(mixer, 0);
177*02e95f1aSMarcin Radomski     } else if (strcmp(cmd, "contents") == 0) {
178*02e95f1aSMarcin Radomski         list_controls(mixer, 1);
179*02e95f1aSMarcin Radomski     } else {
180*02e95f1aSMarcin Radomski         fprintf(stderr, "unknown command '%s'\n", cmd);
181*02e95f1aSMarcin Radomski         usage();
182*02e95f1aSMarcin Radomski         mixer_close(mixer);
183*02e95f1aSMarcin Radomski         return EXIT_FAILURE;
184*02e95f1aSMarcin Radomski     }
185*02e95f1aSMarcin Radomski 
186*02e95f1aSMarcin Radomski     mixer_close(mixer);
187*02e95f1aSMarcin Radomski     return EXIT_SUCCESS;
188*02e95f1aSMarcin Radomski }
189*02e95f1aSMarcin Radomski 
isnumber(const char * str)190*02e95f1aSMarcin Radomski static int isnumber(const char *str) {
191*02e95f1aSMarcin Radomski     char *end;
192*02e95f1aSMarcin Radomski 
193*02e95f1aSMarcin Radomski     if (str == NULL || strlen(str) == 0)
194*02e95f1aSMarcin Radomski         return 0;
195*02e95f1aSMarcin Radomski 
196*02e95f1aSMarcin Radomski     strtol(str, &end, 0);
197*02e95f1aSMarcin Radomski     return strlen(end) == 0;
198*02e95f1aSMarcin Radomski }
199*02e95f1aSMarcin Radomski 
list_controls(struct mixer * mixer,int print_all)200*02e95f1aSMarcin Radomski static void list_controls(struct mixer *mixer, int print_all)
201*02e95f1aSMarcin Radomski {
202*02e95f1aSMarcin Radomski     struct mixer_ctl *ctl;
203*02e95f1aSMarcin Radomski     const char *name, *type;
204*02e95f1aSMarcin Radomski     unsigned int num_ctls, num_values, device;
205*02e95f1aSMarcin Radomski     unsigned int i;
206*02e95f1aSMarcin Radomski 
207*02e95f1aSMarcin Radomski     num_ctls = mixer_get_num_ctls(mixer);
208*02e95f1aSMarcin Radomski 
209*02e95f1aSMarcin Radomski     printf("Number of controls: %u\n", num_ctls);
210*02e95f1aSMarcin Radomski 
211*02e95f1aSMarcin Radomski     if (print_all)
212*02e95f1aSMarcin Radomski         printf("ctl\ttype\tnum\t%-40s\tdevice\tvalue\n", "name");
213*02e95f1aSMarcin Radomski     else
214*02e95f1aSMarcin Radomski         printf("ctl\ttype\tnum\t%-40s\tdevice\n", "name");
215*02e95f1aSMarcin Radomski 
216*02e95f1aSMarcin Radomski     for (i = 0; i < num_ctls; i++) {
217*02e95f1aSMarcin Radomski         ctl = mixer_get_ctl(mixer, i);
218*02e95f1aSMarcin Radomski 
219*02e95f1aSMarcin Radomski         name = mixer_ctl_get_name(ctl);
220*02e95f1aSMarcin Radomski         type = mixer_ctl_get_type_string(ctl);
221*02e95f1aSMarcin Radomski         num_values = mixer_ctl_get_num_values(ctl);
222*02e95f1aSMarcin Radomski         device = mixer_ctl_get_device(ctl);
223*02e95f1aSMarcin Radomski         printf("%u\t%s\t%u\t%-40s\t%u", i, type, num_values, name, device);
224*02e95f1aSMarcin Radomski         if (print_all)
225*02e95f1aSMarcin Radomski             print_control_values(ctl);
226*02e95f1aSMarcin Radomski         printf("\n");
227*02e95f1aSMarcin Radomski     }
228*02e95f1aSMarcin Radomski }
229*02e95f1aSMarcin Radomski 
print_enum(struct mixer_ctl * ctl)230*02e95f1aSMarcin Radomski static void print_enum(struct mixer_ctl *ctl)
231*02e95f1aSMarcin Radomski {
232*02e95f1aSMarcin Radomski     unsigned int num_enums;
233*02e95f1aSMarcin Radomski     unsigned int i;
234*02e95f1aSMarcin Radomski     unsigned int value;
235*02e95f1aSMarcin Radomski     const char *string;
236*02e95f1aSMarcin Radomski 
237*02e95f1aSMarcin Radomski     num_enums = mixer_ctl_get_num_enums(ctl);
238*02e95f1aSMarcin Radomski     value = mixer_ctl_get_value(ctl, 0);
239*02e95f1aSMarcin Radomski 
240*02e95f1aSMarcin Radomski     for (i = 0; i < num_enums; i++) {
241*02e95f1aSMarcin Radomski         string = mixer_ctl_get_enum_string(ctl, i);
242*02e95f1aSMarcin Radomski         printf("%s%s, ", value == i ? "> " : "", string);
243*02e95f1aSMarcin Radomski     }
244*02e95f1aSMarcin Radomski }
245*02e95f1aSMarcin Radomski 
print_control_values_by_name_or_id(struct mixer * mixer,const char * name_or_id)246*02e95f1aSMarcin Radomski static void print_control_values_by_name_or_id(struct mixer *mixer, const char *name_or_id)
247*02e95f1aSMarcin Radomski {
248*02e95f1aSMarcin Radomski     struct mixer_ctl *ctl;
249*02e95f1aSMarcin Radomski 
250*02e95f1aSMarcin Radomski     if (isnumber(name_or_id))
251*02e95f1aSMarcin Radomski         ctl = mixer_get_ctl(mixer, atoi(name_or_id));
252*02e95f1aSMarcin Radomski     else
253*02e95f1aSMarcin Radomski         ctl = mixer_get_ctl_by_name(mixer, name_or_id);
254*02e95f1aSMarcin Radomski 
255*02e95f1aSMarcin Radomski     if (!ctl) {
256*02e95f1aSMarcin Radomski         fprintf(stderr, "Invalid mixer control\n");
257*02e95f1aSMarcin Radomski         return;
258*02e95f1aSMarcin Radomski     }
259*02e95f1aSMarcin Radomski 
260*02e95f1aSMarcin Radomski     print_control_values(ctl);
261*02e95f1aSMarcin Radomski }
262*02e95f1aSMarcin Radomski 
print_control_values(struct mixer_ctl * control)263*02e95f1aSMarcin Radomski static void print_control_values(struct mixer_ctl *control)
264*02e95f1aSMarcin Radomski {
265*02e95f1aSMarcin Radomski     enum mixer_ctl_type type;
266*02e95f1aSMarcin Radomski     unsigned int num_values;
267*02e95f1aSMarcin Radomski     unsigned int i;
268*02e95f1aSMarcin Radomski     int min, max;
269*02e95f1aSMarcin Radomski     int ret;
270*02e95f1aSMarcin Radomski     char *buf = NULL;
271*02e95f1aSMarcin Radomski 
272*02e95f1aSMarcin Radomski     type = mixer_ctl_get_type(control);
273*02e95f1aSMarcin Radomski     num_values = mixer_ctl_get_num_values(control);
274*02e95f1aSMarcin Radomski 
275*02e95f1aSMarcin Radomski     if ((type == MIXER_CTL_TYPE_BYTE) && (num_values > 0)) {
276*02e95f1aSMarcin Radomski         buf = calloc(1, num_values);
277*02e95f1aSMarcin Radomski         if (buf == NULL) {
278*02e95f1aSMarcin Radomski             fprintf(stderr, "Failed to alloc mem for bytes %u\n", num_values);
279*02e95f1aSMarcin Radomski             return;
280*02e95f1aSMarcin Radomski         }
281*02e95f1aSMarcin Radomski 
282*02e95f1aSMarcin Radomski         ret = mixer_ctl_get_array(control, buf, num_values);
283*02e95f1aSMarcin Radomski         if (ret < 0) {
284*02e95f1aSMarcin Radomski             fprintf(stderr, "Failed to mixer_ctl_get_array\n");
285*02e95f1aSMarcin Radomski             free(buf);
286*02e95f1aSMarcin Radomski             return;
287*02e95f1aSMarcin Radomski         }
288*02e95f1aSMarcin Radomski     }
289*02e95f1aSMarcin Radomski 
290*02e95f1aSMarcin Radomski     for (i = 0; i < num_values; i++) {
291*02e95f1aSMarcin Radomski         switch (type)
292*02e95f1aSMarcin Radomski         {
293*02e95f1aSMarcin Radomski         case MIXER_CTL_TYPE_INT:
294*02e95f1aSMarcin Radomski             printf("%d", mixer_ctl_get_value(control, i));
295*02e95f1aSMarcin Radomski             break;
296*02e95f1aSMarcin Radomski         case MIXER_CTL_TYPE_BOOL:
297*02e95f1aSMarcin Radomski             printf("%s", mixer_ctl_get_value(control, i) ? "On" : "Off");
298*02e95f1aSMarcin Radomski             break;
299*02e95f1aSMarcin Radomski         case MIXER_CTL_TYPE_ENUM:
300*02e95f1aSMarcin Radomski             print_enum(control);
301*02e95f1aSMarcin Radomski             break;
302*02e95f1aSMarcin Radomski         case MIXER_CTL_TYPE_BYTE:
303*02e95f1aSMarcin Radomski             printf("%02hhx", buf[i]);
304*02e95f1aSMarcin Radomski             break;
305*02e95f1aSMarcin Radomski         default:
306*02e95f1aSMarcin Radomski             printf("unknown");
307*02e95f1aSMarcin Radomski             break;
308*02e95f1aSMarcin Radomski         };
309*02e95f1aSMarcin Radomski         if ((i + 1) < num_values) {
310*02e95f1aSMarcin Radomski            printf(", ");
311*02e95f1aSMarcin Radomski         }
312*02e95f1aSMarcin Radomski     }
313*02e95f1aSMarcin Radomski 
314*02e95f1aSMarcin Radomski     if (type == MIXER_CTL_TYPE_INT) {
315*02e95f1aSMarcin Radomski         min = mixer_ctl_get_range_min(control);
316*02e95f1aSMarcin Radomski         max = mixer_ctl_get_range_max(control);
317*02e95f1aSMarcin Radomski         printf(" (range %d->%d)", min, max);
318*02e95f1aSMarcin Radomski     }
319*02e95f1aSMarcin Radomski 
320*02e95f1aSMarcin Radomski     free(buf);
321*02e95f1aSMarcin Radomski }
322*02e95f1aSMarcin Radomski 
tinymix_set_byte_ctl(struct mixer_ctl * ctl,char ** values,unsigned int num_values)323*02e95f1aSMarcin Radomski static void tinymix_set_byte_ctl(struct mixer_ctl *ctl,
324*02e95f1aSMarcin Radomski                                  char **values, unsigned int num_values)
325*02e95f1aSMarcin Radomski {
326*02e95f1aSMarcin Radomski     int ret;
327*02e95f1aSMarcin Radomski     char *buf;
328*02e95f1aSMarcin Radomski     char *end;
329*02e95f1aSMarcin Radomski     unsigned int i;
330*02e95f1aSMarcin Radomski     long n;
331*02e95f1aSMarcin Radomski 
332*02e95f1aSMarcin Radomski     buf = calloc(1, num_values);
333*02e95f1aSMarcin Radomski     if (buf == NULL) {
334*02e95f1aSMarcin Radomski         fprintf(stderr, "set_byte_ctl: Failed to alloc mem for bytes %u\n", num_values);
335*02e95f1aSMarcin Radomski         exit(EXIT_FAILURE);
336*02e95f1aSMarcin Radomski     }
337*02e95f1aSMarcin Radomski 
338*02e95f1aSMarcin Radomski     for (i = 0; i < num_values; i++) {
339*02e95f1aSMarcin Radomski         errno = 0;
340*02e95f1aSMarcin Radomski         n = strtol(values[i], &end, 0);
341*02e95f1aSMarcin Radomski         if (*end) {
342*02e95f1aSMarcin Radomski             fprintf(stderr, "%s not an integer\n", values[i]);
343*02e95f1aSMarcin Radomski             goto fail;
344*02e95f1aSMarcin Radomski         }
345*02e95f1aSMarcin Radomski         if (errno) {
346*02e95f1aSMarcin Radomski             fprintf(stderr, "strtol: %s: %s\n", values[i],
347*02e95f1aSMarcin Radomski                 strerror(errno));
348*02e95f1aSMarcin Radomski             goto fail;
349*02e95f1aSMarcin Radomski         }
350*02e95f1aSMarcin Radomski         if (n < 0 || n > 0xff) {
351*02e95f1aSMarcin Radomski             fprintf(stderr, "%s should be between [0, 0xff]\n",
352*02e95f1aSMarcin Radomski                 values[i]);
353*02e95f1aSMarcin Radomski             goto fail;
354*02e95f1aSMarcin Radomski         }
355*02e95f1aSMarcin Radomski         buf[i] = n;
356*02e95f1aSMarcin Radomski     }
357*02e95f1aSMarcin Radomski 
358*02e95f1aSMarcin Radomski     ret = mixer_ctl_set_array(ctl, buf, num_values);
359*02e95f1aSMarcin Radomski     if (ret < 0) {
360*02e95f1aSMarcin Radomski         fprintf(stderr, "Failed to set binary control\n");
361*02e95f1aSMarcin Radomski         goto fail;
362*02e95f1aSMarcin Radomski     }
363*02e95f1aSMarcin Radomski 
364*02e95f1aSMarcin Radomski     free(buf);
365*02e95f1aSMarcin Radomski     return;
366*02e95f1aSMarcin Radomski 
367*02e95f1aSMarcin Radomski fail:
368*02e95f1aSMarcin Radomski     free(buf);
369*02e95f1aSMarcin Radomski     exit(EXIT_FAILURE);
370*02e95f1aSMarcin Radomski }
371*02e95f1aSMarcin Radomski 
is_int(const char * value)372*02e95f1aSMarcin Radomski static int is_int(const char *value)
373*02e95f1aSMarcin Radomski {
374*02e95f1aSMarcin Radomski     return value[0] >= '0' && value[0] <= '9';
375*02e95f1aSMarcin Radomski }
376*02e95f1aSMarcin Radomski 
377*02e95f1aSMarcin Radomski struct parsed_int
378*02e95f1aSMarcin Radomski {
379*02e95f1aSMarcin Radomski     /** Wether or not the integer was valid. */
380*02e95f1aSMarcin Radomski     int valid;
381*02e95f1aSMarcin Radomski     /** The value of the parsed integer. */
382*02e95f1aSMarcin Radomski     int value;
383*02e95f1aSMarcin Radomski     /** The number of characters that were parsed. */
384*02e95f1aSMarcin Radomski     unsigned int length;
385*02e95f1aSMarcin Radomski     /** The number of characters remaining in the string. */
386*02e95f1aSMarcin Radomski     unsigned int remaining_length;
387*02e95f1aSMarcin Radomski     /** The remaining characters (or suffix) of the integer. */
388*02e95f1aSMarcin Radomski     const char* remaining;
389*02e95f1aSMarcin Radomski };
390*02e95f1aSMarcin Radomski 
parse_int(const char * str)391*02e95f1aSMarcin Radomski static struct parsed_int parse_int(const char* str)
392*02e95f1aSMarcin Radomski {
393*02e95f1aSMarcin Radomski     struct parsed_int out = {
394*02e95f1aSMarcin Radomski         0 /* valid */,
395*02e95f1aSMarcin Radomski         0 /* value */,
396*02e95f1aSMarcin Radomski         0 /* length */,
397*02e95f1aSMarcin Radomski         0 /* remaining length */,
398*02e95f1aSMarcin Radomski         NULL /* remaining characters */
399*02e95f1aSMarcin Radomski     };
400*02e95f1aSMarcin Radomski 
401*02e95f1aSMarcin Radomski     size_t length = strlen(str);
402*02e95f1aSMarcin Radomski     size_t i = 0;
403*02e95f1aSMarcin Radomski     int negative = 0;
404*02e95f1aSMarcin Radomski 
405*02e95f1aSMarcin Radomski     if (i < length && str[i] == '-') {
406*02e95f1aSMarcin Radomski         negative = 1;
407*02e95f1aSMarcin Radomski         i++;
408*02e95f1aSMarcin Radomski     }
409*02e95f1aSMarcin Radomski 
410*02e95f1aSMarcin Radomski     while (i < length) {
411*02e95f1aSMarcin Radomski         char c = str[i++];
412*02e95f1aSMarcin Radomski 
413*02e95f1aSMarcin Radomski         if (c < '0' || c > '9') {
414*02e95f1aSMarcin Radomski             --i;
415*02e95f1aSMarcin Radomski             break;
416*02e95f1aSMarcin Radomski         }
417*02e95f1aSMarcin Radomski 
418*02e95f1aSMarcin Radomski         out.value *= 10;
419*02e95f1aSMarcin Radomski         out.value += c - '0';
420*02e95f1aSMarcin Radomski 
421*02e95f1aSMarcin Radomski         out.length++;
422*02e95f1aSMarcin Radomski     }
423*02e95f1aSMarcin Radomski 
424*02e95f1aSMarcin Radomski     if (negative) {
425*02e95f1aSMarcin Radomski         out.value *= -1;
426*02e95f1aSMarcin Radomski     }
427*02e95f1aSMarcin Radomski 
428*02e95f1aSMarcin Radomski     out.valid = out.length > 0;
429*02e95f1aSMarcin Radomski     out.remaining_length = length - out.length;
430*02e95f1aSMarcin Radomski     out.remaining = str + out.length;
431*02e95f1aSMarcin Radomski 
432*02e95f1aSMarcin Radomski     return out;
433*02e95f1aSMarcin Radomski }
434*02e95f1aSMarcin Radomski 
435*02e95f1aSMarcin Radomski struct control_value
436*02e95f1aSMarcin Radomski {
437*02e95f1aSMarcin Radomski     int value;
438*02e95f1aSMarcin Radomski     int is_percent;
439*02e95f1aSMarcin Radomski     int is_relative;
440*02e95f1aSMarcin Radomski };
441*02e95f1aSMarcin Radomski 
to_control_value(const char * value_string)442*02e95f1aSMarcin Radomski static struct control_value to_control_value(const char* value_string)
443*02e95f1aSMarcin Radomski {
444*02e95f1aSMarcin Radomski     struct parsed_int parsed_int = parse_int(value_string);
445*02e95f1aSMarcin Radomski 
446*02e95f1aSMarcin Radomski     struct control_value out = {
447*02e95f1aSMarcin Radomski         0 /* value */,
448*02e95f1aSMarcin Radomski         0 /* is percent */,
449*02e95f1aSMarcin Radomski         0 /* is relative */
450*02e95f1aSMarcin Radomski     };
451*02e95f1aSMarcin Radomski 
452*02e95f1aSMarcin Radomski     out.value = parsed_int.value;
453*02e95f1aSMarcin Radomski 
454*02e95f1aSMarcin Radomski     unsigned int i = 0;
455*02e95f1aSMarcin Radomski 
456*02e95f1aSMarcin Radomski     if (parsed_int.remaining[i] == '%') {
457*02e95f1aSMarcin Radomski         out.is_percent = 1;
458*02e95f1aSMarcin Radomski         i++;
459*02e95f1aSMarcin Radomski     }
460*02e95f1aSMarcin Radomski 
461*02e95f1aSMarcin Radomski     if (parsed_int.remaining[i] == '+') {
462*02e95f1aSMarcin Radomski         out.is_relative = 1;
463*02e95f1aSMarcin Radomski     } else if (parsed_int.remaining[i] == '-') {
464*02e95f1aSMarcin Radomski         out.is_relative = 1;
465*02e95f1aSMarcin Radomski         out.value *= -1;
466*02e95f1aSMarcin Radomski     }
467*02e95f1aSMarcin Radomski 
468*02e95f1aSMarcin Radomski     return out;
469*02e95f1aSMarcin Radomski }
470*02e95f1aSMarcin Radomski 
set_control_value(struct mixer_ctl * ctl,unsigned int i,const struct control_value * value)471*02e95f1aSMarcin Radomski static int set_control_value(struct mixer_ctl* ctl, unsigned int i,
472*02e95f1aSMarcin Radomski                              const struct control_value* value)
473*02e95f1aSMarcin Radomski {
474*02e95f1aSMarcin Radomski     int next_value = value->value;
475*02e95f1aSMarcin Radomski 
476*02e95f1aSMarcin Radomski     if (value->is_relative) {
477*02e95f1aSMarcin Radomski 
478*02e95f1aSMarcin Radomski         int prev_value = value->is_percent ? mixer_ctl_get_percent(ctl, i)
479*02e95f1aSMarcin Radomski                                            : mixer_ctl_get_value(ctl, i);
480*02e95f1aSMarcin Radomski 
481*02e95f1aSMarcin Radomski         if (prev_value < 0) {
482*02e95f1aSMarcin Radomski           return prev_value;
483*02e95f1aSMarcin Radomski         }
484*02e95f1aSMarcin Radomski 
485*02e95f1aSMarcin Radomski         next_value += prev_value;
486*02e95f1aSMarcin Radomski     }
487*02e95f1aSMarcin Radomski 
488*02e95f1aSMarcin Radomski     return value->is_percent ? mixer_ctl_set_percent(ctl, i, next_value)
489*02e95f1aSMarcin Radomski                              : mixer_ctl_set_value(ctl, i, next_value);
490*02e95f1aSMarcin Radomski }
491*02e95f1aSMarcin Radomski 
set_control_values(struct mixer_ctl * ctl,char ** values,unsigned int num_values)492*02e95f1aSMarcin Radomski static int set_control_values(struct mixer_ctl* ctl,
493*02e95f1aSMarcin Radomski                               char** values,
494*02e95f1aSMarcin Radomski                               unsigned int num_values)
495*02e95f1aSMarcin Radomski {
496*02e95f1aSMarcin Radomski     unsigned int num_ctl_values = mixer_ctl_get_num_values(ctl);
497*02e95f1aSMarcin Radomski 
498*02e95f1aSMarcin Radomski     if (num_values == 1) {
499*02e95f1aSMarcin Radomski 
500*02e95f1aSMarcin Radomski         /* Set all values the same */
501*02e95f1aSMarcin Radomski         struct control_value value = to_control_value(values[0]);
502*02e95f1aSMarcin Radomski 
503*02e95f1aSMarcin Radomski         for (unsigned int i = 0; i < num_values; i++) {
504*02e95f1aSMarcin Radomski             int res = set_control_value(ctl, i, &value);
505*02e95f1aSMarcin Radomski             if (res != 0) {
506*02e95f1aSMarcin Radomski                 fprintf(stderr, "Error: invalid value (%d%s%s)\n", value.value,
507*02e95f1aSMarcin Radomski                         value.is_relative ? "r" : "", value.is_percent ? "%" : "");
508*02e95f1aSMarcin Radomski                 return -1;
509*02e95f1aSMarcin Radomski             }
510*02e95f1aSMarcin Radomski         }
511*02e95f1aSMarcin Radomski 
512*02e95f1aSMarcin Radomski     } else {
513*02e95f1aSMarcin Radomski 
514*02e95f1aSMarcin Radomski         /* Set multiple values */
515*02e95f1aSMarcin Radomski         if (num_values > num_ctl_values) {
516*02e95f1aSMarcin Radomski             fprintf(stderr,
517*02e95f1aSMarcin Radomski                     "Error: %u values given, but control only takes %u\n",
518*02e95f1aSMarcin Radomski                     num_values, num_ctl_values);
519*02e95f1aSMarcin Radomski             return -1;
520*02e95f1aSMarcin Radomski         }
521*02e95f1aSMarcin Radomski 
522*02e95f1aSMarcin Radomski         for (unsigned int i = 0; i < num_values; i++) {
523*02e95f1aSMarcin Radomski 
524*02e95f1aSMarcin Radomski             struct control_value v = to_control_value(values[i]);
525*02e95f1aSMarcin Radomski 
526*02e95f1aSMarcin Radomski             int res = set_control_value(ctl, i, &v);
527*02e95f1aSMarcin Radomski             if (res != 0) {
528*02e95f1aSMarcin Radomski                 fprintf(stderr, "Error: invalid value (%d%s%s) for index %u\n", v.value,
529*02e95f1aSMarcin Radomski                         v.is_relative ? "r" : "", v.is_percent ? "%" : "", i);
530*02e95f1aSMarcin Radomski                 return -1;
531*02e95f1aSMarcin Radomski             }
532*02e95f1aSMarcin Radomski         }
533*02e95f1aSMarcin Radomski     }
534*02e95f1aSMarcin Radomski 
535*02e95f1aSMarcin Radomski     return 0;
536*02e95f1aSMarcin Radomski }
537*02e95f1aSMarcin Radomski 
set_values(struct mixer * mixer,const char * control,char ** values,unsigned int num_values)538*02e95f1aSMarcin Radomski static int set_values(struct mixer *mixer, const char *control,
539*02e95f1aSMarcin Radomski                              char **values, unsigned int num_values)
540*02e95f1aSMarcin Radomski {
541*02e95f1aSMarcin Radomski     struct mixer_ctl *ctl;
542*02e95f1aSMarcin Radomski     enum mixer_ctl_type type;
543*02e95f1aSMarcin Radomski 
544*02e95f1aSMarcin Radomski     if (isnumber(control))
545*02e95f1aSMarcin Radomski         ctl = mixer_get_ctl(mixer, atoi(control));
546*02e95f1aSMarcin Radomski     else
547*02e95f1aSMarcin Radomski         ctl = mixer_get_ctl_by_name(mixer, control);
548*02e95f1aSMarcin Radomski 
549*02e95f1aSMarcin Radomski     if (!ctl) {
550*02e95f1aSMarcin Radomski         fprintf(stderr, "Invalid mixer control\n");
551*02e95f1aSMarcin Radomski         return -1;
552*02e95f1aSMarcin Radomski     }
553*02e95f1aSMarcin Radomski 
554*02e95f1aSMarcin Radomski     type = mixer_ctl_get_type(ctl);
555*02e95f1aSMarcin Radomski 
556*02e95f1aSMarcin Radomski     if (type == MIXER_CTL_TYPE_BYTE) {
557*02e95f1aSMarcin Radomski         tinymix_set_byte_ctl(ctl, values, num_values);
558*02e95f1aSMarcin Radomski         return 0;
559*02e95f1aSMarcin Radomski     }
560*02e95f1aSMarcin Radomski 
561*02e95f1aSMarcin Radomski     if (is_int(values[0])) {
562*02e95f1aSMarcin Radomski         set_control_values(ctl, values, num_values);
563*02e95f1aSMarcin Radomski     } else {
564*02e95f1aSMarcin Radomski         if (type == MIXER_CTL_TYPE_ENUM) {
565*02e95f1aSMarcin Radomski             if (num_values != 1) {
566*02e95f1aSMarcin Radomski                 fprintf(stderr, "Enclose strings in quotes and try again\n");
567*02e95f1aSMarcin Radomski                 return -1;
568*02e95f1aSMarcin Radomski             }
569*02e95f1aSMarcin Radomski             if (mixer_ctl_set_enum_by_string(ctl, values[0])) {
570*02e95f1aSMarcin Radomski                 fprintf(stderr, "Error: invalid enum value\n");
571*02e95f1aSMarcin Radomski                 return -1;
572*02e95f1aSMarcin Radomski             }
573*02e95f1aSMarcin Radomski         } else {
574*02e95f1aSMarcin Radomski             fprintf(stderr, "Error: only enum types can be set with strings\n");
575*02e95f1aSMarcin Radomski             return -1;
576*02e95f1aSMarcin Radomski         }
577*02e95f1aSMarcin Radomski     }
578*02e95f1aSMarcin Radomski 
579*02e95f1aSMarcin Radomski     return 0;
580*02e95f1aSMarcin Radomski }
581*02e95f1aSMarcin Radomski 
582