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