Control GPIO using the new Linux user space GPIO API

From the version 4.8, the Linux kernel introduces a new user space API based on character devices for managing and controlling GPIOs ( General-Purpose Input/Output). This post presents the basic of the new interface as well as a simple tutorial/example to demonstrate how to use the new API to control GPIOs.

The hardware used in the tutorial is the Raspberry Pi 3B but the code is generic and can be used on any embedded hardware.

The old user space interface

Before the Linux kernel 4.8, the only interface to manage GPIO in user space is the sysfs interface. GPIOs are configured and controlled via exported files in /sys/class/gpio. Basic GPIO operations that can be performed via this interface:

  • Export a GPIO via /sys/class/gpio/export
  • Configure the GPIO direction (input/output) via: /sys/class/gpio/gpioX/direction
  • Read/write GPIO value via /sys/class/gpio/gpioX/value

This interface is simple and easy to use but it has some drawbacks:

  • Lack of bulk GPIO reads and writes, GPIO configuration options are limited (for example: unable to configure a GPIO as Active low, open drain, open source etc.)
  • Race condition problems may occur when two or more processes accessing the same GPIO at the same time.
  • Polling event from GPIO is not reliable
  • etc.

The new Character Device interface

Since version 4.8, Linux kernel introduces a new user-space GPIO interface based on character device. Interface to the GPIO controller is available in user space in form of a character device: /dev/gpiochipX. Basic file operations such as open(), read(), write(), ioctl(), poll(), close() can be used to interact with the GPIO controller.

This section will detail on how to use this new API to configure and control GPIO via the ioctl interface.

Basically, to interact with the GPIO controller, we first need to open character device using the traditional file open operation:

// include API header for the new interface
#include <linux/gpio.h>

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <stdlib.h>

#define DEV_NAME "/dev/gpiochip0"

int fd, ret;
fd = open(DEV_NAME, O_RDONLY);
if (fd < 0)
{
    printf("Unabled to open %s: %s", dev_name, strerror(errno));
    return;
}
/*
    control GPIO here, such as:
    - configure
    - read
    - write
    - polling
*/
(void)close(fd);

Note that we open the character device /dev/gpiochip0 in read only mode, all GPIO control operations will be performed on the opened file descriptor fd.

This tutorial covers the following operations:

  • GPIO chip and lines information reading
  • GPIO read and write operations (in bulk mode).
  • GPIO event polling

Get GPIO chip information via the IOCTL interface

GPIO chip information is stored in a structure of type struct gpiochip_info and can be queried via the IOCTL GPIO_GET_CHIPINFO_IOCTL request:

struct gpiochip_info info;
struct gpioline_info line_info;
int fd, ret;
// open the device
fd = open(DEV_NAME, O_RDONLY);
if (fd < 0)
{
    printf("Unabled to open %s: %s", dev_name, strerror(errno));
    return;
}

// Query GPIO chip information
ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);
if (ret == -1)
{
    printf("Unable to get chip info from ioctl: %s", strerror(errno));
    close(fd);
    return;
}
printf("Chip name: %s\n", info.name);
printf("Chip label: %s\n", info.label);
printf("Number of lines: %d\n", info.lines);

On line 13 we query the chip info from the kernel using the IOCTL interface (GPIO_GET_CHIPINFO_IOCTL request). The structure info contains the chip name, the chip label, and importantly the number of GPIO lines. From here, we can further query the state of each GPIO lines by issuing the IOCTL GPIO_GET_LINEINFO_IOCTL request on the file descriptor. The line information for each GPIO is stored in a structure of type: struct gpioline_info:

struct gpioline_info line_info;
for (int i = 0; i < info.lines; i++)
{
    line_info.line_offset = i;
    ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line_info);
    if (ret == -1)
    {
        printf("Unable to get line info from offset %d: %s", i, strerror(errno));
    }
    else
    {
        printf("offset: %d, name: %s, consumer: %s. Flags:\t[%s]\t[%s]\t[%s]\t[%s]\t[%s]\n",
               i,
               line_info.name,
               line_info.consumer,
               (line_info.flags & GPIOLINE_FLAG_IS_OUT) ? "OUTPUT" : "INPUT",
               (line_info.flags & GPIOLINE_FLAG_ACTIVE_LOW) ? "ACTIVE_LOW" : "ACTIVE_HIGHT",
               (line_info.flags & GPIOLINE_FLAG_OPEN_DRAIN) ? "OPEN_DRAIN" : "...",
               (line_info.flags & GPIOLINE_FLAG_OPEN_SOURCE) ? "OPENSOURCE" : "...",
               (line_info.flags & GPIOLINE_FLAG_KERNEL) ? "KERNEL" : "");
    }
}

On line 4, we set the line_offset property of the structure line_info to the GPIO line/offset that we want to query before calling the ioctl function with the GPIO_GET_LINEINFO_IOCTL request.

Below is the example output of the code executed on a Raspberry Pi 3B+:

Chip name: gpiochip0
Chip label: pinctrl-bcm2835
Number of lines: 54
offset: 0, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 1, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 2, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 3, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 4, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 5, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 6, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 7, name: , consumer: spi0 CS1. Flags:    [OUTPUT]    [ACTIVE_LOW]    [...]    [...]    [KERNEL]
offset: 8, name: , consumer: spi0 CS0. Flags:    [OUTPUT]    [ACTIVE_LOW]    [...]    [...]    [KERNEL]
offset: 9, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 10, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 11, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 12, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 13, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 14, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 15, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 16, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 17, name: , consumer: ads7846_pendown. Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    [KERNEL]
offset: 18, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 19, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 20, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 21, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 22, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 23, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 24, name: , consumer: dc. Flags:    [OUTPUT]    [ACTIVE_HIGHT]    [...]    [...]    [KERNEL]
offset: 25, name: , consumer: reset. Flags:    [OUTPUT]    [ACTIVE_LOW]    [...]    [...]    [KERNEL]
offset: 26, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 27, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 28, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 29, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 30, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 31, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 32, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 33, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 34, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 35, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 36, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 37, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 38, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 39, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 40, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 41, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 42, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 43, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 44, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 45, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 46, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 47, name: , consumer: . Flags:    [OUTPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 48, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 49, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 50, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 51, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 52, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []
offset: 53, name: , consumer: . Flags:    [INPUT]    [ACTIVE_HIGHT]    [...]    [...]    []

Question: what is exactly the mapping between GPIO offsets (bcm2835) and the physical pinout of the raspberry Pi ?

Well, if we look at the Raspberry pinout:

The offsets here correspond to the BCMx pins on the schema above. For example the offset 17 correspond to the BCM 17 (physical pin 11)

(Bulk) Writing values to output GPIOs via the IOCTL interface

Let say we want to configure pin 11 and 13 (BCM 17 and BCM 27) as OUTPUT and we want to write HIGH (1) on pin 11 and LOW (0) on pin 13.

The new API allows to perform configuration, read and write operation in bulk mode, it can handle multiple GPIO lines in a single IOCTL request.

Basically, to write values to GPIOs, we first need to configured them as OUTPUTs. This can be done by :

  • Filling configuration values in a struct of type struct gpiohandle_request
  • Issuing a GPIO_GET_LINEHANDLE_IOCTL to the GPIO controller via the IOCTL interface to request a lines handle of the configured GPIOs
// open the device
fd = ...
...

struct gpiohandle_request rq;
rq.lineoffsets[0] = 17;
rq.lineoffsets[1] = 27;
rq.lines = 2;
rq.flags = GPIOHANDLE_REQUEST_OUTPUT;

ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &rq);
close(fd);
if (ret == -1)
{
    printf("Unable to line handle from ioctl : %s", strerror(errno));
    close(fd);
    return;
}

In the structure rq, we need to specify which lines (offset 17, offset 27) and the number of lines we want to configured as well as the lines flags. In this case we use the flag GPIOHANDLE_REQUEST_OUTPUT to configure the two lines (17 and 27) as outputs.

If the IOCTL GPIO_GET_LINEHANDLE_IOCTL request successes, a single lines handle is set to the property fd of the structure rq (struct gpiohandle_request) in form of a file descriptor (rq.fd). This file descriptor should be used to write values to the configured GPIOs. The device file (fd) descriptor can now be safety closed.

To write values to the two GPIOs, simply fill the values to a structure of type struct gpiohandle_data, then perform a IOCTL GPIOHANDLE_SET_LINE_VALUES_IOCTL request with this structure on the lines handle file descriptor

 struct gpiohandle_data data;
data.values[0] = 1; // HIGH offset 17
data.values[1] = 0; // LOW offset 27
ret = ioctl(rq.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (ret == -1)
{
    printf("Unable to set line value using ioctl : %s", strerror(errno));
}
// do something else
close(rq.fd);

(Bulk) reading input GPIOs values via the IOCTL interface

The GPIOs reading operation follows the same principle as the writing operation. The modifications necessary are to:

  • Replace GPIOHANDLE_REQUEST_OUTPUT with GPIOHANDLE_REQUEST_INPUT
  • Replace GPIOHANDLE_SET_LINE_VALUES_IOCTL with GPIOHANDLE_GET_LINE_VALUES_IOCTL
struct gpiohandle_request rq;
struct gpiohandle_data data;
int fd, ret;

// open the device
fd = ...
...

rq.lineoffsets[0] = 17;
rq.lineoffsets[1] = 27;
rq.flags = GPIOHANDLE_REQUEST_INPUT;
rq.lines = 2;
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &rq);
close(fd);
if (ret == -1)
{
    printf("Unable to get line handle from ioctl : %s", strerror(errno));
    return;
}
ret = ioctl(rq.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
if (ret == -1)
{
    printf("Unable to get line value using ioctl : %s", strerror(errno));
}
else
{
    printf("Value of GPIO at offset 17: %d and offset 27: %d\n", data.values[0], data.values[1]);
}
close(rq.fd);

GPIO event polling

The new API provides a convenient way to poll GPIO events such as raising edge, falling edge or both edges using the traditional polling API (on file descriptors). Two basic steps for GPIO event polling:

  • Request the event handle on a GPIO line in form of a file descriptor (via the IOCTL interface)
  • Perform traditional polling on that file descriptor

The following snippet requests a event handle on the GPIO at offset 17 (BCM 17).

#include <sys/poll.h>
...

struct gpioevent_request rq;
int fd, ret;

// open the device
fd = ...

rq.lineoffset = 17;
rq.eventflags = GPIOEVENT_EVENT_RISING_EDGE;
ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
close(fd);
if (ret == -1)
{
    printf("Unable to get line event from ioctl : %s", strerror(errno));
    close(fd);
    return;
}

Note that on lines 10 and 11, we configure the event handle to listen to the rising edge event (GPIOEVENT_EVENT_RISING_EDGE) on the GPIO at offset 17.

If the request successes, the line event file descriptor is set to the fd property of the structure rq (struct gpioevent_request). The traditional polling operation can the be performed on this file descriptor (rq.fd).

struct pollfd pfd;
pfd.fd = rq.fd;
pfd.events = POLLIN;
ret = poll(&pfd, 1, -1);
if (ret == -1)
{
    printf("Error while polling event from GPIO: %s", strerror(errno));
}
else if (pfd.revents & POLLIN)
{
    printf("Rising edge event on GPIO offset: %d, of %s\n", offset, dev_name);
}
close(rq.fd);

The code above simply blocks until the event is triggered (polling timeout set to -1). Note that the polling API can poll multiple file descriptors at the same time, thus, it is able to poll events on multiple GPIO lines.

Final code

Bellow is the complete working code of all GPIO operations presented in this post:

#include <linux/gpio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdint.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/poll.h>

typedef enum
{
    APP_OPT_GPIO_LIST,
    APP_OPT_GPIO_READ,
    APP_OPT_GPIO_WRITE,
    APP_OPT_GPIO_POLL,
    APP_OPT_UNKNOWN
} app_mode_t;

typedef struct
{
    char *dev;
    int offset;
    uint8_t val;
    app_mode_t mode;
} app_opt_t;

static void gpio_list(const char *dev_name)
{
    struct gpiochip_info info;
    struct gpioline_info line_info;
    int fd, ret;
    fd = open(dev_name, O_RDONLY);
    if (fd < 0)
    {
        printf("Unabled to open %s: %s", dev_name, strerror(errno));
        return;
    }
    ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);
    if (ret == -1)
    {
        printf("Unable to get chip info from ioctl: %s", strerror(errno));
        close(fd);
        return;
    }
    printf("Chip name: %s\n", info.name);
    printf("Chip label: %s\n", info.label);
    printf("Number of lines: %d\n", info.lines);

    for (int i = 0; i < info.lines; i++)
    {
        line_info.line_offset = i;
        ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line_info);
        if (ret == -1)
        {
            printf("Unable to get line info from offset %d: %s", i, strerror(errno));
        }
        else
        {
            printf("offset: %d, name: %s, consumer: %s. Flags:\t[%s]\t[%s]\t[%s]\t[%s]\t[%s]\n",
                   i,
                   line_info.name,
                   line_info.consumer,
                   (line_info.flags & GPIOLINE_FLAG_IS_OUT) ? "OUTPUT" : "INPUT",
                   (line_info.flags & GPIOLINE_FLAG_ACTIVE_LOW) ? "ACTIVE_LOW" : "ACTIVE_HIGHT",
                   (line_info.flags & GPIOLINE_FLAG_OPEN_DRAIN) ? "OPEN_DRAIN" : "...",
                   (line_info.flags & GPIOLINE_FLAG_OPEN_SOURCE) ? "OPENSOURCE" : "...",
                   (line_info.flags & GPIOLINE_FLAG_KERNEL) ? "KERNEL" : "");
        }
    }
    close(fd);
}

static void gpio_write(const char *dev_name, int offset, uint8_t value)
{
    struct gpiohandle_request rq;
    struct gpiohandle_data data;
    int fd, ret;
    printf("Write value %d to GPIO at offset %d (OUTPUT mode) on chip %s\n", value, offset, dev_name);
    fd = open(dev_name, O_RDONLY);
    if (fd < 0)
    {
        printf("Unabled to open %s: %s", dev_name, strerror(errno));
        return;
    }
    rq.lineoffsets[0] = offset;
    rq.flags = GPIOHANDLE_REQUEST_OUTPUT;
    rq.lines = 1;
    ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &rq);
    close(fd);
    if (ret == -1)
    {
        printf("Unable to line handle from ioctl : %s", strerror(errno));
        return;
    }
    data.values[0] = value;
    ret = ioctl(rq.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
    if (ret == -1)
    {
        printf("Unable to set line value using ioctl : %s", strerror(errno));
    }
    else
    {
         usleep(2000000);
    }
    close(rq.fd);
}

static void gpio_read(const char *dev_name, int offset)
{
    struct gpiohandle_request rq;
    struct gpiohandle_data data;
    int fd, ret;
    fd = open(dev_name, O_RDONLY);
    if (fd < 0)
    {
        printf("Unabled to open %s: %s", dev_name, strerror(errno));
        return;
    }
    rq.lineoffsets[0] = offset;
    rq.flags = GPIOHANDLE_REQUEST_INPUT;
    rq.lines = 1;
    ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &rq);
    close(fd);
    if (ret == -1)
    {
        printf("Unable to get line handle from ioctl : %s", strerror(errno));
        return;
    }
    ret = ioctl(rq.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
    if (ret == -1)
    {
        printf("Unable to get line value using ioctl : %s", strerror(errno));
    }
    else
    {
        printf("Value of GPIO at offset %d (INPUT mode) on chip %s: %d\n", offset, dev_name, data.values[0]);
    }
    close(rq.fd);

}

static void gpio_poll(const char *dev_name, int offset)
{
    struct gpioevent_request rq;
    struct pollfd pfd;
    int fd, ret;
    fd = open(dev_name, O_RDONLY);
    if (fd < 0)
    {
        printf("Unabled to open %s: %s", dev_name, strerror(errno));
        return;
    }
    rq.lineoffset = offset;
    rq.eventflags = GPIOEVENT_EVENT_RISING_EDGE;
    ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
    close(fd);
    if (ret == -1)
    {
        printf("Unable to get line event from ioctl : %s", strerror(errno));
        close(fd);
        return;
    }
    pfd.fd = rq.fd;
    pfd.events = POLLIN;
    ret = poll(&pfd, 1, -1);
    if (ret == -1)
    {
        printf("Error while polling event from GPIO: %s", strerror(errno));
    }
    else if (pfd.revents & POLLIN)
    {
        printf("Rising edge event on GPIO offset: %d, of %s\n", offset, dev_name);
    }
    close(rq.fd);
}

static void help(const char *app)
{
    fprintf(stderr,
            "Usage: %s options dev_name.\n"
            "Options:\n"
            "\t -i: print gpio chip info\n"
            "\t -r <offset>: Read GPIO value at offset (INPUT mode)\n"
            "\t -w <offset>: Write GPIO value at offset (OUTPUT mode). Option -v should be set\n"
            "\t -v <0|1>: value that should be written to the GPIO, used only with option -w\n"
            "\t -p <offset>: Polling raising event on the GPIO at offset\n",
            app);
}

int main(int argc, char *const *argv)
{
    int ret;
    app_opt_t opt;
    opt.val = 0;
    opt.dev = NULL;
    opt.mode = APP_OPT_UNKNOWN;
    while ((ret = getopt(argc, argv, "lr:w:p:v:")) != -1)
    {
        switch (ret)
        {
        case 'l':
            opt.mode = APP_OPT_GPIO_LIST;
            break;
        case 'r':
            opt.mode = APP_OPT_GPIO_READ;
            opt.offset = atoi(optarg);
            break;

        case 'w':
            opt.mode = APP_OPT_GPIO_WRITE;
            opt.offset = atoi(optarg);
            break;

        case 'v':
            opt.val = (uint8_t)atoi(optarg);
            break;

        case 'p':
            opt.mode = APP_OPT_GPIO_POLL;
            opt.offset = atoi(optarg);
            break;

        default:
            help(argv[0]);
            return -1;
        }
    }

    if (optind >= argc || opt.mode == APP_OPT_UNKNOWN)
    {
        help(argv[0]);
        return -1;
    }
    opt.dev = argv[optind];

    switch (opt.mode)
    {
    case APP_OPT_GPIO_LIST:
        gpio_list(opt.dev);
        break;
    case APP_OPT_GPIO_WRITE:
        gpio_write(opt.dev, opt.offset, opt.val);
        break;
    case APP_OPT_GPIO_READ:
        gpio_read(opt.dev, opt.offset);
        break;
    case APP_OPT_GPIO_POLL:
        gpio_poll(opt.dev, opt.offset);
        break;

    default:
        help(argv[0]);
        return -1;
    }
    return 0;
}

Related posts

Comments

The comment editor supports Markdown document format. Your email is necessary to notify you of further updates on the discussion. It will be hidden from the public.
Powered by antd server, (c) 2017 - 2020 Xuan Sang LE