gpioset: major rework
authorBartosz Golaszewski <bartekgola@gmail.com>
Tue, 17 Jan 2017 14:11:16 +0000 (15:11 +0100)
committerBartosz Golaszewski <bartekgola@gmail.com>
Tue, 17 Jan 2017 14:11:16 +0000 (15:11 +0100)
Allow setting multiple lines at once and add different modes
of operation.

Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com>
src/tools/gpioset.c

index 434deab1be8300838fe0f46b867ec3862f1460e0..13654381b09b0aefec2675fd5659b4dca9284fc2 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include <getopt.h>
+#include <sys/select.h>
+#include <limits.h>
 
 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] <chip name/number> <line offset> <value>\n",
+       printf("Usage: %s [OPTIONS] <chip name/number> <offset1>=<value1> <offset2>=<value2> ...\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;
 }