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@xxxxxxxxx> --- Changes for v2: - reverted to using a line request for each line in an event request to retain complete compatiblity with the existing libgpiod API. All tests now pass, including CXX and Python, and also tested on a kernel with uAPI v1 removed to double check it is only using uAPI v2. include/gpiod.h | 2 + lib/core.c | 287 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 193 insertions(+), 96 deletions(-) diff --git a/include/gpiod.h b/include/gpiod.h index 3477f9d..a6e34ae 100644 --- a/include/gpiod.h +++ b/include/gpiod.h @@ -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. */ }; /** diff --git a/lib/core.c b/lib/core.c index b964272..1abe3a4 100644 --- a/lib/core.c +++ b/lib/core.c @@ -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; -- 2.28.0