aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/tinymix.c247
1 files changed, 154 insertions, 93 deletions
diff --git a/utils/tinymix.c b/utils/tinymix.c
index 41ca269..5c0378c 100644
--- a/utils/tinymix.c
+++ b/utils/tinymix.c
@@ -37,27 +37,33 @@
#define OPTPARSE_IMPLEMENTATION
#include "optparse.h"
-static void tinymix_list_controls(struct mixer *mixer, int print_all);
+static void list_controls(struct mixer *mixer, int print_all);
-static void tinymix_detail_control(struct mixer *mixer, const char *control);
+static void print_control_values(struct mixer_ctl *control);
+static void print_control_values_by_name_or_id(struct mixer *mixer, const char *name_or_id);
-static int tinymix_set_value(struct mixer *mixer, const char *control,
+static int set_values(struct mixer *mixer, const char *control,
char **values, unsigned int num_values);
-static void tinymix_print_enum(struct mixer_ctl *ctl);
+static void print_enum(struct mixer_ctl *ctl);
void usage(void)
{
printf("usage: tinymix [options] <command>\n");
printf("options:\n");
- printf("\t-h, --help : prints this help message and exits\n");
- printf("\t-v, --version : prints this version of tinymix and exits\n");
- printf("\t-D, --card NUMBER : specifies the card number of the mixer\n");
+ printf("\t-h, --help : prints this help message and exits\n");
+ printf("\t-v, --version : prints this version of tinymix and exits\n");
+ printf("\t-D, --card NUMBER : specifies the card number of the mixer\n");
+ printf("\n");
printf("commands:\n");
- printf("\tget NAME|ID : prints the values of a control\n");
- printf("\tset NAME|ID VALUE : sets the value of a control\n");
- printf("\tcontrols : lists controls of the mixer\n");
- printf("\tcontents : lists controls of the mixer and their contents\n");
+ printf("\tget NAME|ID : prints the values of a control\n");
+ printf("\tset NAME|ID VALUE(S) ... : sets the value of a control\n");
+ printf("\t\tVALUE(S): integers, percents, and relative values\n");
+ printf("\t\t\tIntegers: 0, 100, -100 ...\n");
+ printf("\t\t\tPercents: 0%%, 100%% ...\n");
+ printf("\t\t\tRelative values: 1+, 1-, 1%%+, 2%%+ ...\n");
+ printf("\tcontrols : lists controls of the mixer\n");
+ printf("\tcontents : lists controls of the mixer and their contents\n");
}
void version(void)
@@ -65,11 +71,24 @@ void version(void)
printf("tinymix version 2.0 (tinyalsa version %s)\n", TINYALSA_VERSION_STRING);
}
+static int is_command(char *arg) {
+ return strcmp(arg, "get") == 0 || strcmp(arg, "set") == 0 ||
+ strcmp(arg, "controls") == 0 || strcmp(arg, "contents") == 0;
+}
+
+static int find_command_position(int argc, char **argv)
+{
+ for (int i = 0; i < argc; ++i) {
+ if (is_command(argv[i])) {
+ return i;
+ }
+ }
+ return -1;
+}
+
int main(int argc, char **argv)
{
- struct mixer *mixer;
- int card = 0, c;
- char *cmd;
+ int card = 0;
struct optparse opts;
static struct optparse_long long_options[] = {
{ "card", 'D', OPTPARSE_REQUIRED },
@@ -78,8 +97,20 @@ int main(int argc, char **argv)
{ 0, 0, 0 }
};
- optparse_init(&opts, argv);
+ // optparse_long may change the order of the argv list. Duplicate one for parsing the options.
+ char **argv_options_list = (char **) calloc(argc + 1, sizeof(char *));
+ if (!argv_options_list) {
+ fprintf(stderr, "Failed to allocate options list\n");
+ return EXIT_FAILURE;
+ }
+
+ for (int i = 0; i < argc; ++i) {
+ argv_options_list[i] = argv[i];
+ }
+
+ optparse_init(&opts, argv_options_list);
/* Detect the end of the options. */
+ int c;
while ((c = optparse_long(&opts, long_options, NULL)) != -1) {
switch (c) {
case 'D':
@@ -87,57 +118,67 @@ int main(int argc, char **argv)
break;
case 'h':
usage();
+ free(argv_options_list);
return EXIT_SUCCESS;
case 'v':
version();
+ free(argv_options_list);
return EXIT_SUCCESS;
case '?':
- fprintf(stderr, "%s\n", opts.errmsg);
- return EXIT_FAILURE;
+ default:
+ break;
}
}
+ free(argv_options_list);
- mixer = mixer_open(card);
+ struct mixer *mixer = mixer_open(card);
if (!mixer) {
fprintf(stderr, "Failed to open mixer\n");
return EXIT_FAILURE;
}
- cmd = argv[opts.optind];
- if (cmd == NULL) {
- fprintf(stderr, "no command specified (see --help)\n");
+ int command_position = find_command_position(argc, argv);
+ if (command_position < 0) {
+ usage();
mixer_close(mixer);
return EXIT_FAILURE;
- } else if (strcmp(cmd, "get") == 0) {
- if ((opts.optind + 1) >= argc) {
+ }
+
+ char *cmd = argv[command_position];
+ if (strcmp(cmd, "get") == 0) {
+ if (command_position + 1 >= argc) {
fprintf(stderr, "no control specified\n");
mixer_close(mixer);
return EXIT_FAILURE;
}
- tinymix_detail_control(mixer, argv[opts.optind + 1]);
+ print_control_values_by_name_or_id(mixer, argv[command_position + 1]);
printf("\n");
} else if (strcmp(cmd, "set") == 0) {
- if ((opts.optind + 1) >= argc) {
+ if (command_position + 1 >= argc) {
fprintf(stderr, "no control specified\n");
mixer_close(mixer);
return EXIT_FAILURE;
}
- if ((opts.optind + 2) >= argc) {
+ if (command_position + 2 >= argc) {
fprintf(stderr, "no value(s) specified\n");
mixer_close(mixer);
return EXIT_FAILURE;
}
- int res = tinymix_set_value(mixer, argv[opts.optind + 1], &argv[opts.optind + 2], argc - opts.optind - 2);
+ int res = set_values(mixer,
+ argv[command_position + 1],
+ &argv[command_position + 2],
+ argc - command_position - 2);
if (res != 0) {
mixer_close(mixer);
return EXIT_FAILURE;
}
} else if (strcmp(cmd, "controls") == 0) {
- tinymix_list_controls(mixer, 0);
+ list_controls(mixer, 0);
} else if (strcmp(cmd, "contents") == 0) {
- tinymix_list_controls(mixer, 1);
+ list_controls(mixer, 1);
} else {
- fprintf(stderr, "unknown command '%s' (see --help)\n", cmd);
+ fprintf(stderr, "unknown command '%s'\n", cmd);
+ usage();
mixer_close(mixer);
return EXIT_FAILURE;
}
@@ -156,7 +197,7 @@ static int isnumber(const char *str) {
return strlen(end) == 0;
}
-static void tinymix_list_controls(struct mixer *mixer, int print_all)
+static void list_controls(struct mixer *mixer, int print_all)
{
struct mixer_ctl *ctl;
const char *name, *type;
@@ -180,12 +221,12 @@ static void tinymix_list_controls(struct mixer *mixer, int print_all)
num_values = mixer_ctl_get_num_values(ctl);
printf("%u\t%s\t%u\t%-40s", i, type, num_values, name);
if (print_all)
- tinymix_detail_control(mixer, name);
+ print_control_values(ctl);
printf("\n");
}
}
-static void tinymix_print_enum(struct mixer_ctl *ctl)
+static void print_enum(struct mixer_ctl *ctl)
{
unsigned int num_enums;
unsigned int i;
@@ -201,28 +242,34 @@ static void tinymix_print_enum(struct mixer_ctl *ctl)
}
}
-static void tinymix_detail_control(struct mixer *mixer, const char *control)
+static void print_control_values_by_name_or_id(struct mixer *mixer, const char *name_or_id)
{
struct mixer_ctl *ctl;
- enum mixer_ctl_type type;
- unsigned int num_values;
- unsigned int i;
- int min, max;
- int ret;
- char *buf = NULL;
- if (isnumber(control))
- ctl = mixer_get_ctl(mixer, atoi(control));
+ if (isnumber(name_or_id))
+ ctl = mixer_get_ctl(mixer, atoi(name_or_id));
else
- ctl = mixer_get_ctl_by_name(mixer, control);
+ ctl = mixer_get_ctl_by_name(mixer, name_or_id);
if (!ctl) {
fprintf(stderr, "Invalid mixer control\n");
return;
}
- type = mixer_ctl_get_type(ctl);
- num_values = mixer_ctl_get_num_values(ctl);
+ print_control_values(ctl);
+}
+
+static void print_control_values(struct mixer_ctl *control)
+{
+ enum mixer_ctl_type type;
+ unsigned int num_values;
+ unsigned int i;
+ int min, max;
+ int ret;
+ char *buf = NULL;
+
+ type = mixer_ctl_get_type(control);
+ num_values = mixer_ctl_get_num_values(control);
if ((type == MIXER_CTL_TYPE_BYTE) && (num_values > 0)) {
buf = calloc(1, num_values);
@@ -231,7 +278,7 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control)
return;
}
- ret = mixer_ctl_get_array(ctl, buf, num_values);
+ ret = mixer_ctl_get_array(control, buf, num_values);
if (ret < 0) {
fprintf(stderr, "Failed to mixer_ctl_get_array\n");
free(buf);
@@ -243,16 +290,16 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control)
switch (type)
{
case MIXER_CTL_TYPE_INT:
- printf("%d", mixer_ctl_get_value(ctl, i));
+ printf("%d", mixer_ctl_get_value(control, i));
break;
case MIXER_CTL_TYPE_BOOL:
- printf("%s", mixer_ctl_get_value(ctl, i) ? "On" : "Off");
+ printf("%s", mixer_ctl_get_value(control, i) ? "On" : "Off");
break;
case MIXER_CTL_TYPE_ENUM:
- tinymix_print_enum(ctl);
+ print_enum(control);
break;
case MIXER_CTL_TYPE_BYTE:
- printf(" %02x", buf[i]);
+ printf("%02hhx", buf[i]);
break;
default:
printf("unknown");
@@ -264,8 +311,8 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control)
}
if (type == MIXER_CTL_TYPE_INT) {
- min = mixer_ctl_get_range_min(ctl);
- max = mixer_ctl_get_range_max(ctl);
+ min = mixer_ctl_get_range_min(control);
+ max = mixer_ctl_get_range_max(control);
printf(" (range %d->%d)", min, max);
}
@@ -323,54 +370,65 @@ fail:
static int is_int(const char *value)
{
- return (value[0] >= '0') || (value[0] <= '9');
+ return value[0] >= '0' || value[0] <= '9';
}
struct parsed_int
{
- /** Wether or not the integer was valid. */
- int valid;
- /** The value of the parsed integer. */
- int value;
- /** The number of characters that were parsed. */
- unsigned int length;
- /** The number of characters remaining in the string. */
- unsigned int remaining_length;
- /** The remaining characters (or suffix) of the integer. */
- const char* remaining;
+ /** Wether or not the integer was valid. */
+ int valid;
+ /** The value of the parsed integer. */
+ int value;
+ /** The number of characters that were parsed. */
+ unsigned int length;
+ /** The number of characters remaining in the string. */
+ unsigned int remaining_length;
+ /** The remaining characters (or suffix) of the integer. */
+ const char* remaining;
};
static struct parsed_int parse_int(const char* str)
{
- struct parsed_int out = {
- 0 /* valid */,
- 0 /* value */,
- 0 /* length */,
- 0 /* remaining length */,
- "" /* remaining characters */
- };
+ struct parsed_int out = {
+ 0 /* valid */,
+ 0 /* value */,
+ 0 /* length */,
+ 0 /* remaining length */,
+ NULL /* remaining characters */
+ };
- unsigned int max = strlen(str);
+ size_t length = strlen(str);
+ size_t i = 0;
+ int negative = 0;
- for (unsigned int i = 0; i < max; i++) {
+ if (i < length && str[i] == '-') {
+ negative = 1;
+ i++;
+ }
- char c = str[i];
+ while (i < length) {
+ char c = str[i++];
- if ((c < '0') || (c > '9')) {
- break;
- }
+ if (c < '0' || c > '9') {
+ --i;
+ break;
+ }
+
+ out.value *= 10;
+ out.value += c - '0';
- out.value *= 10;
- out.value += c - '0';
+ out.length++;
+ }
- out.length++;
- }
+ if (negative) {
+ out.value *= -1;
+ }
- out.valid = out.length > 0;
- out.remaining_length = max - out.length;
- out.remaining = str + out.length;
+ out.valid = out.length > 0;
+ out.remaining_length = length - out.length;
+ out.remaining = str + out.length;
- return out;
+ return out;
}
struct control_value
@@ -395,21 +453,22 @@ static struct control_value to_control_value(const char* value_string)
unsigned int i = 0;
if (parsed_int.remaining[i] == '%') {
- out.is_percent = 1;
- i++;
+ out.is_percent = 1;
+ i++;
}
if (parsed_int.remaining[i] == '+') {
- out.is_relative = 1;
+ out.is_relative = 1;
} else if (parsed_int.remaining[i] == '-') {
- out.is_relative = 1;
- out.value *= -1;
+ out.is_relative = 1;
+ out.value *= -1;
}
return out;
}
-static int set_control_value(struct mixer_ctl* ctl, unsigned int i, const struct control_value* value)
+static int set_control_value(struct mixer_ctl* ctl, unsigned int i,
+ const struct control_value* value)
{
int next_value = value->value;
@@ -443,7 +502,8 @@ static int set_control_values(struct mixer_ctl* ctl,
for (unsigned int i = 0; i < num_values; i++) {
int res = set_control_value(ctl, i, &value);
if (res != 0) {
- fprintf(stderr, "Error: invalid value\n");
+ fprintf(stderr, "Error: invalid value (%d%s%s)\n", value.value,
+ value.is_relative ? "r" : "", value.is_percent ? "%" : "");
return -1;
}
}
@@ -464,7 +524,8 @@ static int set_control_values(struct mixer_ctl* ctl,
int res = set_control_value(ctl, i, &v);
if (res != 0) {
- fprintf(stderr, "Error: invalid value for index %u\n", i);
+ fprintf(stderr, "Error: invalid value (%d%s%s) for index %u\n", v.value,
+ v.is_relative ? "r" : "", v.is_percent ? "%" : "", i);
return -1;
}
}
@@ -473,7 +534,7 @@ static int set_control_values(struct mixer_ctl* ctl,
return 0;
}
-static int tinymix_set_value(struct mixer *mixer, const char *control,
+static int set_values(struct mixer *mixer, const char *control,
char **values, unsigned int num_values)
{
struct mixer_ctl *ctl;