core: Basic port to uAPI v2
authorKent Gibson <warthog618@gmail.com>
Wed, 14 Oct 2020 11:30:52 +0000 (19:30 +0800)
committerBartosz Golaszewski <bgolaszewski@baylibre.com>
Wed, 14 Oct 2020 13:15:53 +0000 (15:15 +0200)
Port existing implementation from GPIO uAPI v1 to v2.
The libgpiod external interface remains unchanged, only the internal
implementation switches from uAPI v1 to v2.

This is a minimal port - uAPI v2 features are not used, only the
uAPI v1 calls are switched to the v2 equivalent.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
include/gpiod.h
lib/core.c

index 3477f9d341d319dfc1c4d0059d1b0754633646b3..a6e34ae1bf73295dcd79c23579f2c768a1400008 100644 (file)
@@ -1485,6 +1485,8 @@ struct gpiod_line_event {
        /**< Best estimate of time of event occurrence. */
        int event_type;
        /**< Type of the event that occurred. */
+       int offset;
+       /**< Offset of line on which the event occurred. */
 };
 
 /**
index b9642723a2e42ea743328e746893dd7abeed513b..1abe3a4e28e6a6579cd75d1c668e1836b9112143 100644 (file)
@@ -403,26 +403,49 @@ bool gpiod_line_needs_update(struct gpiod_line *line GPIOD_UNUSED)
        return false;
 }
 
+static int line_info_v2_to_info_flags(struct gpio_v2_line_info *info)
+{
+       int iflags = 0;
+
+       if (info->flags & GPIO_V2_LINE_FLAG_USED)
+               iflags |= GPIOLINE_FLAG_KERNEL;
+
+       if (info->flags & GPIO_V2_LINE_FLAG_OPEN_DRAIN)
+               iflags |= GPIOLINE_FLAG_OPEN_DRAIN;
+       if (info->flags & GPIO_V2_LINE_FLAG_OPEN_SOURCE)
+               iflags |= GPIOLINE_FLAG_OPEN_SOURCE;
+
+       if (info->flags & GPIO_V2_LINE_FLAG_BIAS_DISABLED)
+               iflags |= GPIOLINE_FLAG_BIAS_DISABLE;
+       if (info->flags & GPIO_V2_LINE_FLAG_BIAS_PULL_UP)
+               iflags |= GPIOLINE_FLAG_BIAS_PULL_UP;
+       if (info->flags & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN)
+               iflags |= GPIOLINE_FLAG_BIAS_PULL_DOWN;
+
+       return iflags;
+}
+
 int gpiod_line_update(struct gpiod_line *line)
 {
-       struct gpioline_info info;
+       struct gpio_v2_line_info info;
        int rv;
 
        memset(&info, 0, sizeof(info));
-       info.line_offset = line->offset;
+       info.offset = line->offset;
 
-       rv = ioctl(line->chip->fd, GPIO_GET_LINEINFO_IOCTL, &info);
+       rv = ioctl(line->chip->fd, GPIO_V2_GET_LINEINFO_IOCTL, &info);
        if (rv < 0)
                return -1;
 
-       line->direction = info.flags & GPIOLINE_FLAG_IS_OUT
+       line->direction = info.flags & GPIO_V2_LINE_FLAG_OUTPUT
                                                ? GPIOD_LINE_DIRECTION_OUTPUT
                                                : GPIOD_LINE_DIRECTION_INPUT;
-       line->active_state = info.flags & GPIOLINE_FLAG_ACTIVE_LOW
+
+       line->active_state = info.flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW
                                                ? GPIOD_LINE_ACTIVE_STATE_LOW
                                                : GPIOD_LINE_ACTIVE_STATE_HIGH;
 
-       line->info_flags = info.flags;
+       line->info_flags = line_info_v2_to_info_flags(&info);
 
        strncpy(line->name, info.name, sizeof(line->name));
        strncpy(line->consumer, info.consumer, sizeof(line->consumer));
@@ -508,86 +531,149 @@ static bool line_request_direction_is_valid(int direction)
        return false;
 }
 
-static __u32 line_request_direction_to_gpio_handleflag(int direction)
+static void line_request_type_to_gpio_v2_line_config(int reqtype,
+               struct gpio_v2_line_config *config)
 {
-       if (direction == GPIOD_LINE_REQUEST_DIRECTION_INPUT)
-               return GPIOHANDLE_REQUEST_INPUT;
-       if (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
-               return GPIOHANDLE_REQUEST_OUTPUT;
+       if (reqtype == GPIOD_LINE_REQUEST_DIRECTION_AS_IS)
+               return;
 
-       return 0;
+       if (reqtype == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT) {
+               config->flags |= GPIO_V2_LINE_FLAG_OUTPUT;
+               return;
+       }
+       config->flags |= GPIO_V2_LINE_FLAG_INPUT;
+
+       if (reqtype == GPIOD_LINE_REQUEST_EVENT_RISING_EDGE)
+               config->flags |= GPIO_V2_LINE_FLAG_EDGE_RISING;
+       else if (reqtype == GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE)
+               config->flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING;
+       else if (reqtype == GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES)
+               config->flags |= (GPIO_V2_LINE_FLAG_EDGE_RISING |
+                                 GPIO_V2_LINE_FLAG_EDGE_FALLING);
 }
 
-static __u32 line_request_flag_to_gpio_handleflag(int flags)
+static void line_request_flag_to_gpio_v2_line_config(int flags,
+               struct gpio_v2_line_config *config)
 {
-       int hflags = 0;
+       if (flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW)
+               config->flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
 
        if (flags & GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN)
-               hflags |= GPIOHANDLE_REQUEST_OPEN_DRAIN;
-       if (flags & GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)
-               hflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE;
-       if (flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW)
-               hflags |= GPIOHANDLE_REQUEST_ACTIVE_LOW;
+               config->flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN;
+       else if (flags & GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)
+               config->flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE;
+
        if (flags & GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE)
-               hflags |= GPIOHANDLE_REQUEST_BIAS_DISABLE;
-       if (flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN)
-               hflags |= GPIOHANDLE_REQUEST_BIAS_PULL_DOWN;
-       if (flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP)
-               hflags |= GPIOHANDLE_REQUEST_BIAS_PULL_UP;
+               config->flags |= GPIO_V2_LINE_FLAG_BIAS_DISABLED;
+       else if (flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN)
+               config->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN;
+       else if (flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP)
+               config->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP;
+}
+
+static void line_request_config_to_gpio_v2_line_config(
+               const struct gpiod_line_request_config *reqcfg,
+               struct gpio_v2_line_config *lc)
+{
+       line_request_type_to_gpio_v2_line_config(reqcfg->request_type, lc);
+       line_request_flag_to_gpio_v2_line_config(reqcfg->flags, lc);
+}
+
+static bool line_request_config_validate(
+               const struct gpiod_line_request_config *config)
+{
+       int bias_flags = 0;
+
+       if ((config->request_type != GPIOD_LINE_REQUEST_DIRECTION_OUTPUT) &&
+           (config->flags & (GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN |
+                             GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)))
+               return false;
+
+
+       if ((config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN) &&
+           (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)) {
+               return false;
+       }
+
+       if (config->flags & GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE)
+               bias_flags++;
+       if (config->flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP)
+               bias_flags++;
+       if (config->flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN)
+               bias_flags++;
+       if (bias_flags > 1)
+               return false;
+
+       return true;
+}
 
-       return hflags;
+static void lines_bitmap_set_bit(__u64 *bits, int nr)
+{
+       *bits |= _BITULL(nr);
+}
+
+static void lines_bitmap_clear_bit(__u64 *bits, int nr)
+{
+       *bits &= ~_BITULL(nr);
+}
+
+static int lines_bitmap_test_bit(__u64 bits, int nr)
+{
+       return !!(bits & _BITULL(nr));
+}
+
+static void lines_bitmap_assign_bit(__u64 *bits, int nr, bool value)
+{
+       if (value)
+               lines_bitmap_set_bit(bits, nr);
+       else
+               lines_bitmap_clear_bit(bits, nr);
 }
 
 static int line_request_values(struct gpiod_line_bulk *bulk,
                               const struct gpiod_line_request_config *config,
-                              const int *default_vals)
+                              const int *vals)
 {
        struct gpiod_line *line;
        struct line_fd_handle *line_fd;
-       struct gpiohandle_request req;
+       struct gpio_v2_line_request req;
        unsigned int i;
        int rv, fd;
 
-       if ((config->request_type != GPIOD_LINE_REQUEST_DIRECTION_OUTPUT) &&
-           (config->flags & (GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN |
-                             GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE))) {
-               errno = EINVAL;
-               return -1;
-       }
-
-       if ((config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN) &&
-           (config->flags & GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE)) {
+       if (!line_request_config_validate(config)) {
                errno = EINVAL;
                return -1;
        }
 
        memset(&req, 0, sizeof(req));
 
-       req.lines = gpiod_line_bulk_num_lines(bulk);
-       req.flags = line_request_flag_to_gpio_handleflag(config->flags);
-
-       if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_INPUT)
-               req.flags |= GPIOHANDLE_REQUEST_INPUT;
-       else if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
-               req.flags |= GPIOHANDLE_REQUEST_OUTPUT;
+       req.num_lines = gpiod_line_bulk_num_lines(bulk);
+       line_request_config_to_gpio_v2_line_config(config, &req.config);
 
-
-       gpiod_line_bulk_foreach_line_off(bulk, line, i) {
-               req.lineoffsets[i] = gpiod_line_offset(line);
-               if (config->request_type ==
-                               GPIOD_LINE_REQUEST_DIRECTION_OUTPUT &&
-                   default_vals)
-                       req.default_values[i] = !!default_vals[i];
+       gpiod_line_bulk_foreach_line_off(bulk, line, i)
+               req.offsets[i] = gpiod_line_offset(line);
+
+       if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT &&
+           vals) {
+               req.config.num_attrs = 1;
+               req.config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
+               gpiod_line_bulk_foreach_line_off(bulk, line, i) {
+                       lines_bitmap_assign_bit(
+                               &req.config.attrs[0].mask, i, 1);
+                       lines_bitmap_assign_bit(
+                               &req.config.attrs[0].attr.values,
+                               i, vals[i]);
+               }
        }
 
        if (config->consumer)
-               strncpy(req.consumer_label, config->consumer,
-                       sizeof(req.consumer_label) - 1);
+               strncpy(req.consumer, config->consumer,
+                       sizeof(req.consumer) - 1);
 
        line = gpiod_line_bulk_get_line(bulk, 0);
        fd = line->chip->fd;
 
-       rv = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
+       rv = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);
        if (rv < 0)
                return -1;
 
@@ -598,9 +684,9 @@ static int line_request_values(struct gpiod_line_bulk *bulk,
        gpiod_line_bulk_foreach_line_off(bulk, line, i) {
                line->state = LINE_REQUESTED_VALUES;
                line->req_flags = config->flags;
-               if (config->request_type ==
-                               GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
-                       line->output_value = req.default_values[i];
+               if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
+                       line->output_value = lines_bitmap_test_bit(
+                               req.config.attrs[0].attr.values, i);
                line_set_fd(line, line_fd);
 
                rv = gpiod_line_update(line);
@@ -617,27 +703,20 @@ static int line_request_event_single(struct gpiod_line *line,
                        const struct gpiod_line_request_config *config)
 {
        struct line_fd_handle *line_fd;
-       struct gpioevent_request req;
+       struct gpio_v2_line_request req;
        int rv;
 
        memset(&req, 0, sizeof(req));
 
        if (config->consumer)
-               strncpy(req.consumer_label, config->consumer,
-                       sizeof(req.consumer_label) - 1);
+               strncpy(req.consumer, config->consumer,
+                       sizeof(req.consumer) - 1);
 
-       req.lineoffset = gpiod_line_offset(line);
-       req.handleflags = line_request_flag_to_gpio_handleflag(config->flags);
-       req.handleflags |= GPIOHANDLE_REQUEST_INPUT;
+       req.offsets[0] = gpiod_line_offset(line);
+       req.num_lines = 1;
+       line_request_config_to_gpio_v2_line_config(config, &req.config);
 
-       if (config->request_type == GPIOD_LINE_REQUEST_EVENT_RISING_EDGE)
-               req.eventflags |= GPIOEVENT_REQUEST_RISING_EDGE;
-       else if (config->request_type == GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE)
-               req.eventflags |= GPIOEVENT_REQUEST_FALLING_EDGE;
-       else if (config->request_type == GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES)
-               req.eventflags |= GPIOEVENT_REQUEST_BOTH_EDGES;
-
-       rv = ioctl(line->chip->fd, GPIO_GET_LINEEVENT_IOCTL, &req);
+       rv = ioctl(line->chip->fd, GPIO_V2_GET_LINE_IOCTL, &req);
        if (rv < 0)
                return -1;
 
@@ -708,13 +787,13 @@ static bool line_request_is_events(int request)
 
 int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk,
                            const struct gpiod_line_request_config *config,
-                           const int *default_vals)
+                           const int *vals)
 {
        if (!line_bulk_same_chip(bulk) || !line_bulk_all_free(bulk))
                return -1;
 
        if (line_request_is_direction(config->request_type))
-               return line_request_values(bulk, config, default_vals);
+               return line_request_values(bulk, config, vals);
        else if (line_request_is_events(config->request_type))
                return line_request_events(bulk, config);
 
@@ -772,7 +851,7 @@ int gpiod_line_get_value(struct gpiod_line *line)
 
 int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, int *values)
 {
-       struct gpiohandle_data data;
+       struct gpio_v2_line_values lv;
        struct gpiod_line *line;
        unsigned int i;
        int rv, fd;
@@ -782,27 +861,31 @@ int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, int *values)
 
        line = gpiod_line_bulk_get_line(bulk, 0);
 
+       memset(&lv, 0, sizeof(lv));
+
        if (line->state == LINE_REQUESTED_VALUES) {
-               memset(&data, 0, sizeof(data));
+               for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++)
+                       lines_bitmap_set_bit(&lv.mask, i);
 
                fd = line_get_fd(line);
 
-               rv = ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
+               rv = ioctl(fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &lv);
                if (rv < 0)
                        return -1;
 
                for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++)
-                       values[i] = data.values[i];
+                       values[i] = lines_bitmap_test_bit(lv.bits, i);
 
        } else if (line->state == LINE_REQUESTED_EVENTS) {
+               lines_bitmap_set_bit(&lv.mask, 0);
                for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++) {
                        line = gpiod_line_bulk_get_line(bulk, i);
 
                        fd = line_get_fd(line);
-                       rv = ioctl(fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
+                       rv = ioctl(fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &lv);
                        if (rv < 0)
                                return -1;
-                       values[i] = data.values[0];
+                       values[i] = lines_bitmap_test_bit(lv.bits, 0);
                }
        } else {
                errno = EINVAL;
@@ -823,7 +906,7 @@ int gpiod_line_set_value(struct gpiod_line *line, int value)
 
 int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, const int *values)
 {
-       struct gpiohandle_data data;
+       struct gpio_v2_line_values lv;
        struct gpiod_line *line;
        unsigned int i;
        int rv, fd;
@@ -831,22 +914,22 @@ int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, const int *values)
        if (!line_bulk_same_chip(bulk) || !line_bulk_all_requested(bulk))
                return -1;
 
-       memset(&data, 0, sizeof(data));
+       memset(&lv, 0, sizeof(lv));
 
-       if (values) {
-               for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++)
-                       data.values[i] = (uint8_t)!!values[i];
+       for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++) {
+               lines_bitmap_set_bit(&lv.mask, i);
+               lines_bitmap_assign_bit(&lv.bits, i, values && values[i]);
        }
 
        line = gpiod_line_bulk_get_line(bulk, 0);
        fd = line_get_fd(line);
 
-       rv = ioctl(fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
+       rv = ioctl(fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &lv);
        if (rv < 0)
                return -1;
 
        gpiod_line_bulk_foreach_line_off(bulk, line, i)
-               line->output_value = data.values[i];
+               line->output_value = lines_bitmap_test_bit(lv.bits, i);
 
        return 0;
 }
@@ -866,7 +949,7 @@ int gpiod_line_set_config_bulk(struct gpiod_line_bulk *bulk,
                               int direction, int flags,
                               const int *values)
 {
-       struct gpiohandle_config hcfg;
+       struct gpio_v2_line_config hcfg;
        struct gpiod_line *line;
        unsigned int i;
        int rv, fd;
@@ -880,24 +963,30 @@ int gpiod_line_set_config_bulk(struct gpiod_line_bulk *bulk,
 
        memset(&hcfg, 0, sizeof(hcfg));
 
-       hcfg.flags = line_request_flag_to_gpio_handleflag(flags);
-       hcfg.flags |= line_request_direction_to_gpio_handleflag(direction);
+       line_request_flag_to_gpio_v2_line_config(flags, &hcfg);
+       line_request_type_to_gpio_v2_line_config(direction, &hcfg);
        if (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT && values) {
-               for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++)
-                       hcfg.default_values[i] = (uint8_t)!!values[i];
+               hcfg.num_attrs = 1;
+               hcfg.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
+               for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++) {
+                       lines_bitmap_assign_bit(&hcfg.attrs[0].mask, i, 1);
+                       lines_bitmap_assign_bit(
+                               &hcfg.attrs[0].attr.values, i, values[i]);
+               }
        }
 
        line = gpiod_line_bulk_get_line(bulk, 0);
        fd = line_get_fd(line);
 
-       rv = ioctl(fd, GPIOHANDLE_SET_CONFIG_IOCTL, &hcfg);
+       rv = ioctl(fd, GPIO_V2_LINE_SET_CONFIG_IOCTL, &hcfg);
        if (rv < 0)
                return -1;
 
        gpiod_line_bulk_foreach_line_off(bulk, line, i) {
                line->req_flags = flags;
                if (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
-                       line->output_value = hcfg.default_values[i];
+                       line->output_value = lines_bitmap_test_bit(
+                               hcfg.attrs[0].attr.values, i);
 
                rv = gpiod_line_update(line);
                if (rv < 0)
@@ -1082,8 +1171,13 @@ int gpiod_line_event_read_fd_multiple(int fd, struct gpiod_line_event *events,
        /*
         * 16 is the maximum number of events the kernel can store in the FIFO
         * so we can allocate the buffer on the stack.
+        *
+        * NOTE: This is no longer strictly true for uAPI v2.  While 16 is
+        * the default for single line, a request with multiple lines will
+        * have a larger buffer.  So need to rethink the allocation here,
+        * or at least the comment above...
         */
-       struct gpioevent_data evdata[16], *curr;
+       struct gpio_v2_line_event evdata[16], *curr;
        struct gpiod_line_event *event;
        unsigned int events_read, i;
        ssize_t rd;
@@ -1109,11 +1203,12 @@ int gpiod_line_event_read_fd_multiple(int fd, struct gpiod_line_event *events,
                curr = &evdata[i];
                event = &events[i];
 
-               event->event_type = curr->id == GPIOEVENT_EVENT_RISING_EDGE
+               event->offset = curr->offset;
+               event->event_type = curr->id == GPIO_V2_LINE_EVENT_RISING_EDGE
                                        ? GPIOD_LINE_EVENT_RISING_EDGE
                                        : GPIOD_LINE_EVENT_FALLING_EDGE;
-               event->ts.tv_sec = curr->timestamp / 1000000000ULL;
-               event->ts.tv_nsec = curr->timestamp % 1000000000ULL;
+               event->ts.tv_sec = curr->timestamp_ns / 1000000000ULL;
+               event->ts.tv_nsec = curr->timestamp_ns % 1000000000ULL;
        }
 
        return i;