From: Bartosz Golaszewski Date: Tue, 17 Jan 2017 14:11:16 +0000 (+0100) Subject: gpioset: major rework X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=70f489b80b230c4afd0f89656a5c2997ce6eff4c;p=qemu-gpiodev%2Flibgpiod.git gpioset: major rework Allow setting multiple lines at once and add different modes of operation. Signed-off-by: Bartosz Golaszewski --- diff --git a/src/tools/gpioset.c b/src/tools/gpioset.c index 434deab..1365438 100644 --- a/src/tools/gpioset.c +++ b/src/tools/gpioset.c @@ -14,43 +14,112 @@ #include #include #include +#include +#include static const struct option longopts[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { "active-low", no_argument, NULL, 'l' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "active-low", no_argument, NULL, 'l' }, + { "mode", required_argument, NULL, 'm' }, + { "sec", required_argument, NULL, 's' }, + { "usec", required_argument, NULL, 'u' }, { 0 }, }; -static const char *const shortopts = "+hvl"; +static const char *const shortopts = "+hvlm:s:u:"; static void print_help(void) { - printf("Usage: %s [OPTIONS] \n", + printf("Usage: %s [OPTIONS] = = ...\n", get_progname()); - printf("Set value of a GPIO line\n"); + printf("Set GPIO line values of a GPIO chip\n"); printf("Options:\n"); printf(" -h, --help:\t\tdisplay this message and exit\n"); printf(" -v, --version:\tdisplay the version and exit\n"); printf(" -l, --active-low:\tset the line active state to low\n"); + printf(" -m, --mode=[exit|wait|time] (defaults to 'exit'):\n"); + printf(" tell the program what to do after setting values\n"); + printf(" -s, --sec=SEC:\tspecify the number of seconds to wait (only valid for --mode=time)\n"); + printf(" -u, --usec=USEC:\tspecify the number of microseconds to wait (only valid for --mode=time)\n"); printf("\n"); - printf("This program reserves the GPIO line, sets its value and waits for the user to press ENTER before releasing the line\n"); + printf("Modes:\n"); + printf(" exit:\tset values and exit immediately\n"); + printf(" wait:\tset values and wait for user to press ENTER\n"); + printf(" time:\tset values and sleep for a specified amount of time\n"); } -static void wait_for_enter(void *data UNUSED) +struct callback_data { + /* Replace with a union once we have more modes using callback data. */ + struct timeval tv; +}; + +static void wait_enter(void *data UNUSED) { getchar(); } +static void wait_time(void *data) +{ + struct callback_data *cbdata = data; + + select(0, NULL, NULL, NULL, &cbdata->tv); +} + +enum { + MODE_EXIT = 0, + MODE_WAIT, + MODE_TIME, +}; + +struct mode_mapping { + int id; + const char *name; + gpiod_set_value_cb callback; +}; + +static const struct mode_mapping modes[] = { + [MODE_EXIT] = { + .id = MODE_EXIT, + .name = "exit", + .callback = NULL, + }, + [MODE_WAIT] = { + .id = MODE_WAIT, + .name = "wait", + .callback = wait_enter, + }, + [MODE_TIME] = { + .id = MODE_TIME, + .name = "time", + .callback = wait_time, + } +}; + +static const struct mode_mapping * parse_mode(const char *mode) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(modes); i++) + if (strcmp(mode, modes[i].name) == 0) + return &modes[i]; + + return NULL; +} + int main(int argc, char **argv) { - int value, status, optc, opti; + const struct mode_mapping *mode = &modes[MODE_EXIT]; + int *values, status, optc, opti; bool active_low = false; - unsigned int offset; + unsigned int *offsets, num_lines, i; char *device, *end; + struct callback_data cbdata; set_progname(argv[0]); + memset(&cbdata, 0, sizeof(cbdata)); + for (;;) { optc = getopt_long(argc, argv, shortopts, longopts, &opti); if (optc < 0) @@ -66,6 +135,26 @@ int main(int argc, char **argv) case 'l': active_low = true; break; + case 'm': + mode = parse_mode(optarg); + if (!mode) + die("invalid mode: %s", optarg); + break; + case 's': + if (mode->id != MODE_TIME) + die("--mode=time must be selected to specify seconds"); + cbdata.tv.tv_sec = strtoul(optarg, &end, 10); + if (*end != '\0') + die("invalid time value in seconds: %s", optarg); + break; + case 'u': + if (mode->id != MODE_TIME) + die("--mode=time must be selected to specify microseconds"); + cbdata.tv.tv_usec = strtoul(optarg, &end, 10); + if (*end != '\0') + die("invalid time value in microseconds: %s", + optarg); + break; case '?': die("try %s --help", get_progname()); default: @@ -80,25 +169,37 @@ int main(int argc, char **argv) die("gpiochip must be specified"); if (argc < 2) - die("gpio line offset must be specified"); - - if (argc < 3) - die("value must be specified"); + die("at least one gpio line offset to value mapping must be specified"); device = argv[0]; - offset = strtoul(argv[1], &end, 10); - if (*end != '\0') - die("invalid GPIO offset: %s", argv[1]); + num_lines = argc - 1; + + offsets = malloc(sizeof(*offsets) * num_lines); + values = malloc(sizeof(*values) * num_lines); + if (!values || !offsets) + die("out of memory"); + + for (i = 0; i < num_lines; i++) { + status = sscanf(argv[i + 1], "%u=%d", &offsets[i], &values[i]); + if (status != 2) + die("invalid offset<->value mapping: %s", argv[i + 1]); - value = strtoul(argv[2], &end, 10); - if (*end != '\0' || (value != 0 && value != 1)) - die("invalid value: %s", argv[2]); + if (values[i] != 0 && values[i] != 1) + die("value must be 0 or 1: %s", argv[i + 1]); - status = gpiod_simple_set_value(device, offset, value, - active_low, wait_for_enter, NULL); + if (offsets[i] > INT_MAX) + die("invalid offset: %s", argv[i + 1]); + } + + status = gpiod_simple_set_value_multiple(device, offsets, values, + num_lines, active_low, + mode->callback, &cbdata); if (status < 0) - die_perror("error setting the GPIO line value"); + die_perror("error setting the GPIO line values"); + + free(offsets); + free(values); return EXIT_SUCCESS; }