This introduces the bulk of changes for the v2 API in libgpiod. All objects become opaque. The API supports all features exposed by the v2 of the kernel uAPI. Signed-off-by: Bartosz Golaszewski <brgl@xxxxxxxx> --- include/gpiod.h | 1023 ++++++++++++++------------------ lib/Makefile.am | 9 +- lib/attr.c | 232 ++++++++ lib/chip.c | 183 ++++++ lib/config.c | 158 +++++ lib/core.c | 1131 ------------------------------------ lib/event.c | 44 +- lib/handle.c | 144 +++++ lib/helpers.c | 306 ---------- lib/info.c | 1 + lib/internal.c | 101 ++++ lib/internal.h | 14 + lib/mask.c | 43 ++ lib/misc.c | 75 +++ lib/request.c | 118 ++++ tests/tests-event.c | 58 +- tools/gpio-tools-test.bats | 12 +- tools/gpiodetect.c | 11 +- tools/gpiofind.c | 1 + tools/gpioget.c | 57 +- tools/gpioinfo.c | 12 +- tools/gpiomon.c | 113 ++-- tools/gpioset.c | 85 ++- tools/tools-common.c | 8 +- tools/tools-common.h | 2 +- 25 files changed, 1722 insertions(+), 2219 deletions(-) create mode 100644 lib/attr.c create mode 100644 lib/chip.c create mode 100644 lib/config.c delete mode 100644 lib/core.c create mode 100644 lib/handle.c delete mode 100644 lib/helpers.c create mode 100644 lib/mask.c create mode 100644 lib/request.c diff --git a/include/gpiod.h b/include/gpiod.h index e99233b..8831d4c 100644 --- a/include/gpiod.h +++ b/include/gpiod.h @@ -1,11 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> */ +/* SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@xxxxxxxx> */ #ifndef __LIBGPIOD_GPIOD_H__ #define __LIBGPIOD_GPIOD_H__ #include <stdbool.h> -#include <stdlib.h> #include <stdint.h> #ifdef __cplusplus @@ -29,48 +29,32 @@ extern "C" { * errno to one of the error values defined in errno.h upon failure. The way * of notifying the caller that an error occurred varies between functions, * but in general a function that returns an int, returns -1 on error, while - * a function returning a pointer bails out on error condition by returning + * a function returning a pointer indicates an error condition by returning * a NULL pointer. + * + * <p>In general libgpiod functions are not NULL-aware and it's expected that + * users pass valid pointers to objects as arguments. + * + * <p>Almost all opaque objects are reference counted. The associated resources + * are released when the reference count reaches 0. */ struct gpiod_chip; -struct gpiod_line; struct gpiod_line_info; -struct gpiod_line_bulk; +struct gpiod_line_attr; +struct gpiod_line_config; +struct gpiod_request_config; +struct gpiod_request_handle; struct gpiod_line_event; struct gpiod_line_event_buffer; /** - * @defgroup common Common helper macros - * @{ - * - * Commonly used utility macros. - */ - -/** - * @brief Shift 1 by given offset. - * @param nr Bit position. - * @return 1 shifted by nr. - */ -#define GPIOD_BIT(nr) (1UL << (nr)) - -/** - * @} - * - * @defgroup chips GPIO chip operations + * @defgroup chips GPIO chips * @{ * - * Functions and data structures dealing with GPIO chips. + * Functions and data structures for manipulating GPIO chips. */ -/** - * @brief Check if the file pointed to by path is a GPIO chip character device. - * @param path Path to check. - * @return True if the file exists and is a GPIO chip character device or a - * symbolic link to it. - */ -bool gpiod_is_gpiochip_device(const char *path); - /** * @brief Open a gpiochip by path. * @param path Path to the gpiochip device file. @@ -80,7 +64,7 @@ struct gpiod_chip *gpiod_chip_open(const char *path); /** * @brief Increase the refcount on this GPIO object. - * @param chip The GPIO chip object. + * @param chip GPIO chip object. * @return Passed reference to the GPIO chip. */ struct gpiod_chip *gpiod_chip_ref(struct gpiod_chip *chip); @@ -88,27 +72,27 @@ struct gpiod_chip *gpiod_chip_ref(struct gpiod_chip *chip); /** * @brief Decrease the refcount on this GPIO object. If the refcount reaches 0, * close the chip device and free all associated resources. - * @param chip The GPIO chip object. + * @param chip GPIO chip object. */ void gpiod_chip_unref(struct gpiod_chip *chip); /** * @brief Get the GPIO chip name as represented in the kernel. - * @param chip The GPIO chip object. + * @param chip GPIO chip object. * @return Pointer to a human-readable string containing the chip name. */ const char *gpiod_chip_get_name(struct gpiod_chip *chip); /** * @brief Get the GPIO chip label as represented in the kernel. - * @param chip The GPIO chip object. + * @param chip GPIO chip object. * @return Pointer to a human-readable string containing the chip label. */ const char *gpiod_chip_get_label(struct gpiod_chip *chip); /** * @brief Get the number of GPIO lines exposed by this chip. - * @param chip The GPIO chip object. + * @param chip GPIO chip object. * @return Number of GPIO lines. */ unsigned int gpiod_chip_get_num_lines(struct gpiod_chip *chip); @@ -116,143 +100,34 @@ unsigned int gpiod_chip_get_num_lines(struct gpiod_chip *chip); /** * @brief Get the current snapshot of information about the line at given * offset. - * @param chip The GPIO chip object. + * @param chip GPIO chip object. * @param offset The offset of the GPIO line. - * @return New GPIO line info object that must be freed using - * ::gpiod_line_info_free or NULL if an error occurred. + * @return New GPIO line info object or NULL if an error occurred. */ struct gpiod_line_info *gpiod_chip_get_line_info(struct gpiod_chip *chip, unsigned int offset); -/** - * @brief Get the handle to the GPIO line at given offset. - * @param chip The GPIO chip object. - * @param offset The offset of the GPIO line. - * @return Pointer to the GPIO line handle or NULL if an error occured. - */ -struct gpiod_line * -gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset); - -/** - * @brief Retrieve a set of lines and store them in a line bulk object. - * @param chip The GPIO chip object. - * @param offsets Array of offsets of lines to retrieve. - * @param num_offsets Number of lines to retrieve. - * @return New line bulk object or NULL on error. - */ -struct gpiod_line_bulk * -gpiod_chip_get_lines(struct gpiod_chip *chip, unsigned int *offsets, - unsigned int num_offsets); - -/** - * @brief Retrieve all lines exposed by a chip and store them in a bulk object. - * @param chip The GPIO chip object. - * @return New line bulk object or NULL on error. - */ -struct gpiod_line_bulk * -gpiod_chip_get_all_lines(struct gpiod_chip *chip); - /** * @brief Map a GPIO line's name to its offset within the chip. - * @param chip The GPIO chip object. + * @param chip GPIO chip object. * @param name Name of the GPIO line to map. - * @return Offset of the line within the chip or -1 if a line with given name - * is not exposed by the chip. + * @return Offset of the line within the chip or -1 on error. + * @note If a line with given name is not exposed by the chip, the function + * sets errno to ENOENT. */ int gpiod_chip_find_line(struct gpiod_chip *chip, const char *name); /** - * @} - * - * @defgroup lines GPIO line operations - * @{ - * - * Functions and data structures dealing with GPIO lines. - * - * @defgroup line_bulk Operating on multiple lines - * @{ - * - * Convenience data structures and helper functions for storing and operating - * on multiple lines at once. - */ - -/** - * @brief Allocate and initialize a new line bulk object. - * @param max_lines Maximum number of lines this object can hold. - * @return New line bulk object or NULL on error. - */ -struct gpiod_line_bulk *gpiod_line_bulk_new(unsigned int max_lines); - -/** - * @brief Reset a bulk object. Remove all lines and set size to 0. - * @param bulk Bulk object to reset. - */ -void gpiod_line_bulk_reset(struct gpiod_line_bulk *bulk); - -/** - * @brief Release all resources allocated for this bulk object. - * @param bulk Bulk object to free. - */ -void gpiod_line_bulk_free(struct gpiod_line_bulk *bulk); - -/** - * @brief Add a single line to a GPIO bulk object. - * @param bulk Line bulk object. - * @param line Line to add. - * @return 0 on success, -1 on error. - * @note The line is added at the next free bulk index. - * - * The function can fail if this bulk already holds its maximum amount of - * lines or if the added line is associated with a different chip than all - * the other lines already held by this object. - */ -int gpiod_line_bulk_add_line(struct gpiod_line_bulk *bulk, - struct gpiod_line *line); - -/** - * @brief Retrieve the line handle from a line bulk object at given index. - * @param bulk Line bulk object. - * @param index Index of the line to retrieve. - * @return Line handle at given index or NULL if index is greater or equal to - * the number of lines this bulk can hold. - */ -struct gpiod_line * -gpiod_line_bulk_get_line(struct gpiod_line_bulk *bulk, unsigned int index); - -/** - * @brief Retrieve the number of GPIO lines held by this line bulk object. - * @param bulk Line bulk object. - * @return Number of lines held by this line bulk. - */ -unsigned int gpiod_line_bulk_num_lines(struct gpiod_line_bulk *bulk); - -/** - * @brief Values returned by the callback passed to - * ::gpiod_line_bulk_foreach_line. + * @brief Request a set of lines for exclusive usage. + * @param chip GPIO chip object. + * @param req_cfg Request config object. + * @param line_cfg Line config object. + * @return New request handle object or NULL if an error occurred. */ -enum { - /**< Continue the loop. */ - GPIOD_LINE_BULK_CB_NEXT = 0, - /**< Stop the loop. */ - GPIOD_LINE_BULK_CB_STOP, -}; - -/** - * @brief Signature of the callback passed to ::gpiod_line_bulk_foreach_line. - * - * Takes the current line and additional user data as arguments. - */ -typedef int (*gpiod_line_bulk_foreach_cb)(struct gpiod_line *, void *); - -/** - * @brief Iterate over all lines held by this bulk object. - * @param bulk Bulk object to iterate over. - * @param func Callback to be called for each line. - * @param data User data pointer that is passed to the callback. - */ -void gpiod_line_bulk_foreach_line(struct gpiod_line_bulk *bulk, - gpiod_line_bulk_foreach_cb func, - void *data); +struct gpiod_request_handle * +gpiod_chip_request_lines(struct gpiod_chip *chip, + struct gpiod_request_config *req_cfg, + struct gpiod_line_config *line_cfg); /** * @} @@ -314,13 +189,6 @@ enum { /**< Line detects both rising and falling edge events. */ }; -/** - * @brief Read the GPIO line offset. - * @param line GPIO line object. - * @return Line offset. - */ -unsigned int gpiod_line_offset(struct gpiod_line *line); - /** * @brief Increase the reference count for this line info object. * @param info GPIO line info object. @@ -428,485 +296,460 @@ unsigned long gpiod_line_get_debounce_period(struct gpiod_line_info *info); /** - * @brief Get the handle to the GPIO chip controlling this line. - * @param line The GPIO line object. - * @return Pointer to the GPIO chip handle controlling this line. + * @} + * + * @defgroup line_mask Line mask bitmaps + * @{ + */ + +/** + * @brief Bitmask used to store values of lines in a single request (up to 64 + * lines) as well as to specify on which lines in a request to act when + * reading/setting values or applying configuration options. + */ +typedef uint64_t gpiod_line_mask; + +/** + * @brief Set all bits in the mask. + * @param mask The GPIO line mask. + */ +void gpiod_line_mask_fill(gpiod_line_mask *mask); + +/** + * @brief Clear all bits in the mask. + * @param mask The GPIO line mask. + */ +void gpiod_line_mask_zero(gpiod_line_mask *mask); + +/** + * @brief Test a single bit. + * @param mask The GPIO line mask. + * @param offset Offset of the bit to test. + * @return True if the bit is set, false otherwise. + */ +bool gpiod_line_mask_test_bit(gpiod_line_mask mask, unsigned int offset); + +/** + * @brief Set a single bit. + * @param mask The GPIO line mask. + * @param offset Offset of the bit to set. + */ +void gpiod_line_mask_set_bit(gpiod_line_mask *mask, unsigned int offset); + +/** + * @brief Clear a single bit. + * @param mask The GPIO line mask. + * @param offset Offset of the bit to clear. + */ +void gpiod_line_mask_clear_bit(gpiod_line_mask *mask, unsigned int offset); + +/** + * @brief Set or clear a single bit depending on a boolean value. + * @param mask The GPIO line mask. + * @param offset Offset of the bit to assign. + * @param value Boolean value to indicate the value of the bit. */ -struct gpiod_chip *gpiod_line_get_chip(struct gpiod_line *line); +void gpiod_line_mask_assign_bit(gpiod_line_mask *mask, + unsigned int offset, bool value); /** * @} * - * @defgroup line_request Line requests + * @defgroup line_config Line and request configuration objects * @{ * - * Interface for requesting GPIO lines from userspace for both values and - * events. + * Functions for manipulating line attributes and configuration objects. */ /** * @brief Available types of requests. */ enum { - GPIOD_LINE_REQUEST_DIRECTION_AS_IS = 1, + GPIOD_LINE_CONFIG_DIRECTION_AS_IS = 1, /**< Request the line(s), but don't change current direction. */ - GPIOD_LINE_REQUEST_DIRECTION_INPUT, + GPIOD_LINE_CONFIG_DIRECTION_INPUT, /**< Request the line(s) for reading the GPIO line state. */ - GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, + GPIOD_LINE_CONFIG_DIRECTION_OUTPUT, /**< Request the line(s) for setting the GPIO line state. */ - GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE, + GPIOD_LINE_CONFIG_EVENT_FALLING_EDGE, /**< Only watch falling edge events. */ - GPIOD_LINE_REQUEST_EVENT_RISING_EDGE, + GPIOD_LINE_CONFIG_EVENT_RISING_EDGE, /**< Only watch rising edge events. */ - GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES, + GPIOD_LINE_CONFIG_EVENT_BOTH_EDGES, /**< Monitor both types of events. */ }; /** - * @brief Miscellaneous GPIO request flags. + * @brief Available drive settings for line requests. */ enum { - GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN = GPIOD_BIT(0), - /**< The line is an open-drain port. */ - GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE = GPIOD_BIT(1), - /**< The line is an open-source port. */ - GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW = GPIOD_BIT(2), - /**< The active state of the line is low (high is the default). */ - GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLED = GPIOD_BIT(3), - /**< The line has neither either pull-up nor pull-down resistor. */ - GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN = GPIOD_BIT(4), - /**< The line has pull-down resistor enabled. */ - GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP = GPIOD_BIT(5), - /**< The line has pull-up resistor enabled. */ + GPIOD_LINE_CONFIG_DRIVE_PUSH_PULL = 1, + /**< Drive setting should be set to push-pull (the default). */ + GPIOD_LINE_CONFIG_DRIVE_OPEN_DRAIN, + /**< Line output should be set to open-drain. */ + GPIOD_LINE_CONFIG_DRIVE_OPEN_SOURCE, + /**< Line output should be set to open-source. */ }; /** - * @brief Structure holding configuration of a line request. + * @brief Available internal bias settings for line requests. */ -struct gpiod_line_request_config { - const char *consumer; - /**< Name of the consumer. */ - int request_type; - /**< Request type. */ - int flags; - /**< Other configuration flags. */ +enum { + GPIOD_LINE_CONFIG_BIAS_DISABLED = 1, + /**< The internal bias should be disabled (the default). */ + GPIOD_LINE_CONFIG_BIAS_PULL_UP, + /**< The internal pull-up bias is enabled. */ + GPIOD_LINE_CONFIG_BIAS_PULL_DOWN, + /**< The internal pull-down bias is enabled. */ }; /** - * @brief Reserve a single line. - * @param line GPIO line object. - * @param config Request options. - * @param default_val Initial line value - only relevant if we're setting - * the direction to output. - * @return 0 if the line was properly reserved. In case of an error this - * routine returns -1 and sets the last error number. - * - * If this routine succeeds, the caller takes ownership of the GPIO line until - * it's released. + * @brief Available line attribute types. */ -int gpiod_line_request(struct gpiod_line *line, - const struct gpiod_line_request_config *config, - int default_val); +enum { + GPIOD_LINE_ATTR_TYPE_OPTS, + /**< The attribute specifies a set of simple line options. */ + GPIOD_LINE_ATTR_TYPE_OUTPUT_VALUES, + /**< The attribute contains output values for a set of GPIO lines. */ + GPIOD_LINE_ATTR_TYPE_DEBOUNCE, + /**< The attribute defines debounce config. */ +}; /** - * @brief Reserve a single line, set the direction to input. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @return 0 if the line was properly reserved, -1 on failure. + * @brief Create a new line attribute. + * @param type Type of this attribute. + * @return New line attribute or NULL on error. */ -int gpiod_line_request_input(struct gpiod_line *line, const char *consumer); +struct gpiod_line_attr *gpiod_line_attr_new(int type); /** - * @brief Reserve a single line, set the direction to output. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @param default_val Initial line value. - * @return 0 if the line was properly reserved, -1 on failure. + * @brief Increase the reference count of this object. + * @param attr Line attribute object. + * @return Passed reference to the line attribute. */ -int gpiod_line_request_output(struct gpiod_line *line, - const char *consumer, int default_val); +struct gpiod_line_attr *gpiod_line_attr_ref(struct gpiod_line_attr *attr); /** - * @brief Request rising edge event notifications on a single line. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Decrease the reference count to the line attribute. If the refcount + * reaches 0, free all resources associated with this object. + * @param attr Line attribute object. */ -int gpiod_line_request_rising_edge_events(struct gpiod_line *line, - const char *consumer); +void gpiod_line_attr_unref(struct gpiod_line_attr *attr); /** - * @brief Request falling edge event notifications on a single line. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Set the line mask of this attribute. + * @param attr Line attribute object. + * @param mask New line mask. + * + * An attribute can be associated with up to 64 lines (the maximum for a single + * line request). Each bit in the mask bitmap represents a single line from the + * request at the same offset as the bit. If the bit is set, the configuration + * specified in this attribute will apply to the associated line. */ -int gpiod_line_request_falling_edge_events(struct gpiod_line *line, - const char *consumer); +void gpiod_line_attr_set_line_mask(struct gpiod_line_attr *attr, + gpiod_line_mask mask); /** - * @brief Request all event type notifications on a single line. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Set the request type. + * @param attr Line attribute object. + * @param request_type New request type. + * @return 0 on success, -1 on error. + * @note Can only be used with attributes of type ::GPIOD_LINE_ATTR_TYPE_OPTS. */ -int gpiod_line_request_both_edges_events(struct gpiod_line *line, - const char *consumer); +int gpiod_line_attr_set_request_type(struct gpiod_line_attr *attr, + int request_type); /** - * @brief Reserve a single line, set the direction to input. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @param flags Additional request flags. - * @return 0 if the line was properly reserved, -1 on failure. + * @brief Set the drive setting. + * @param attr Line attribute object. + * @param drive New drive setting. + * @return 0 on success, -1 on error. + * @note Can only be used with attributes of type ::GPIOD_LINE_ATTR_TYPE_OPTS. */ -int gpiod_line_request_input_flags(struct gpiod_line *line, - const char *consumer, int flags); +int gpiod_line_attr_set_drive(struct gpiod_line_attr *attr, int drive); /** - * @brief Reserve a single line, set the direction to output. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @param flags Additional request flags. - * @param default_val Initial line value. - * @return 0 if the line was properly reserved, -1 on failure. + * @brief Set the bias setting. + * @param attr Line attribute object. + * @param bias New bias setting. + * @return 0 on success, -1 on error. + * @note Can only be used with attributes of type ::GPIOD_LINE_ATTR_TYPE_OPTS. */ -int gpiod_line_request_output_flags(struct gpiod_line *line, - const char *consumer, int flags, - int default_val); +int gpiod_line_attr_set_bias(struct gpiod_line_attr *attr, int bias); /** - * @brief Request rising edge event notifications on a single line. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @param flags Additional request flags. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Set the active-low setting. + * @param attr Line attribute object. + * @param active_low New active-low setting. + * @return 0 on success, -1 on error. + * @note Can only be used with attributes of type ::GPIOD_LINE_ATTR_TYPE_OPTS. */ -int gpiod_line_request_rising_edge_events_flags(struct gpiod_line *line, - const char *consumer, - int flags); +int gpiod_line_attr_set_active_low(struct gpiod_line_attr *attr, + bool active_low); /** - * @brief Request falling edge event notifications on a single line. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @param flags Additional request flags. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Set the realtime clock setting. + * @param attr Line attribute object. + * @param clock_realtime New realtime clock setting. + * @return 0 on success, -1 on error. + * @note Can only be used with attributes of type ::GPIOD_LINE_ATTR_TYPE_OPTS. + * + * This option can be used to tell the kernel to get the line event timestamps + * from the realtime clock, not the monotonic clock which is the default. */ -int gpiod_line_request_falling_edge_events_flags(struct gpiod_line *line, - const char *consumer, - int flags); +int gpiod_line_attr_set_clock_realtime(struct gpiod_line_attr *attr, + bool clock_realtime); /** - * @brief Request all event type notifications on a single line. - * @param line GPIO line object. - * @param consumer Name of the consumer. - * @param flags Additional request flags. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Set the debounce config. + * @param attr Line attribute object. + * @param debounce Activate or de-activate debouncing. + * @param debounce_period New debounce period in microseconds. + * @return 0 on success, -1 on error. + * @note Can only be used with attributes of type + * ::GPIOD_LINE_ATTR_TYPE_DEBOUNCE. + * */ -int gpiod_line_request_both_edges_events_flags(struct gpiod_line *line, - const char *consumer, - int flags); +int gpiod_line_attr_set_debounce(struct gpiod_line_attr *attr, bool debounce, + unsigned long debounce_period); /** - * @brief Reserve a set of GPIO lines. - * @param bulk Set of GPIO lines to reserve. - * @param config Request options. - * @param default_vals Initial line values - only relevant if we're setting - * the direction to output. - * @return 0 if all lines were properly requested. In case of an error - * this routine returns -1 and sets the last error number. + * @brief Set the output values carried by this attribute. + * @param attr Line attribute object. + * @param values GPIO line mask specifing the output values. + * @return 0 on success + * @note Can only be used with attributes of type + * ::GPIOD_LINE_ATTR_TYPE_OUTPUT_VALUES. * - * If this routine succeeds, the caller takes ownership of the GPIO lines - * until they're released. All the requested lines must be provided by the - * same gpiochip. + * Each bit in the values mask is associated with a line in a request handle. + * If the bit is set, the line should be driven high, if the bit is cleared, + * the line should be driven low. */ -int gpiod_line_request_bulk(struct gpiod_line_bulk *bulk, - const struct gpiod_line_request_config *config, - const int *default_vals); +int gpiod_line_attr_set_output_values(struct gpiod_line_attr *attr, + gpiod_line_mask values); /** - * @brief Reserve a set of GPIO lines, set the direction to input. - * @param bulk Set of GPIO lines to reserve. - * @param consumer Name of the consumer. - * @return 0 if the lines were properly reserved, -1 on failure. + * @brief Create a new line config object. + * @return New line config object or NULL on error. */ -int gpiod_line_request_bulk_input(struct gpiod_line_bulk *bulk, - const char *consumer); +struct gpiod_line_config *gpiod_line_config_new(void); /** - * @brief Reserve a set of GPIO lines, set the direction to output. - * @param bulk Set of GPIO lines to reserve. - * @param consumer Name of the consumer. - * @param default_vals Initial line values. - * @return 0 if the lines were properly reserved, -1 on failure. + * @brief Increase the reference count of this line config object. + * @param config Line config object. + * @return Passed reference to the line config object. */ -int gpiod_line_request_bulk_output(struct gpiod_line_bulk *bulk, - const char *consumer, - const int *default_vals); +struct gpiod_line_config * +gpiod_line_config_ref(struct gpiod_line_config *config); /** - * @brief Request rising edge event notifications on a set of lines. - * @param bulk Set of GPIO lines to request. - * @param consumer Name of the consumer. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Decrease the reference count of this line config object. If the + * reference count reaches 0, free all associated resources. + * @param config Line config object. */ -int gpiod_line_request_bulk_rising_edge_events(struct gpiod_line_bulk *bulk, - const char *consumer); +void gpiod_line_config_unref(struct gpiod_line_config *config); /** - * @brief Request falling edge event notifications on a set of lines. - * @param bulk Set of GPIO lines to request. - * @param consumer Name of the consumer. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Add a new line attribute to this line config. + * @param config Line config object. + * @param attr Line attribute to add. + * @return 0 on success, -1 on failure. */ -int gpiod_line_request_bulk_falling_edge_events(struct gpiod_line_bulk *bulk, - const char *consumer); +int gpiod_line_config_add_attribute(struct gpiod_line_config *config, + struct gpiod_line_attr *attr); /** - * @brief Request all event type notifications on a set of lines. - * @param bulk Set of GPIO lines to request. - * @param consumer Name of the consumer. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Remove all attributes from this config. + * @param config Line config. */ -int gpiod_line_request_bulk_both_edges_events(struct gpiod_line_bulk *bulk, - const char *consumer); +void gpiod_line_config_clear_attrs(struct gpiod_line_config *config); /** - * @brief Reserve a set of GPIO lines, set the direction to input. - * @param bulk Set of GPIO lines to reserve. - * @param consumer Name of the consumer. - * @param flags Additional request flags. - * @return 0 if the lines were properly reserved, -1 on failure. + * @brief Set the request type. + * @param config Line config. + * @param request_type New request type. + * @return 0 on success, -1 on error. */ -int gpiod_line_request_bulk_input_flags(struct gpiod_line_bulk *bulk, - const char *consumer, int flags); +int gpiod_line_config_set_request_type(struct gpiod_line_config *config, + int request_type); /** - * @brief Reserve a set of GPIO lines, set the direction to output. - * @param bulk Set of GPIO lines to reserve. - * @param consumer Name of the consumer. - * @param flags Additional request flags. - * @param default_vals Initial line values. - * @return 0 if the lines were properly reserved, -1 on failure. + * @brief Set the drive setting. + * @param config Line config. + * @param drive New drive setting. + * @return 0 on success, -1 on error. */ -int gpiod_line_request_bulk_output_flags(struct gpiod_line_bulk *bulk, - const char *consumer, int flags, - const int *default_vals); +int gpiod_line_config_set_drive(struct gpiod_line_config *config, int drive); /** - * @brief Request rising edge event notifications on a set of lines. - * @param bulk Set of GPIO lines to request. - * @param consumer Name of the consumer. - * @param flags Additional request flags. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Set the bias setting. + * @param config Line config. + * @param bias New bias setting. + * @return 0 on success, -1 on error. */ -int gpiod_line_request_bulk_rising_edge_events_flags( - struct gpiod_line_bulk *bulk, - const char *consumer, int flags); +int gpiod_line_config_set_bias(struct gpiod_line_config *config, int bias); /** - * @brief Request falling edge event notifications on a set of lines. - * @param bulk Set of GPIO lines to request. - * @param consumer Name of the consumer. - * @param flags Additional request flags. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Set the active-low setting. + * @param config Line config. + * @param active_low New active-low setting. */ -int gpiod_line_request_bulk_falling_edge_events_flags( - struct gpiod_line_bulk *bulk, - const char *consumer, int flags); +void gpiod_line_config_set_active_low(struct gpiod_line_config *config, + bool active_low); /** - * @brief Request all event type notifications on a set of lines. - * @param bulk Set of GPIO lines to request. - * @param consumer Name of the consumer. - * @param flags Additional request flags. - * @return 0 if the operation succeeds, -1 on failure. + * @brief Set the realtime clock setting. + * @param config Line config. + * @param clock_realtime New realtime clock setting. + * + * This option can be used to tell the kernel to get the line event timestamps + * from the realtime clock, not the monotonic clock which is the default. */ -int gpiod_line_request_bulk_both_edges_events_flags( - struct gpiod_line_bulk *bulk, - const char *consumer, int flags); +void +gpiod_line_config_set_event_clock_realtime(struct gpiod_line_config *config, + bool clock_realtime); /** - * @brief Release a previously reserved line. - * @param line GPIO line object. + * @brief Create a new request config object. + * @return New request config object or NULL on error. */ -void gpiod_line_release(struct gpiod_line *line); +struct gpiod_request_config *gpiod_request_config_new(void); /** - * @brief Release a set of previously reserved lines. - * @param bulk Set of GPIO lines to release. - * - * If the lines were not previously requested together, the behavior is - * undefined. + * @brief Increase the reference count of this request config object. + * @param config Request config object. + * @return Passed reference to this request config. */ -void gpiod_line_release_bulk(struct gpiod_line_bulk *bulk); +struct gpiod_request_config * +gpiod_request_config_ref(struct gpiod_request_config *config); /** - * @} - * - * @defgroup line_value Reading & setting line values - * @{ - * - * Functions allowing to read and set GPIO line values for single lines and - * in bulk. + * @brief Decrease the reference count of this request config object. If the + * reference count reaches 0, free all associated resources. + * @param config Request config object. */ +void gpiod_request_config_unref(struct gpiod_request_config *config); /** - * @brief Read current value of a single GPIO line. - * @param line GPIO line object. - * @return 0 or 1 if the operation succeeds. On error this routine returns -1 - * and sets the last error number. + * @brief Set the consumer string. + * @param config Request config object. + * @param consumer Consumer name. + * @return 0 on success, -1 on failure. */ -int gpiod_line_get_value(struct gpiod_line *line); +int gpiod_request_config_set_consumer(struct gpiod_request_config *config, + const char *consumer); /** - * @brief Read current values of a set of GPIO lines. - * @param bulk Set of GPIO lines to reserve. - * @param values An array big enough to hold line_bulk->num_lines values. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - * - * If succeeds, this routine fills the values array with a set of values in - * the same order, the lines are added to line_bulk. If the lines were not - * previously requested together, the behavior is undefined. + * @brief Set line offsets for this request. + * @param config Request config object. + * @param num_lines Number of offsets. + * @param offsets Array of line offsets. + * @return 0 on success, -1 on failure. */ -int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, int *values); +int gpiod_request_config_set_offsets(struct gpiod_request_config *config, + unsigned int num_lines, + unsigned int *offsets); /** - * @brief Set the value of a single GPIO line. - * @param line GPIO line object. - * @param value New value. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. + * @brief Set the size of the kernel event buffer. + * @param config Request config object. + * @param event_buffer_size New event buffer size. + * @return 0 on success, -1 on failure. */ -int gpiod_line_set_value(struct gpiod_line *line, int value); +int +gpiod_request_config_set_event_buffer_size(struct gpiod_request_config *config, + unsigned int event_buffer_size); /** - * @brief Set the values of a set of GPIO lines. - * @param bulk Set of GPIO lines to reserve. - * @param values An array holding line_bulk->num_lines new values for lines. - * A NULL pointer is interpreted as a logical low for all lines. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. + * @} * - * If the lines were not previously requested together, the behavior is - * undefined. + * @defgroup request_handle Line request handle operations + * @{ */ -int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, const int *values); /** - * @} - * - * @defgroup line_config Setting line configuration - * @{ - * - * Functions allowing modification of config options of GPIO lines requested - * from user-space. - */ - -/** - * @brief Update the configuration of a single GPIO line. - * @param line GPIO line object. - * @param direction Updated direction which may be one of - * GPIOD_LINE_REQUEST_DIRECTION_AS_IS, - * GPIOD_LINE_REQUEST_DIRECTION_INPUT, or - * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT. - * @param flags Replacement flags. - * @param value The new output value for the line when direction is - * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - */ -int gpiod_line_set_config(struct gpiod_line *line, int direction, - int flags, int value); - -/** - * @brief Update the configuration of a set of GPIO lines. - * @param bulk Set of GPIO lines. - * @param direction Updated direction which may be one of - * GPIOD_LINE_REQUEST_DIRECTION_AS_IS, - * GPIOD_LINE_REQUEST_DIRECTION_INPUT, or - * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT. - * @param flags Replacement flags. - * @param values An array holding line_bulk->num_lines new logical values - * for lines when direction is - * GPIOD_LINE_REQUEST_DIRECTION_OUTPUT. - * A NULL pointer is interpreted as a logical low for all lines. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - * - * If the lines were not previously requested together, the behavior is - * undefined. + * @brief Increase the reference count of this request handle. + * @param handle GPIO request handle. + * @return Passed reference to this request handle. */ -int gpiod_line_set_config_bulk(struct gpiod_line_bulk *bulk, - int direction, int flags, const int *values); +struct gpiod_request_handle * +gpiod_request_handle_ref(struct gpiod_request_handle *handle); +/** + * @brief Decrease the reference count of this request handle. If the reference + * count reaches 0, close the associated file descriptor and release + * all allocated resources. + * @param handle GPIO request handle. + */ +void gpiod_request_handle_unref(struct gpiod_request_handle *handle); /** - * @brief Update the configuration flags of a single GPIO line. - * @param line GPIO line object. - * @param flags Replacement flags. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. + * @brief Read values of lines associated with this request handle. + * @param handle GPIO request handle. + * @param values Address of the bitmap to store values in. + * @param mask Mask of the lines to read values from. + * @return 0 on success, -1 on failure. + * @note The bits in the values and mask bitmaps represent the lines associated + * with this request handle at the same offsets. Only values of lines + * for which the relevant bits in mask are set will be read. */ -int gpiod_line_set_flags(struct gpiod_line *line, int flags); +int gpiod_request_handle_get_values(struct gpiod_request_handle *handle, + gpiod_line_mask *values, + gpiod_line_mask mask); /** - * @brief Update the configuration flags of a set of GPIO lines. - * @param bulk Set of GPIO lines. - * @param flags Replacement flags. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - * - * If the lines were not previously requested together, the behavior is - * undefined. + * @brief Set values of lines associated with this request handle. + * @param handle GPIO request handle. + * @param values Mask of line values to set. + * @param mask Mask of the lines to set values for. + * @return 0 on success, -1 on failure. + * @note The bits in the values and mask bitmaps represent the lines associated + * with this request handle at the same offsets. Only values of lines + * for which the relevant bits in mask are set will be modified. */ -int gpiod_line_set_flags_bulk(struct gpiod_line_bulk *bulk, int flags); +int gpiod_request_handle_set_values(struct gpiod_request_handle *handle, + gpiod_line_mask values, + gpiod_line_mask mask); /** - * @brief Set the direction of a single GPIO line to input. - * @param line GPIO line object. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. + * @brief Update the configuration of lines associated with this request handle. + * @param handle GPIO request handle. + * @param config New line config to apply. + * @return 0 on success, -1 on failure. */ -int gpiod_line_set_direction_input(struct gpiod_line *line); +int gpiod_request_handle_set_config(struct gpiod_request_handle *handle, + struct gpiod_line_config *config); /** - * @brief Set the direction of a set of GPIO lines to input. - * @param bulk Set of GPIO lines. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - * - * If the lines were not previously requested together, the behavior is - * undefined. + * @brief Get the file descriptor associated with this request handle. + * @param handle GPIO request handle. + * @return Number of the file descriptor associated with this request. This + * function never fails. */ -int -gpiod_line_set_direction_input_bulk(struct gpiod_line_bulk *bulk); +int gpiod_request_handle_get_fd(struct gpiod_request_handle *handle); /** - * @brief Set the direction of a single GPIO line to output. - * @param line GPIO line object. - * @param value The logical value output on the line. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. + * @brief Wait for line events on any of the lines associated with this handle. + * @param handle GPIO request handle. + * @param timeout Wait time limit in nanoseconds. + * @return 0 if wait timed out, -1 if an error occurred, 1 if an event is + * pending. */ -int gpiod_line_set_direction_output(struct gpiod_line *line, int value); +int gpiod_request_handle_event_wait(struct gpiod_request_handle *handle, + uint64_t timeout); /** - * @brief Set the direction of a set of GPIO lines to output. - * @param bulk Set of GPIO lines. - * @param values An array holding line_bulk->num_lines new logical values - * for lines. A NULL pointer is interpreted as a logical low - * for all lines. - * @return 0 is the operation succeeds. In case of an error this routine - * returns -1 and sets the last error number. - * - * If the lines were not previously requested together, the behavior is - * undefined. + * @brief Read a number of line events from a request handle. + * @param handle GPIO request handle. + * @param buf Line event buffer. + * @param max_events Maximum number of events to read. + * @return On success returns the number of events read from the file + * descriptor, on failure return -1. + * @note This function will block if no event was queued for this line. */ -int gpiod_line_set_direction_output_bulk(struct gpiod_line_bulk *bulk, - const int *values); +int gpiod_request_handle_event_read(struct gpiod_request_handle *handle, + struct gpiod_line_event_buffer *buf, + unsigned int max_events); /** * @} @@ -930,120 +773,133 @@ enum { /**< Falling edge event. */ }; +/** + * @brief Increase the reference count of this line event. + * @param event GPIO line event. + * @return Passed reference to the event object. + * @note Must not be used with events held by the event buffer (retrieved + * using ::gpiod_line_event_buffer_get_event. Only event objects + * created by ::gpiod_line_event_buffer_copy_event are refcounted. + */ struct gpiod_line_event *gpiod_line_event_ref(struct gpiod_line_event *event); +/** + * @brief Decrease the reference count of this line event. If the reference + * count reaches zero, free all associated resources. + * @param event GPIO line event. + */ void gpiod_line_event_unref(struct gpiod_line_event *event); +/** + * @brief Get the event type. + * @param event GPIO line event. + * @return The event type (::GPIOD_LINE_EVENT_RISING_EDGE or + * ::GPIOD_LINE_EVENT_FALLING_EDGE). + */ int gpiod_line_event_get_event_type(struct gpiod_line_event *event); +/** + * @brief Get the timestamp of the event. + * @param event GPIO line event. + * @return Timestamp in nanoseconds. + */ uint64_t gpiod_line_event_get_timestamp(struct gpiod_line_event *event); +/** + * @brief Get the hardware offset of the line on which the event was triggered. + * @param event GPIO line event. + * @return Line offset. + */ unsigned int gpiod_line_event_get_line_offset(struct gpiod_line_event *event); +/** + * @brief Get the global sequence number of this event. + * @param event GPIO line event. + * @return Sequence number of the event relative to all lines in the associated + * line request. + */ unsigned int gpiod_line_event_get_global_seqno(struct gpiod_line_event *event); +/** + * @brief Get the event sequence number specific to this line. + * @param event GPIO line event. + * @return Sequence number of the event relative to this line within the + * lifetime of the associated line request. + */ unsigned int gpiod_line_event_get_line_seqno(struct gpiod_line_event *event); +/** + * @brief Create a new line event buffer. + * @param capacity Number of events this buffer can store (min = 1, max = 1024). + * @return New line event buffer or NULL on error. + */ struct gpiod_line_event_buffer * gpiod_line_event_buffer_new(unsigned int capacity); +/** + * @brief Increase the reference count of this event buffer. + * @param buf Line event buffer. + * @return Passed reference to the event buffer. + */ struct gpiod_line_event_buffer * gpiod_line_event_buffer_ref(struct gpiod_line_event_buffer *buf); +/** + * @brief Decrease the reference count of this event buffer. If the reference + * count reaches 0, free all associated resources. + * @param buf Line event buffer. + */ void gpiod_line_event_buffer_unref(struct gpiod_line_event_buffer *buf); +/** + * @brief Get a weak reference to an event in the buffer. + * @param buf Line event buffer. + * @param index Index of the event in the buffer. + * @return Pointer to an event stored in the buffer. The lifetime of this + * event is tied to the buffer object. Users must not call event + * reference counting functions on pointers retrieved using this + * function. + */ struct gpiod_line_event * gpiod_line_event_buffer_get_event(struct gpiod_line_event_buffer *buf, unsigned long index); +/** + * @brief Get a copy of a line event. + * @param buf Line event buffer. + * @param index Index of the event in the buffer. + * @return Returns a copy of the line event stored in this buffer. The event's + * lifetime is managed by the caller. The event's reference count must + * be decreases using ::gpiod_line_event_unref. + */ struct gpiod_line_event * gpiod_line_event_buffer_copy_event(struct gpiod_line_event_buffer *buf, unsigned long index); /** - * @brief Wait for an event on a single line. - * @param line GPIO line object. - * @param timeout Wait time limit. - * @return 0 if wait timed out, -1 if an error occurred, 1 if an event - * occurred. - */ -int gpiod_line_event_wait(struct gpiod_line *line, uint64_t timeout); - -/** - * @brief Wait for events on a set of lines. - * @param bulk Set of GPIO lines to monitor. - * @param timeout Wait time limit. - * @param event_bulk Bulk object in which to store the line handles on which - * events occurred. Can be NULL. - * @return 0 if wait timed out, -1 if an error occurred, 1 if at least one - * event occurred. - */ -int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk, uint64_t timeout, - struct gpiod_line_bulk *event_bulk); - -/** - * @brief Read next pending event from the GPIO line. - * @param line GPIO line object. - * @param event Buffer to which the event data will be copied. - * @return 0 if the event was read correctly, -1 on error. - * @note This function will block if no event was queued for this line. - */ -int gpiod_line_event_read(struct gpiod_line *line, - struct gpiod_line_event_buffer *buf); - -/** - * @brief Read up to a certain number of events from the GPIO line. - * @param line GPIO line object. - * @param events Buffer to which the event data will be copied. Must hold at - * least the amount of events specified in num_events. - * @param num_events Specifies how many events can be stored in the buffer. - * @return On success returns the number of events stored in the buffer, on - * failure -1 is returned. - */ -int gpiod_line_event_read_multiple(struct gpiod_line *line, - struct gpiod_line_event_buffer *buf, - unsigned int num_events); - -/** - * @brief Get the event file descriptor. - * @param line GPIO line object. - * @return Number of the event file descriptor or -1 if the user tries to - * retrieve the descriptor from a line that wasn't configured for - * event monitoring. - * - * Users may want to poll the event file descriptor on their own. This routine - * allows to access it. + * @brief Get the number of events this buffers stores. + * @param buf Line event buffer. + * @return Number of events stored in this buffer. */ -int gpiod_line_event_get_fd(struct gpiod_line *line); +unsigned int +gpiod_line_event_buffer_num_events(struct gpiod_line_event_buffer *buf); /** - * @brief Read the last GPIO event directly from a file descriptor. + * @brief Read GPIO line events directly from a file descriptor. * @param fd File descriptor. - * @param event Buffer in which the event data will be stored. - * @return 0 if the event was read correctly, -1 on error. + * @param buf Line event buffer. + * @param max_events Maximum number of events to read. + * @return On success returns the number of events read from the file + * descriptor, on failure return -1. * * Users who directly poll the file descriptor for incoming events can also * directly read the event data from it using this routine. This function * translates the kernel representation of the event to the libgpiod format. */ -int gpiod_line_event_read_fd(int fd, struct gpiod_line_event_buffer *buf); - -/** - * @brief Read up to a certain number of events directly from a file descriptor. - * @param fd File descriptor. - * @param events Buffer to which the event data will be copied. Must hold at - * least the amount of events specified in num_events. - * @param max_events Specifies the maximum number of events to read. - * @return On success returns the number of events stored in the buffer, on - * failure -1 is returned. - */ -int gpiod_line_event_read_fd_multiple(int fd, - struct gpiod_line_event_buffer *buf, - unsigned int max_events); +int gpiod_line_event_buffer_read_fd(int fd, struct gpiod_line_event_buffer *buf, + unsigned int max_events); /** - * @} - * * @} * * @defgroup misc Stuff that didn't fit anywhere else @@ -1052,6 +908,19 @@ int gpiod_line_event_read_fd_multiple(int fd, * Various libgpiod-related functions. */ +/** + * @brief Check if the file pointed to by path is a GPIO chip character device. + * @param path Path to check. + * @return True if the file exists and is a GPIO chip character device or a + * symbolic link to it. + */ +bool gpiod_is_gpiochip_device(const char *path); + +/** + * @brief Convert seconds to nanoseconds. + * @param sec Number of seconds to convert. + * @return The same duration in nanoseconds. + */ uint64_t gpiod_sec_to_nsec(uint64_t sec); /** diff --git a/lib/Makefile.am b/lib/Makefile.am index 0f19eec..8713d52 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -2,14 +2,19 @@ # SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> lib_LTLIBRARIES = libgpiod.la -libgpiod_la_SOURCES = core.c \ +libgpiod_la_SOURCES = attr.c \ + chip.c \ + config.c \ event.c \ - helpers.c \ + handle.c \ internal.h \ internal.c \ info.c \ + mask.c \ misc.c \ + request.c \ uapi/gpio.h + libgpiod_la_CFLAGS = -Wall -Wextra -g -std=gnu89 libgpiod_la_CFLAGS += -fvisibility=hidden -I$(top_srcdir)/include/ libgpiod_la_CFLAGS += -include $(top_builddir)/config.h diff --git a/lib/attr.c b/lib/attr.c new file mode 100644 index 0000000..893f4d9 --- /dev/null +++ b/lib/attr.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@xxxxxxxx> + +/* Line attribute data structure and functions. */ + +#include <errno.h> +#include <gpiod.h> +#include <stdlib.h> +#include <string.h> + +#include "internal.h" +#include "uapi/gpio.h" + +struct line_attr_flags { + int request_type; + int drive; + int bias; + bool active_low; + bool clock_realtime; +}; + +struct line_attr_debounce { + bool debounced; + unsigned long debounce_period; +}; + +struct gpiod_line_attr { + struct gpiod_refcount refcount; + int type; + gpiod_line_mask mask; + union { + struct line_attr_flags flags; + uint64_t values; + struct line_attr_debounce debounce; + }; +}; + +static void line_attr_release(struct gpiod_refcount *refcount) +{ + struct gpiod_line_attr *attr; + + attr = gpiod_container_of(refcount, struct gpiod_line_attr, refcount); + + free(attr); +} + +GPIOD_API struct gpiod_line_attr *gpiod_line_attr_new(int type) +{ + struct gpiod_line_attr *attr; + + switch (type) { + case GPIOD_LINE_ATTR_TYPE_OPTS: + case GPIOD_LINE_ATTR_TYPE_OUTPUT_VALUES: + case GPIOD_LINE_ATTR_TYPE_DEBOUNCE: + break; + default: + errno = EINVAL; + return NULL; + } + + attr = malloc(sizeof(*attr)); + if (!attr) + return NULL; + + memset(attr, 0, sizeof(*attr)); + gpiod_refcount_init(&attr->refcount, line_attr_release); + attr->type = type; + + return attr; +} + +GPIOD_API struct gpiod_line_attr * +gpiod_line_attr_ref(struct gpiod_line_attr *attr) +{ + gpiod_refcount_ref(&attr->refcount); + return attr; +} + +GPIOD_API void gpiod_line_attr_unref(struct gpiod_line_attr *attr) +{ + gpiod_refcount_unref(&attr->refcount); +} + +GPIOD_API void gpiod_line_attr_set_line_mask(struct gpiod_line_attr *attr, + gpiod_line_mask mask) +{ + attr->mask = mask; +} + +static int line_attr_expect_type(struct gpiod_line_attr *attr, int type) +{ + if (attr->type != type) { + errno = EINVAL; + return -1; + } + + return 0; +} + +GPIOD_API int gpiod_line_attr_set_request_type(struct gpiod_line_attr *attr, + int request_type) +{ + int ret; + + ret = line_attr_expect_type(attr, GPIOD_LINE_ATTR_TYPE_OPTS); + if (ret) + return -1; + + ret = gpiod_validate_request_type(request_type); + if (ret) + return -1; + + attr->flags.request_type = request_type; + return 0; +} + +GPIOD_API int gpiod_line_attr_set_drive(struct gpiod_line_attr *attr, int drive) +{ + int ret; + + ret = line_attr_expect_type(attr, GPIOD_LINE_ATTR_TYPE_OPTS); + if (ret) + return -1; + + ret = gpiod_validate_drive(drive); + if (ret) + return -1; + + attr->flags.drive = drive; + return 0; +} + +GPIOD_API int gpiod_line_attr_set_bias(struct gpiod_line_attr *attr, int bias) +{ + int ret; + + ret = line_attr_expect_type(attr, GPIOD_LINE_ATTR_TYPE_OPTS); + if (ret) + return -1; + + ret = gpiod_validate_bias(bias); + if (ret) + return -1; + + attr->flags.bias = bias; + return 0; +} + +GPIOD_API int gpiod_line_attr_set_active_low(struct gpiod_line_attr *attr, + bool active_low) +{ + int ret; + + ret = line_attr_expect_type(attr, GPIOD_LINE_ATTR_TYPE_OPTS); + if (ret) + return -1; + + attr->flags.active_low = active_low; + return 0; +} + +GPIOD_API int gpiod_line_attr_set_clock_realtime(struct gpiod_line_attr *attr, + bool clock_realtime) +{ + int ret; + + ret = line_attr_expect_type(attr, GPIOD_LINE_ATTR_TYPE_OPTS); + if (ret) + return -1; + + attr->flags.clock_realtime = clock_realtime; + return 0; +} + +GPIOD_API int gpiod_line_attr_set_debounce(struct gpiod_line_attr *attr, + bool debounce, + unsigned long debounce_period) +{ + int ret; + + ret = line_attr_expect_type(attr, GPIOD_LINE_ATTR_TYPE_DEBOUNCE); + if (ret) + return -1; + + attr->debounce.debounced = debounce; + if (debounce) + attr->debounce.debounce_period = debounce_period; + + return 0; +} + +GPIOD_API int gpiod_line_attr_set_output_values(struct gpiod_line_attr *attr, + gpiod_line_mask values) +{ + int ret; + + ret = line_attr_expect_type(attr, GPIOD_LINE_ATTR_TYPE_OUTPUT_VALUES); + if (ret) + return -1; + + attr->values = values; + return 0; +} + +void gpiod_line_attr_to_kernel(struct gpiod_line_attr *attr, + struct gpio_v2_line_config_attribute *attrbuf) +{ + struct gpio_v2_line_attribute *la = &attrbuf->attr; + struct line_attr_flags *flags; + + attrbuf->mask = attr->mask; + + switch (attr->type) { + case GPIOD_LINE_ATTR_TYPE_OPTS: + la->id = GPIO_V2_LINE_ATTR_ID_FLAGS; + flags = &attr->flags; + la->flags = gpiod_make_kernel_flags(flags->request_type, + flags->drive, + flags->bias, + flags->active_low, + flags->clock_realtime); + break; + case GPIOD_LINE_ATTR_TYPE_OUTPUT_VALUES: + la->id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES; + la->values = attr->values; + break; + case GPIOD_LINE_ATTR_TYPE_DEBOUNCE: + la->id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; + la->debounce_period_us = attr->debounce.debounce_period; + break; + } +} diff --git a/lib/chip.c b/lib/chip.c new file mode 100644 index 0000000..9bacfe7 --- /dev/null +++ b/lib/chip.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@xxxxxxxx> + +/* Line attribute data structure and functions. */ + +#include <errno.h> +#include <fcntl.h> +#include <gpiod.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include "internal.h" +#include "uapi/gpio.h" + +struct gpiod_chip { + struct gpiod_refcount refcount; + int fd; + unsigned int num_lines; + char name[32]; + char label[32]; +}; + +static void chip_release(struct gpiod_refcount *refcount) +{ + struct gpiod_chip *chip; + + chip = gpiod_container_of(refcount, struct gpiod_chip, refcount); + + close(chip->fd); + free(chip); +} + +GPIOD_API struct gpiod_chip *gpiod_chip_open(const char *path) +{ + struct gpiochip_info info; + struct gpiod_chip *chip; + int rv, fd; + + fd = open(path, O_RDWR | O_CLOEXEC); + if (fd < 0) + return NULL; + + /* + * We were able to open the file but is it really a gpiochip character + * device? + */ + if (!gpiod_is_gpiochip_device(path)) + goto err_close_fd; + + chip = malloc(sizeof(*chip)); + if (!chip) + goto err_close_fd; + + memset(chip, 0, sizeof(*chip)); + memset(&info, 0, sizeof(info)); + + rv = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info); + if (rv < 0) + goto err_free_chip; + + chip->fd = fd; + chip->num_lines = info.lines; + gpiod_refcount_init(&chip->refcount, chip_release); + + /* + * GPIO device must have a name - don't bother checking this field. In + * the worst case (would have to be a weird kernel bug) it'll be empty. + */ + strncpy(chip->name, info.name, sizeof(chip->name)); + + /* + * The kernel sets the label of a GPIO device to "unknown" if it + * hasn't been defined in DT, board file etc. On the off-chance that + * we got an empty string, do the same. + */ + if (info.label[0] == '\0') + strncpy(chip->label, "unknown", sizeof(chip->label)); + else + strncpy(chip->label, info.label, sizeof(chip->label)); + + return chip; + +err_free_chip: + free(chip); +err_close_fd: + close(fd); + + return NULL; +} + +GPIOD_API struct gpiod_chip *gpiod_chip_ref(struct gpiod_chip *chip) +{ + gpiod_refcount_ref(&chip->refcount); + return chip; +} + +GPIOD_API void gpiod_chip_unref(struct gpiod_chip *chip) +{ + gpiod_refcount_unref(&chip->refcount); +} + +GPIOD_API const char *gpiod_chip_get_name(struct gpiod_chip *chip) +{ + return chip->name; +} + +GPIOD_API const char *gpiod_chip_get_label(struct gpiod_chip *chip) +{ + return chip->label; +} + +GPIOD_API unsigned int gpiod_chip_get_num_lines(struct gpiod_chip *chip) +{ + return chip->num_lines; +} + +static int chip_read_line_info(int fd, unsigned int offset, + struct gpio_v2_line_info *infobuf) +{ + int ret; + + memset(infobuf, 0, sizeof(*infobuf)); + infobuf->offset = offset; + + ret = ioctl(fd, GPIO_V2_GET_LINEINFO_IOCTL, infobuf); + if (ret) + return -1; + + return 0; +} + +GPIOD_API struct gpiod_line_info * +gpiod_chip_get_line_info(struct gpiod_chip *chip, unsigned int offset) +{ + struct gpio_v2_line_info infobuf; + int ret; + + ret = chip_read_line_info(chip->fd, offset, &infobuf); + if (ret) + return NULL; + + return gpiod_line_info_from_kernel(&infobuf); +} + +GPIOD_API int gpiod_chip_find_line(struct gpiod_chip *chip, const char *name) +{ + struct gpio_v2_line_info infobuf; + unsigned int offset; + int ret; + + for (offset = 0; offset < chip->num_lines; offset++) { + ret = chip_read_line_info(chip->fd, offset, &infobuf); + if (ret) + return -1; + + if (strcmp(name, infobuf.name) == 0) + return offset; + } + + errno = ENOENT; + return -1; +} + +GPIOD_API struct gpiod_request_handle * +gpiod_chip_request_lines(struct gpiod_chip *chip, + struct gpiod_request_config *req_cfg, + struct gpiod_line_config *line_cfg) +{ + struct gpio_v2_line_request reqbuf; + int ret; + + memset(&reqbuf, 0, sizeof(reqbuf)); + gpiod_request_config_to_kernel(req_cfg, &reqbuf); + gpiod_line_config_to_kernel(line_cfg, &reqbuf.config); + + ret = ioctl(chip->fd, GPIO_V2_GET_LINE_IOCTL, &reqbuf); + if (ret < 0) + return NULL; + + return gpiod_request_handle_from_fd(reqbuf.fd); +} diff --git a/lib/config.c b/lib/config.c new file mode 100644 index 0000000..924302e --- /dev/null +++ b/lib/config.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@xxxxxxxx> + +/* Line configuration data structure and functions. */ + +#include <errno.h> +#include <gpiod.h> +#include <stdlib.h> +#include <string.h> + +#include "internal.h" +#include "uapi/gpio.h" + +#define LINE_CONFIG_ATTR_MAX 10 + +struct gpiod_line_config { + struct gpiod_refcount refcount; + int request_type; + int drive; + int bias; + bool active_low; + bool clock_realtime; + struct gpiod_line_attr *attrs[LINE_CONFIG_ATTR_MAX]; + unsigned int num_attrs; +}; + +static void line_config_unref_attrs(struct gpiod_line_config *config) +{ + unsigned int i; + + for (i = 0; i < config->num_attrs; i++) + gpiod_line_attr_unref(config->attrs[i]); +} + +static void line_config_release(struct gpiod_refcount *refcount) +{ + struct gpiod_line_config *config; + + config = gpiod_container_of(refcount, + struct gpiod_line_config, refcount); + + line_config_unref_attrs(config); + free(config); +} + +GPIOD_API struct gpiod_line_config *gpiod_line_config_new(void) +{ + struct gpiod_line_config *config; + + config = malloc(sizeof(*config)); + if (!config) + return NULL; + + memset(config, 0, sizeof(*config)); + gpiod_refcount_init(&config->refcount, line_config_release); + + return config; +} + +GPIOD_API struct gpiod_line_config * +gpiod_line_config_ref(struct gpiod_line_config *config) +{ + gpiod_refcount_ref(&config->refcount); + return config; +} + +GPIOD_API void gpiod_line_config_unref(struct gpiod_line_config *config) +{ + gpiod_refcount_unref(&config->refcount); +} + +GPIOD_API int gpiod_line_config_add_attribute(struct gpiod_line_config *config, + struct gpiod_line_attr *attr) +{ + if (config->num_attrs == LINE_CONFIG_ATTR_MAX) { + errno = EBUSY; + return -1; + } + + config->attrs[config->num_attrs++] = gpiod_line_attr_ref(attr); + return 0; +} + +GPIOD_API void gpiod_line_config_clear_attrs(struct gpiod_line_config *config) +{ + line_config_unref_attrs(config); + memset(config->attrs, 0, sizeof(config->attrs)); + config->num_attrs = 0; +} + +GPIOD_API int +gpiod_line_config_set_request_type(struct gpiod_line_config *config, + int request_type) +{ + int ret; + + ret = gpiod_validate_request_type(request_type); + if (ret) + return -1; + + config->request_type = request_type; + return 0; +} + +GPIOD_API int +gpiod_line_config_set_drive(struct gpiod_line_config *config, int drive) +{ + int ret; + + ret = gpiod_validate_drive(drive); + if (ret) + return -1; + + config->drive = drive; + return 0; +} + +GPIOD_API int +gpiod_line_config_set_bias(struct gpiod_line_config *config, int bias) +{ + int ret; + + ret = gpiod_validate_bias(bias); + if (ret) + return -1; + + config->bias = bias; + return 0; +} + +GPIOD_API void +gpiod_line_config_set_active_low(struct gpiod_line_config *config, + bool active_low) +{ + config->active_low = active_low; +} + +GPIOD_API void +gpiod_line_config_set_event_clock_realtime(struct gpiod_line_config *config, + bool clock_realtime) +{ + config->clock_realtime = clock_realtime; +} + +void gpiod_line_config_to_kernel(struct gpiod_line_config *config, + struct gpio_v2_line_config *cfgbuf) +{ + unsigned int i; + + cfgbuf->flags = gpiod_make_kernel_flags(config->request_type, + config->drive, config->bias, + config->active_low, + config->clock_realtime); + + cfgbuf->num_attrs = config->num_attrs; + for (i = 0; i < config->num_attrs; i++) + gpiod_line_attr_to_kernel(config->attrs[i], &cfgbuf->attrs[i]); +} diff --git a/lib/core.c b/lib/core.c deleted file mode 100644 index ed65653..0000000 --- a/lib/core.c +++ /dev/null @@ -1,1131 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1-or-later -// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> - -/* Low-level, core library code. */ - -#include <errno.h> -#include <fcntl.h> -#include <gpiod.h> -#include <limits.h> -#include <poll.h> -#include <stdint.h> -#include <stdio.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/sysmacros.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -#include "internal.h" -#include "uapi/gpio.h" - -#define LINE_REQUEST_MAX_LINES 64 - -enum { - LINE_FREE = 0, - LINE_REQUESTED_VALUES, - LINE_REQUESTED_EVENTS, -}; - -struct line_fd_handle { - int fd; - int refcount; -}; - -struct gpiod_line { - unsigned int offset; - - /* The direction of the GPIO line. */ - int direction; - - /* Is this line active-low?. */ - bool active_low; - - /* The logical value last written to the line. */ - int output_value; - - /* The GPIOLINE_FLAGs returned by GPIO_GET_LINEINFO_IOCTL. */ - __u32 info_flags; - - /* The GPIOD_LINE_REQUEST_FLAGs provided to request the line. */ - __u32 req_flags; - - /* - * Indicator of LINE_FREE, LINE_REQUESTED_VALUES or - * LINE_REQUESTED_EVENTS. - */ - int state; - - struct gpiod_chip *chip; - struct line_fd_handle *fd_handle; - - char name[32]; - char consumer[32]; -}; - -struct gpiod_chip { - struct gpiod_refcount refcount; - - struct gpiod_line **lines; - unsigned int num_lines; - - int fd; - - char name[32]; - char label[32]; -}; - -/* - * The structure is defined in a way that allows internal users to allocate - * bulk objects that hold a single line on the stack - that way we can reuse - * a lot of code between functions that take single lines and those that take - * bulks as arguments while not unnecessarily allocating memory dynamically. - */ -struct gpiod_line_bulk { - unsigned int num_lines; - unsigned int max_lines; - struct gpiod_line *lines[1]; -}; - -#define BULK_SINGLE_LINE_INIT(line) \ - { 1, 1, { (line) } } - -GPIOD_API struct gpiod_line_bulk *gpiod_line_bulk_new(unsigned int max_lines) -{ - struct gpiod_line_bulk *bulk; - size_t size; - - if (max_lines == 0) { - errno = EINVAL; - return NULL; - } - - size = sizeof(struct gpiod_line_bulk) + - (max_lines - 1) * sizeof(struct gpiod_line *); - - bulk = malloc(size); - if (!bulk) - return NULL; - - bulk->max_lines = max_lines; - gpiod_line_bulk_reset(bulk); - - return bulk; -} - -GPIOD_API void gpiod_line_bulk_reset(struct gpiod_line_bulk *bulk) -{ - bulk->num_lines = 0; - memset(bulk->lines, 0, bulk->max_lines * sizeof(struct line *)); -} - -GPIOD_API void gpiod_line_bulk_free(struct gpiod_line_bulk *bulk) -{ - free(bulk); -} - -GPIOD_API int gpiod_line_bulk_add_line(struct gpiod_line_bulk *bulk, - struct gpiod_line *line) -{ - if (bulk->num_lines == bulk->max_lines) { - errno = EINVAL; - return -1; - } - - if (bulk->num_lines != 0) { - if (bulk->lines[0]->chip != gpiod_line_get_chip(line)) { - errno = EINVAL; - return -1; - } - } - - bulk->lines[bulk->num_lines++] = line; - - return 0; -} - -GPIOD_API struct gpiod_line * -gpiod_line_bulk_get_line(struct gpiod_line_bulk *bulk, unsigned int index) -{ - if (index >= bulk->num_lines) { - errno = EINVAL; - return NULL; - } - - return bulk->lines[index]; -} - -GPIOD_API unsigned int gpiod_line_bulk_num_lines(struct gpiod_line_bulk *bulk) -{ - return bulk->num_lines; -} - -GPIOD_API void gpiod_line_bulk_foreach_line(struct gpiod_line_bulk *bulk, - gpiod_line_bulk_foreach_cb func, - void *data) -{ - unsigned int index; - int ret; - - for (index = 0; index < bulk->num_lines; index++) { - ret = func(bulk->lines[index], data); - if (ret == GPIOD_LINE_BULK_CB_STOP) - return; - } -} - -#define line_bulk_foreach_line(bulk, line, index) \ - for ((index) = 0, (line) = (bulk)->lines[0]; \ - (index) < (bulk)->num_lines; \ - (index)++, (line) = (bulk)->lines[(index)]) - -GPIOD_API struct gpiod_line_info * -gpiod_chip_get_line_info(struct gpiod_chip *chip, unsigned int offset) -{ - struct gpio_v2_line_info infobuf; - int ret; - - memset(&infobuf, 0, sizeof(infobuf)); - infobuf.offset = offset; - - ret = ioctl(chip->fd, GPIO_V2_GET_LINEINFO_IOCTL, &infobuf); - if (ret < 0) - return NULL; - - return gpiod_line_info_from_kernel(&infobuf); -} - -GPIOD_API bool gpiod_is_gpiochip_device(const char *path) -{ - char *realname, *sysfsp, devpath[64]; - struct stat statbuf; - bool ret = false; - int rv; - - rv = lstat(path, &statbuf); - if (rv) - goto out; - - /* - * Is it a symbolic link? We have to resolve it before checking - * the rest. - */ - realname = S_ISLNK(statbuf.st_mode) ? realpath(path, NULL) - : strdup(path); - if (realname == NULL) - goto out; - - rv = stat(realname, &statbuf); - if (rv) - goto out_free_realname; - - /* Is it a character device? */ - if (!S_ISCHR(statbuf.st_mode)) { - /* - * Passing a file descriptor not associated with a character - * device to ioctl() makes it set errno to ENOTTY. Let's do - * the same in order to stay compatible with the versions of - * libgpiod from before the introduction of this routine. - */ - errno = ENOTTY; - goto out_free_realname; - } - - /* Is the device associated with the GPIO subsystem? */ - snprintf(devpath, sizeof(devpath), "/sys/dev/char/%u:%u/subsystem", - major(statbuf.st_rdev), minor(statbuf.st_rdev)); - - sysfsp = realpath(devpath, NULL); - if (!sysfsp) - goto out_free_realname; - - if (strcmp(sysfsp, "/sys/bus/gpio") != 0) { - /* - * This is a character device but not the one we're after. - * Before the introduction of this function, we'd fail with - * ENOTTY on the first GPIO ioctl() call for this file - * descriptor. Let's stay compatible here and keep returning - * the same error code. - */ - errno = ENOTTY; - goto out_free_sysfsp; - } - - ret = true; - -out_free_sysfsp: - free(sysfsp); -out_free_realname: - free(realname); -out: - return ret; -} - -static void chip_release(struct gpiod_refcount *refcount) -{ - struct gpiod_chip *chip; - struct gpiod_line *line; - unsigned int i; - - chip = gpiod_container_of(refcount, struct gpiod_chip, refcount); - - if (chip->lines) { - for (i = 0; i < chip->num_lines; i++) { - line = chip->lines[i]; - if (line) { - gpiod_line_release(line); - free(line); - } - } - - free(chip->lines); - } - - close(chip->fd); - free(chip); -} - -GPIOD_API struct gpiod_chip *gpiod_chip_open(const char *path) -{ - struct gpiochip_info info; - struct gpiod_chip *chip; - int rv, fd; - - fd = open(path, O_RDWR | O_CLOEXEC); - if (fd < 0) - return NULL; - - /* - * We were able to open the file but is it really a gpiochip character - * device? - */ - if (!gpiod_is_gpiochip_device(path)) - goto err_close_fd; - - chip = malloc(sizeof(*chip)); - if (!chip) - goto err_close_fd; - - memset(chip, 0, sizeof(*chip)); - memset(&info, 0, sizeof(info)); - - rv = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info); - if (rv < 0) - goto err_free_chip; - - chip->fd = fd; - chip->num_lines = info.lines; - gpiod_refcount_init(&chip->refcount, chip_release); - - /* - * GPIO device must have a name - don't bother checking this field. In - * the worst case (would have to be a weird kernel bug) it'll be empty. - */ - strncpy(chip->name, info.name, sizeof(chip->name)); - - /* - * The kernel sets the label of a GPIO device to "unknown" if it - * hasn't been defined in DT, board file etc. On the off-chance that - * we got an empty string, do the same. - */ - if (info.label[0] == '\0') - strncpy(chip->label, "unknown", sizeof(chip->label)); - else - strncpy(chip->label, info.label, sizeof(chip->label)); - - return chip; - -err_free_chip: - free(chip); -err_close_fd: - close(fd); - - return NULL; -} - -GPIOD_API struct gpiod_chip *gpiod_chip_ref(struct gpiod_chip *chip) -{ - gpiod_refcount_ref(&chip->refcount); - return chip; -} - -GPIOD_API void gpiod_chip_unref(struct gpiod_chip *chip) -{ - gpiod_refcount_unref(&chip->refcount); -} - -GPIOD_API const char *gpiod_chip_get_name(struct gpiod_chip *chip) -{ - return chip->name; -} - -GPIOD_API const char *gpiod_chip_get_label(struct gpiod_chip *chip) -{ - return chip->label; -} - -GPIOD_API unsigned int gpiod_chip_get_num_lines(struct gpiod_chip *chip) -{ - return chip->num_lines; -} - -static int line_update(struct gpiod_line *line); - -GPIOD_API struct gpiod_line * -gpiod_chip_get_line(struct gpiod_chip *chip, unsigned int offset) -{ - struct gpiod_line *line; - int rv; - - if (offset >= chip->num_lines) { - errno = EINVAL; - return NULL; - } - - if (!chip->lines) { - chip->lines = calloc(chip->num_lines, - sizeof(struct gpiod_line *)); - if (!chip->lines) - return NULL; - } - - if (!chip->lines[offset]) { - line = malloc(sizeof(*line)); - if (!line) - return NULL; - - memset(line, 0, sizeof(*line)); - - line->offset = offset; - line->chip = chip; - - chip->lines[offset] = line; - } else { - line = chip->lines[offset]; - } - - rv = line_update(line); - if (rv < 0) - return NULL; - - return line; -} - -static struct line_fd_handle *line_make_fd_handle(int fd) -{ - struct line_fd_handle *handle; - - handle = malloc(sizeof(*handle)); - if (!handle) - return NULL; - - handle->fd = fd; - handle->refcount = 0; - - return handle; -} - -static void line_fd_incref(struct gpiod_line *line) -{ - line->fd_handle->refcount++; -} - -static void line_fd_decref(struct gpiod_line *line) -{ - struct line_fd_handle *handle = line->fd_handle; - - handle->refcount--; - - if (handle->refcount == 0) { - close(handle->fd); - free(handle); - line->fd_handle = NULL; - } -} - -static void line_set_fd(struct gpiod_line *line, struct line_fd_handle *handle) -{ - line->fd_handle = handle; - line_fd_incref(line); -} - -static int line_get_fd(struct gpiod_line *line) -{ - return line->fd_handle->fd; -} - -GPIOD_API struct gpiod_chip *gpiod_line_get_chip(struct gpiod_line *line) -{ - return line->chip; -} - -GPIOD_API unsigned int gpiod_line_offset(struct gpiod_line *line) -{ - return line->offset; -} - -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; -} - -static int line_update(struct gpiod_line *line) -{ - struct gpio_v2_line_info info; - int rv; - - memset(&info, 0, sizeof(info)); - info.offset = line->offset; - - rv = ioctl(line->chip->fd, GPIO_V2_GET_LINEINFO_IOCTL, &info); - if (rv < 0) - return -1; - - line->direction = info.flags & GPIO_V2_LINE_FLAG_OUTPUT - ? GPIOD_LINE_DIRECTION_OUTPUT - : GPIOD_LINE_DIRECTION_INPUT; - - line->active_low = !!(info.flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW); - - 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)); - - return 0; -} - -static bool line_is_requested(struct gpiod_line *line) -{ - return (line->state == LINE_REQUESTED_VALUES || - line->state == LINE_REQUESTED_EVENTS); -} - -static bool line_bulk_all_requested(struct gpiod_line_bulk *bulk) -{ - struct gpiod_line *line; - unsigned int idx; - - line_bulk_foreach_line(bulk, line, idx) { - if (!line_is_requested(line)) { - errno = EPERM; - return false; - } - } - - return true; -} - -static bool line_bulk_all_requested_values(struct gpiod_line_bulk *bulk) -{ - struct gpiod_line *line; - unsigned int idx; - - line_bulk_foreach_line(bulk, line, idx) { - if (line->state != LINE_REQUESTED_VALUES) { - errno = EPERM; - return false; - } - } - - return true; -} - -static bool line_request_direction_is_valid(int direction) -{ - if ((direction == GPIOD_LINE_REQUEST_DIRECTION_AS_IS) || - (direction == GPIOD_LINE_REQUEST_DIRECTION_INPUT) || - (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)) - return true; - - errno = EINVAL; - return false; -} - -static void line_request_type_to_gpio_v2_line_config(int reqtype, - struct gpio_v2_line_config *config) -{ - if (reqtype == GPIOD_LINE_REQUEST_DIRECTION_AS_IS) - return; - - 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 void line_request_flag_to_gpio_v2_line_config(int flags, - struct gpio_v2_line_config *config) -{ - if (flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW) - config->flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW; - - if (flags & GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN) - 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_DISABLED) - 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_DISABLED) - 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; -} - -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 *vals) -{ - struct gpiod_line *line; - struct line_fd_handle *line_fd; - struct gpio_v2_line_request req; - unsigned int i; - int rv, fd; - - if (!line_request_config_validate(config)) { - errno = EINVAL; - return -1; - } - - memset(&req, 0, sizeof(req)); - - req.num_lines = gpiod_line_bulk_num_lines(bulk); - line_request_config_to_gpio_v2_line_config(config, &req.config); - - line_bulk_foreach_line(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; - line_bulk_foreach_line(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, config->consumer, - sizeof(req.consumer) - 1); - - line = gpiod_line_bulk_get_line(bulk, 0); - fd = line->chip->fd; - - rv = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req); - if (rv < 0) - return -1; - - line_fd = line_make_fd_handle(req.fd); - if (!line_fd) - return -1; - - line_bulk_foreach_line(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 = lines_bitmap_test_bit( - req.config.attrs[0].attr.values, i); - line_set_fd(line, line_fd); - - rv = line_update(line); - if (rv) { - gpiod_line_release_bulk(bulk); - return rv; - } - } - - return 0; -} - -static int line_request_event_single(struct gpiod_line *line, - const struct gpiod_line_request_config *config) -{ - struct line_fd_handle *line_fd; - struct gpio_v2_line_request req; - int rv; - - memset(&req, 0, sizeof(req)); - - if (config->consumer) - strncpy(req.consumer, config->consumer, - sizeof(req.consumer) - 1); - - req.offsets[0] = gpiod_line_offset(line); - req.num_lines = 1; - line_request_config_to_gpio_v2_line_config(config, &req.config); - - rv = ioctl(line->chip->fd, GPIO_V2_GET_LINE_IOCTL, &req); - if (rv < 0) - return -1; - - line_fd = line_make_fd_handle(req.fd); - if (!line_fd) - return -1; - - line->state = LINE_REQUESTED_EVENTS; - line->req_flags = config->flags; - line_set_fd(line, line_fd); - - rv = line_update(line); - if (rv) { - gpiod_line_release(line); - return rv; - } - - return 0; -} - -static int line_request_events(struct gpiod_line_bulk *bulk, - const struct gpiod_line_request_config *config) -{ - struct gpiod_line *line; - unsigned int off; - int rv, rev; - - line_bulk_foreach_line(bulk, line, off) { - rv = line_request_event_single(line, config); - if (rv) { - for (rev = off - 1; rev >= 0; rev--) { - line = gpiod_line_bulk_get_line(bulk, rev); - gpiod_line_release(line); - } - - return -1; - } - } - - return 0; -} - -GPIOD_API int gpiod_line_request(struct gpiod_line *line, - const struct gpiod_line_request_config *config, - int default_val) -{ - struct gpiod_line_bulk bulk = BULK_SINGLE_LINE_INIT(line); - - return gpiod_line_request_bulk(&bulk, config, &default_val); -} - -static bool line_request_is_direction(int request) -{ - return request == GPIOD_LINE_REQUEST_DIRECTION_AS_IS || - request == GPIOD_LINE_REQUEST_DIRECTION_INPUT || - request == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; -} - -static bool line_request_is_events(int request) -{ - return request == GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE || - request == GPIOD_LINE_REQUEST_EVENT_RISING_EDGE || - request == GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES; -} - -GPIOD_API int -gpiod_line_request_bulk(struct gpiod_line_bulk *bulk, - const struct gpiod_line_request_config *config, - const int *vals) -{ - if (line_request_is_direction(config->request_type)) - return line_request_values(bulk, config, vals); - else if (line_request_is_events(config->request_type)) - return line_request_events(bulk, config); - - errno = EINVAL; - return -1; -} - -GPIOD_API void gpiod_line_release(struct gpiod_line *line) -{ - struct gpiod_line_bulk bulk = BULK_SINGLE_LINE_INIT(line); - - gpiod_line_release_bulk(&bulk); -} - -GPIOD_API void gpiod_line_release_bulk(struct gpiod_line_bulk *bulk) -{ - struct gpiod_line *line; - unsigned int idx; - - line_bulk_foreach_line(bulk, line, idx) { - if (line->state != LINE_FREE) { - line_fd_decref(line); - line->state = LINE_FREE; - } - } -} - -GPIOD_API int gpiod_line_get_value(struct gpiod_line *line) -{ - struct gpiod_line_bulk bulk = BULK_SINGLE_LINE_INIT(line); - int rv, value; - - rv = gpiod_line_get_value_bulk(&bulk, &value); - if (rv < 0) - return -1; - - return value; -} - -GPIOD_API int gpiod_line_get_value_bulk(struct gpiod_line_bulk *bulk, - int *values) -{ - struct gpio_v2_line_values lv; - struct gpiod_line *line; - unsigned int i; - int rv, fd; - - if (!line_bulk_all_requested(bulk)) - return -1; - - line = gpiod_line_bulk_get_line(bulk, 0); - - memset(&lv, 0, sizeof(lv)); - - if (line->state == LINE_REQUESTED_VALUES) { - 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, 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] = 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, GPIO_V2_LINE_GET_VALUES_IOCTL, &lv); - if (rv < 0) - return -1; - values[i] = lines_bitmap_test_bit(lv.bits, 0); - } - } else { - errno = EINVAL; - return -1; - } - return 0; -} - -GPIOD_API int gpiod_line_set_value(struct gpiod_line *line, int value) -{ - struct gpiod_line_bulk bulk = BULK_SINGLE_LINE_INIT(line); - - return gpiod_line_set_value_bulk(&bulk, &value); -} - -GPIOD_API int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, - const int *values) -{ - struct gpio_v2_line_values lv; - struct gpiod_line *line; - unsigned int i; - int rv, fd; - - if (!line_bulk_all_requested(bulk)) - return -1; - - memset(&lv, 0, sizeof(lv)); - - 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, GPIO_V2_LINE_SET_VALUES_IOCTL, &lv); - if (rv < 0) - return -1; - - line_bulk_foreach_line(bulk, line, i) - line->output_value = lines_bitmap_test_bit(lv.bits, i); - - return 0; -} - -GPIOD_API int gpiod_line_set_config(struct gpiod_line *line, int direction, - int flags, int value) -{ - struct gpiod_line_bulk bulk = BULK_SINGLE_LINE_INIT(line); - - return gpiod_line_set_config_bulk(&bulk, direction, flags, &value); -} - -GPIOD_API int gpiod_line_set_config_bulk(struct gpiod_line_bulk *bulk, - int direction, int flags, - const int *values) -{ - struct gpio_v2_line_config hcfg; - struct gpiod_line *line; - unsigned int i; - int rv, fd; - - if (!line_bulk_all_requested_values(bulk)) - return -1; - - if (!line_request_direction_is_valid(direction)) - return -1; - - memset(&hcfg, 0, sizeof(hcfg)); - - 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) { - 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, GPIO_V2_LINE_SET_CONFIG_IOCTL, &hcfg); - if (rv < 0) - return -1; - - line_bulk_foreach_line(bulk, line, i) { - line->req_flags = flags; - if (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT) - line->output_value = lines_bitmap_test_bit( - hcfg.attrs[0].attr.values, i); - - rv = line_update(line); - if (rv < 0) - return rv; - } - return 0; -} - -GPIOD_API int gpiod_line_set_flags(struct gpiod_line *line, int flags) -{ - struct gpiod_line_bulk bulk = BULK_SINGLE_LINE_INIT(line); - - return gpiod_line_set_flags_bulk(&bulk, flags); -} - -GPIOD_API int gpiod_line_set_flags_bulk(struct gpiod_line_bulk *bulk, int flags) -{ - struct gpiod_line *line; - int values[LINE_REQUEST_MAX_LINES]; - unsigned int i; - int direction; - - line = gpiod_line_bulk_get_line(bulk, 0); - if (line->direction == GPIOD_LINE_DIRECTION_OUTPUT) { - line_bulk_foreach_line(bulk, line, i) - values[i] = line->output_value; - - direction = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; - } else { - direction = GPIOD_LINE_REQUEST_DIRECTION_INPUT; - } - - return gpiod_line_set_config_bulk(bulk, direction, - flags, values); -} - -GPIOD_API int gpiod_line_set_direction_input(struct gpiod_line *line) -{ - return gpiod_line_set_config(line, GPIOD_LINE_REQUEST_DIRECTION_INPUT, - line->req_flags, 0); -} - -GPIOD_API int gpiod_line_set_direction_input_bulk(struct gpiod_line_bulk *bulk) -{ - struct gpiod_line *line; - - line = gpiod_line_bulk_get_line(bulk, 0); - return gpiod_line_set_config_bulk(bulk, - GPIOD_LINE_REQUEST_DIRECTION_INPUT, - line->req_flags, NULL); -} - -GPIOD_API int gpiod_line_set_direction_output(struct gpiod_line *line, - int value) -{ - return gpiod_line_set_config(line, GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, - line->req_flags, value); -} - -GPIOD_API int gpiod_line_set_direction_output_bulk(struct gpiod_line_bulk *bulk, - const int *values) -{ - struct gpiod_line *line; - - line = gpiod_line_bulk_get_line(bulk, 0); - return gpiod_line_set_config_bulk(bulk, - GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, - line->req_flags, values); -} - -GPIOD_API int gpiod_line_event_wait(struct gpiod_line *line, uint64_t timeout) -{ - struct gpiod_line_bulk bulk = BULK_SINGLE_LINE_INIT(line); - - return gpiod_line_event_wait_bulk(&bulk, timeout, NULL); -} - -GPIOD_API int gpiod_line_event_wait_bulk(struct gpiod_line_bulk *bulk, - uint64_t timeout, - struct gpiod_line_bulk *event_bulk) -{ - struct pollfd fds[LINE_REQUEST_MAX_LINES]; - unsigned int off, num_lines; - struct gpiod_line *line; - struct timespec ts; - int rv; - - if (!line_bulk_all_requested(bulk)) - return -1; - - memset(fds, 0, sizeof(fds)); - num_lines = gpiod_line_bulk_num_lines(bulk); - - line_bulk_foreach_line(bulk, line, off) { - fds[off].fd = line_get_fd(line); - fds[off].events = POLLIN | POLLPRI; - } - - ts.tv_sec = timeout / 1000000000ULL; - ts.tv_nsec = timeout % 1000000000ULL; - - rv = ppoll(fds, num_lines, &ts, NULL); - if (rv < 0) - return -1; - else if (rv == 0) - return 0; - - for (off = 0; off < num_lines; off++) { - if (fds[off].revents) { - if (fds[off].revents & POLLNVAL) { - errno = EINVAL; - return -1; - } - - if (event_bulk) { - line = gpiod_line_bulk_get_line(bulk, off); - rv = gpiod_line_bulk_add_line(event_bulk, line); - if (rv) - return -1; - } - - if (!--rv) - break; - } - } - - return 1; -} - -GPIOD_API int gpiod_line_event_get_fd(struct gpiod_line *line) -{ - if (line->state != LINE_REQUESTED_EVENTS) { - errno = EPERM; - return -1; - } - - return line_get_fd(line); -} diff --git a/lib/event.c b/lib/event.c index f4dfce8..03b2781 100644 --- a/lib/event.c +++ b/lib/event.c @@ -3,6 +3,7 @@ #include <errno.h> #include <gpiod.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> @@ -169,48 +170,15 @@ gpiod_line_event_buffer_copy_event(struct gpiod_line_event_buffer *buf, return event; } -GPIOD_API int gpiod_line_event_read(struct gpiod_line *line, - struct gpiod_line_event_buffer *buf) -{ - int ret; - - ret = gpiod_line_event_read_multiple(line, buf, 1); - if (ret < 0) - return -1; - - return 0; -} - -GPIOD_API int -gpiod_line_event_read_multiple(struct gpiod_line *line, - struct gpiod_line_event_buffer *buf, - unsigned int num_events) -{ - int fd; - - fd = gpiod_line_event_get_fd(line); - if (fd < 0) - return -1; - - return gpiod_line_event_read_fd_multiple(fd, buf, num_events); -} - -GPIOD_API int gpiod_line_event_read_fd(int fd, - struct gpiod_line_event_buffer *buf) +GPIOD_API unsigned int +gpiod_line_event_buffer_num_events(struct gpiod_line_event_buffer *buf) { - int ret; - - ret = gpiod_line_event_read_fd_multiple(fd, buf, 1); - if (ret < 0) - return -1; - - return 0; + return buf->num_events; } GPIOD_API int -gpiod_line_event_read_fd_multiple(int fd, - struct gpiod_line_event_buffer *buf, - unsigned int max_events) +gpiod_line_event_buffer_read_fd(int fd, struct gpiod_line_event_buffer *buf, + unsigned int max_events) { struct gpio_v2_line_event *curr; struct gpiod_line_event *event; diff --git a/lib/handle.c b/lib/handle.c new file mode 100644 index 0000000..abd08f9 --- /dev/null +++ b/lib/handle.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@xxxxxxxx> + +/* Line attribute data structure and functions. */ + +#include <errno.h> +#include <gpiod.h> +#include <poll.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include "internal.h" +#include "uapi/gpio.h" + +struct gpiod_request_handle { + struct gpiod_refcount refcount; + int fd; +}; + +static void request_handle_release(struct gpiod_refcount *refcount) +{ + struct gpiod_request_handle *handle; + + handle = gpiod_container_of(refcount, + struct gpiod_request_handle, refcount); + + close(handle->fd); + free(handle); +} + +struct gpiod_request_handle *gpiod_request_handle_from_fd(int fd) +{ + struct gpiod_request_handle *handle; + + handle = malloc(sizeof(*handle)); + if (!handle) + return NULL; + + memset(handle, 0, sizeof(*handle)); + gpiod_refcount_init(&handle->refcount, request_handle_release); + handle->fd = fd; + + return handle; +} + +GPIOD_API struct gpiod_request_handle * +gpiod_request_handle_ref(struct gpiod_request_handle *handle) +{ + gpiod_refcount_ref(&handle->refcount); + return handle; +} + +GPIOD_API void gpiod_request_handle_unref(struct gpiod_request_handle *handle) +{ + gpiod_refcount_unref(&handle->refcount); +} + +GPIOD_API int +gpiod_request_handle_get_values(struct gpiod_request_handle *handle, + gpiod_line_mask *values, + gpiod_line_mask mask) +{ + struct gpio_v2_line_values valbuf; + int ret; + + valbuf.bits = 0; + valbuf.mask = mask; + + ret = ioctl(handle->fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &valbuf); + if (ret) + return -1; + + *values = valbuf.bits; + + return 0; +} + +GPIOD_API int +gpiod_request_handle_set_values(struct gpiod_request_handle *handle, + gpiod_line_mask values, + gpiod_line_mask mask) +{ + struct gpio_v2_line_values valbuf; + + valbuf.bits = values; + valbuf.mask = mask; + + return ioctl(handle->fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &valbuf); +} + +GPIOD_API int +gpiod_request_handle_set_config(struct gpiod_request_handle *handle, + struct gpiod_line_config *config) +{ + struct gpio_v2_line_config cfgbuf; + int ret; + + gpiod_line_config_to_kernel(config, &cfgbuf); + + ret = ioctl(handle->fd, GPIO_V2_LINE_SET_CONFIG_IOCTL, &cfgbuf); + if (ret) + return ret; + + return 0; +} + +GPIOD_API int +gpiod_request_handle_event_wait(struct gpiod_request_handle *handle, + uint64_t timeout) +{ + struct timespec ts; + struct pollfd pfd; + int ret; + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = handle->fd; + pfd.events = POLLIN | POLLPRI; + + ts.tv_sec = timeout / 1000000000ULL; + ts.tv_nsec = timeout % 1000000000ULL; + + ret = ppoll(&pfd, 1, &ts, NULL); + if (ret < 0) + return -1; + else if (ret == 0) + return 0; + + return 1; +} + +GPIOD_API int +gpiod_request_handle_event_read(struct gpiod_request_handle *handle, + struct gpiod_line_event_buffer *buf, + unsigned int max_events) +{ + return gpiod_line_event_buffer_read_fd(handle->fd, buf, max_events); +} + +GPIOD_API int gpiod_request_handle_get_fd(struct gpiod_request_handle *handle) +{ + return handle->fd; +} diff --git a/lib/helpers.c b/lib/helpers.c deleted file mode 100644 index 6e15dcf..0000000 --- a/lib/helpers.c +++ /dev/null @@ -1,306 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1-or-later -// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> - -/* - * More specific variants of the core API and misc functions that don't need - * access to neither the internal library data structures nor the kernel UAPI. - */ - -#include <errno.h> -#include <gpiod.h> -#include <stdio.h> -#include <string.h> - -#include "internal.h" - -GPIOD_API struct gpiod_line_bulk * -gpiod_chip_get_lines(struct gpiod_chip *chip, - unsigned int *offsets, unsigned int num_offsets) -{ - struct gpiod_line_bulk *bulk; - struct gpiod_line *line; - unsigned int i; - - bulk = gpiod_line_bulk_new(num_offsets); - if (!bulk) - return NULL; - - for (i = 0; i < num_offsets; i++) { - line = gpiod_chip_get_line(chip, offsets[i]); - if (!line) { - gpiod_line_bulk_free(bulk); - return NULL; - } - - gpiod_line_bulk_add_line(bulk, line); - } - - return bulk; -} - -GPIOD_API struct gpiod_line_bulk * -gpiod_chip_get_all_lines(struct gpiod_chip *chip) -{ - struct gpiod_line_bulk *bulk; - struct gpiod_line *line; - unsigned int offset; - - bulk = gpiod_line_bulk_new(gpiod_chip_get_num_lines(chip)); - if (!bulk) - return NULL; - - for (offset = 0; offset < gpiod_chip_get_num_lines(chip); offset++) { - line = gpiod_chip_get_line(chip, offset); - if (!line) { - gpiod_line_bulk_free(bulk); - return NULL; - } - - gpiod_line_bulk_add_line(bulk, line); - } - - return bulk; -} - -GPIOD_API int gpiod_chip_find_line(struct gpiod_chip *chip, const char *name) -{ - unsigned int offset, num_lines; - struct gpiod_line_info *info; - const char *tmp; - - num_lines = gpiod_chip_get_num_lines(chip); - - for (offset = 0; offset < num_lines; offset++) { - info = gpiod_chip_get_line_info(chip, offset); - if (!info) - return -1; - - tmp = gpiod_line_get_name(info); - if (tmp && strcmp(tmp, name) == 0) { - gpiod_line_info_unref(info); - return offset; - } - - gpiod_line_info_unref(info); - } - - errno = ENOENT; - return -1; -} - -GPIOD_API int gpiod_line_request_input(struct gpiod_line *line, - const char *consumer) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT, - }; - - return gpiod_line_request(line, &config, 0); -} - -GPIOD_API int gpiod_line_request_output(struct gpiod_line *line, - const char *consumer, int default_val) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, - }; - - return gpiod_line_request(line, &config, default_val); -} - -GPIOD_API int gpiod_line_request_input_flags(struct gpiod_line *line, - const char *consumer, int flags) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT, - .flags = flags, - }; - - return gpiod_line_request(line, &config, 0); -} - -GPIOD_API int gpiod_line_request_output_flags(struct gpiod_line *line, - const char *consumer, int flags, - int default_val) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, - .flags = flags, - }; - - return gpiod_line_request(line, &config, default_val); -} - -static int line_event_request_type(struct gpiod_line *line, - const char *consumer, int flags, int type) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .request_type = type, - .flags = flags, - }; - - return gpiod_line_request(line, &config, 0); -} - -GPIOD_API int gpiod_line_request_rising_edge_events(struct gpiod_line *line, - const char *consumer) -{ - return line_event_request_type(line, consumer, 0, - GPIOD_LINE_REQUEST_EVENT_RISING_EDGE); -} - -GPIOD_API int gpiod_line_request_falling_edge_events(struct gpiod_line *line, - const char *consumer) -{ - return line_event_request_type(line, consumer, 0, - GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE); -} - -GPIOD_API int gpiod_line_request_both_edges_events(struct gpiod_line *line, - const char *consumer) -{ - return line_event_request_type(line, consumer, 0, - GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES); -} - -GPIOD_API int -gpiod_line_request_rising_edge_events_flags(struct gpiod_line *line, - const char *consumer, - int flags) -{ - return line_event_request_type(line, consumer, flags, - GPIOD_LINE_REQUEST_EVENT_RISING_EDGE); -} - -GPIOD_API int -gpiod_line_request_falling_edge_events_flags(struct gpiod_line *line, - const char *consumer, - int flags) -{ - return line_event_request_type(line, consumer, flags, - GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE); -} - -GPIOD_API int -gpiod_line_request_both_edges_events_flags(struct gpiod_line *line, - const char *consumer, int flags) -{ - return line_event_request_type(line, consumer, flags, - GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES); -} - -GPIOD_API int gpiod_line_request_bulk_input(struct gpiod_line_bulk *bulk, - const char *consumer) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT, - }; - - return gpiod_line_request_bulk(bulk, &config, 0); -} - -GPIOD_API int gpiod_line_request_bulk_output(struct gpiod_line_bulk *bulk, - const char *consumer, - const int *default_vals) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, - }; - - return gpiod_line_request_bulk(bulk, &config, default_vals); -} - -static int line_event_request_type_bulk(struct gpiod_line_bulk *bulk, - const char *consumer, - int flags, int type) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .request_type = type, - .flags = flags, - }; - - return gpiod_line_request_bulk(bulk, &config, 0); -} - -GPIOD_API int -gpiod_line_request_bulk_rising_edge_events(struct gpiod_line_bulk *bulk, - const char *consumer) -{ - return line_event_request_type_bulk(bulk, consumer, 0, - GPIOD_LINE_REQUEST_EVENT_RISING_EDGE); -} - -GPIOD_API int -gpiod_line_request_bulk_falling_edge_events(struct gpiod_line_bulk *bulk, - const char *consumer) -{ - return line_event_request_type_bulk(bulk, consumer, 0, - GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE); -} - -GPIOD_API int -gpiod_line_request_bulk_both_edges_events(struct gpiod_line_bulk *bulk, - const char *consumer) -{ - return line_event_request_type_bulk(bulk, consumer, 0, - GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES); -} - -GPIOD_API int gpiod_line_request_bulk_input_flags(struct gpiod_line_bulk *bulk, - const char *consumer, - int flags) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT, - .flags = flags, - }; - - return gpiod_line_request_bulk(bulk, &config, 0); -} - -GPIOD_API int gpiod_line_request_bulk_output_flags(struct gpiod_line_bulk *bulk, - const char *consumer, - int flags, - const int *default_vals) -{ - struct gpiod_line_request_config config = { - .consumer = consumer, - .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, - .flags = flags, - }; - - return gpiod_line_request_bulk(bulk, &config, default_vals); -} - -GPIOD_API int gpiod_line_request_bulk_rising_edge_events_flags( - struct gpiod_line_bulk *bulk, - const char *consumer, int flags) -{ - return line_event_request_type_bulk(bulk, consumer, flags, - GPIOD_LINE_REQUEST_EVENT_RISING_EDGE); -} - -GPIOD_API int gpiod_line_request_bulk_falling_edge_events_flags( - struct gpiod_line_bulk *bulk, - const char *consumer, int flags) -{ - return line_event_request_type_bulk(bulk, consumer, flags, - GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE); -} - -GPIOD_API int gpiod_line_request_bulk_both_edges_events_flags( - struct gpiod_line_bulk *bulk, - const char *consumer, int flags) -{ - return line_event_request_type_bulk(bulk, consumer, flags, - GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES); -} diff --git a/lib/info.c b/lib/info.c index 5f7c463..ef69309 100644 --- a/lib/info.c +++ b/lib/info.c @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@xxxxxxxx> #include <gpiod.h> +#include <stdlib.h> #include <string.h> #include "internal.h" diff --git a/lib/internal.c b/lib/internal.c index 52b9461..06adcd2 100644 --- a/lib/internal.c +++ b/lib/internal.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: LGPL-2.1-or-later // SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@xxxxxxxx> +#include <errno.h> + #include "internal.h" void gpiod_refcount_init(struct gpiod_refcount *refcount, @@ -20,3 +22,102 @@ void gpiod_refcount_unref(struct gpiod_refcount *refcount) if (--refcount->refcnt == 0) refcount->release(refcount); } + +int gpiod_validate_request_type(int request_type) +{ + switch (request_type) { + case GPIOD_LINE_CONFIG_DIRECTION_AS_IS: + case GPIOD_LINE_CONFIG_DIRECTION_INPUT: + case GPIOD_LINE_CONFIG_DIRECTION_OUTPUT: + case GPIOD_LINE_CONFIG_EVENT_FALLING_EDGE: + case GPIOD_LINE_CONFIG_EVENT_RISING_EDGE: + case GPIOD_LINE_CONFIG_EVENT_BOTH_EDGES: + return 0; + } + + errno = EINVAL; + return -1; +} + +int gpiod_validate_drive(int drive) +{ + switch (drive) { + case GPIOD_LINE_CONFIG_DRIVE_PUSH_PULL: + case GPIOD_LINE_CONFIG_DRIVE_OPEN_DRAIN: + case GPIOD_LINE_CONFIG_DRIVE_OPEN_SOURCE: + return 0; + } + + errno = EINVAL; + return -1; +} + +int gpiod_validate_bias(int bias) +{ + switch (bias) { + case GPIOD_LINE_CONFIG_BIAS_DISABLED: + case GPIOD_LINE_CONFIG_BIAS_PULL_UP: + case GPIOD_LINE_CONFIG_BIAS_PULL_DOWN: + return 0; + } + + errno = EINVAL; + return -1; +} + +uint64_t gpiod_make_kernel_flags(int request_type, int drive, int bias, + bool active_low, bool clock_realtime) +{ + uint64_t flags = 0; + + switch (request_type) { + case GPIOD_LINE_CONFIG_DIRECTION_INPUT: + flags |= GPIO_V2_LINE_FLAG_INPUT; + break; + case GPIOD_LINE_CONFIG_DIRECTION_OUTPUT: + flags |= GPIO_V2_LINE_FLAG_OUTPUT; + break; + case GPIOD_LINE_CONFIG_EVENT_FALLING_EDGE: + flags |= (GPIO_V2_LINE_FLAG_EDGE_FALLING | + GPIO_V2_LINE_FLAG_INPUT); + break; + case GPIOD_LINE_CONFIG_EVENT_RISING_EDGE: + flags |= (GPIO_V2_LINE_FLAG_EDGE_RISING | + GPIO_V2_LINE_FLAG_INPUT); + break; + case GPIOD_LINE_CONFIG_EVENT_BOTH_EDGES: + flags |= (GPIO_V2_LINE_FLAG_EDGE_FALLING | + GPIO_V2_LINE_FLAG_EDGE_RISING | + GPIO_V2_LINE_FLAG_INPUT); + break; + } + + switch (drive) { + case GPIOD_LINE_CONFIG_DRIVE_OPEN_DRAIN: + flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN; + break; + case GPIOD_LINE_CONFIG_DRIVE_OPEN_SOURCE: + flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE; + break; + } + + switch (bias) { + case GPIOD_LINE_CONFIG_BIAS_DISABLED: + flags |= GPIO_V2_LINE_FLAG_BIAS_DISABLED; + break; + case GPIOD_LINE_CONFIG_BIAS_PULL_UP: + flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP; + break; + case GPIOD_LINE_CONFIG_BIAS_PULL_DOWN: + flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN; + break; + } + + if (active_low) + flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW; + + if (clock_realtime) + flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME; + + return flags; +} diff --git a/lib/internal.h b/lib/internal.h index 2d1627d..2677ee3 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -6,6 +6,7 @@ #include <gpiod.h> #include <stddef.h> +#include <stdint.h> #include "uapi/gpio.h" @@ -32,5 +33,18 @@ void gpiod_refcount_unref(struct gpiod_refcount *refcount); struct gpiod_line_info * gpiod_line_info_from_kernel(struct gpio_v2_line_info *infobuf); +void gpiod_request_config_to_kernel(struct gpiod_request_config *config, + struct gpio_v2_line_request *reqbuf); +void gpiod_line_config_to_kernel(struct gpiod_line_config *config, + struct gpio_v2_line_config *cfgbuf); +void gpiod_line_attr_to_kernel(struct gpiod_line_attr *attr, + struct gpio_v2_line_config_attribute *attrbuf); +uint64_t gpiod_make_kernel_flags(int request_type, int drive, int bias, + bool active_low, bool clock_realtime); +struct gpiod_request_handle *gpiod_request_handle_from_fd(int fd); + +int gpiod_validate_request_type(int request_type); +int gpiod_validate_drive(int drive); +int gpiod_validate_bias(int bias); #endif /* __LIBGPIOD_GPIOD_INTERNAL_H__ */ diff --git a/lib/mask.c b/lib/mask.c new file mode 100644 index 0000000..0b24821 --- /dev/null +++ b/lib/mask.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@xxxxxxxx> + +/* Line mask bitmaps. */ + +#include "internal.h" + +GPIOD_API void gpiod_line_mask_fill(gpiod_line_mask *mask) +{ + *mask = UINT64_MAX; +} + +GPIOD_API void gpiod_line_mask_zero(gpiod_line_mask *mask) +{ + *mask = 0ULL; +} + +GPIOD_API bool gpiod_line_mask_test_bit(gpiod_line_mask mask, + unsigned int offset) +{ + return mask & (1ULL << offset); +} + +GPIOD_API void gpiod_line_mask_set_bit(gpiod_line_mask *mask, + unsigned int offset) +{ + *mask |= (1ULL << offset); +} + +GPIOD_API void gpiod_line_mask_clear_bit(gpiod_line_mask *mask, + unsigned int offset) +{ + *mask &= ~(1ULL << offset); +} + +GPIOD_API void gpiod_line_mask_assign_bit(gpiod_line_mask *mask, + unsigned int offset, bool value) +{ + if (value) + gpiod_line_mask_set_bit(mask, offset); + else + gpiod_line_mask_clear_bit(mask, offset); +} diff --git a/lib/misc.c b/lib/misc.c index 67654f9..0b945ef 100644 --- a/lib/misc.c +++ b/lib/misc.c @@ -3,10 +3,85 @@ /* Misc code that didn't fit anywhere else. */ +#include <errno.h> #include <gpiod.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/types.h> +#include <unistd.h> #include "internal.h" +GPIOD_API bool gpiod_is_gpiochip_device(const char *path) +{ + char *realname, *sysfsp, devpath[64]; + struct stat statbuf; + bool ret = false; + int rv; + + rv = lstat(path, &statbuf); + if (rv) + goto out; + + /* + * Is it a symbolic link? We have to resolve it before checking + * the rest. + */ + realname = S_ISLNK(statbuf.st_mode) ? realpath(path, NULL) + : strdup(path); + if (realname == NULL) + goto out; + + rv = stat(realname, &statbuf); + if (rv) + goto out_free_realname; + + /* Is it a character device? */ + if (!S_ISCHR(statbuf.st_mode)) { + /* + * Passing a file descriptor not associated with a character + * device to ioctl() makes it set errno to ENOTTY. Let's do + * the same in order to stay compatible with the versions of + * libgpiod from before the introduction of this routine. + */ + errno = ENOTTY; + goto out_free_realname; + } + + /* Is the device associated with the GPIO subsystem? */ + snprintf(devpath, sizeof(devpath), "/sys/dev/char/%u:%u/subsystem", + major(statbuf.st_rdev), minor(statbuf.st_rdev)); + + sysfsp = realpath(devpath, NULL); + if (!sysfsp) + goto out_free_realname; + + if (strcmp(sysfsp, "/sys/bus/gpio") != 0) { + /* + * This is a character device but not the one we're after. + * Before the introduction of this function, we'd fail with + * ENOTTY on the first GPIO ioctl() call for this file + * descriptor. Let's stay compatible here and keep returning + * the same error code. + */ + errno = ENOTTY; + goto out_free_sysfsp; + } + + ret = true; + +out_free_sysfsp: + free(sysfsp); +out_free_realname: + free(realname); +out: + return ret; +} + GPIOD_API uint64_t gpiod_sec_to_nsec(uint64_t sec) { return sec * 1000000000ULL; diff --git a/lib/request.c b/lib/request.c new file mode 100644 index 0000000..1b9ae03 --- /dev/null +++ b/lib/request.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@xxxxxxxx> + +/* Line configuration data structure and functions. */ + +#include <errno.h> +#include <gpiod.h> +#include <stdlib.h> +#include <string.h> + +#include "internal.h" +#include "uapi/gpio.h" + +struct gpiod_request_config { + struct gpiod_refcount refcount; + char consumer[GPIO_MAX_NAME_SIZE]; + unsigned int offsets[GPIO_V2_LINES_MAX]; + unsigned int num_lines; + unsigned int event_buffer_size; +}; + +static void request_config_release(struct gpiod_refcount *refcount) +{ + struct gpiod_request_config *config; + + config = gpiod_container_of(refcount, + struct gpiod_request_config, refcount); + + free(config); +} + +GPIOD_API struct gpiod_request_config *gpiod_request_config_new(void) +{ + struct gpiod_request_config *config; + + config = malloc(sizeof(*config)); + if (!config) + return NULL; + + memset(config, 0, sizeof(*config)); + gpiod_refcount_init(&config->refcount, request_config_release); + + return config; +} + +GPIOD_API struct gpiod_request_config * +gpiod_request_config_ref(struct gpiod_request_config *config) +{ + gpiod_refcount_ref(&config->refcount); + return config; +} + +GPIOD_API void gpiod_request_config_unref(struct gpiod_request_config *config) +{ + gpiod_refcount_unref(&config->refcount); +} + +GPIOD_API int +gpiod_request_config_set_consumer(struct gpiod_request_config *config, + const char *consumer) +{ + if (strlen(consumer) >= GPIO_MAX_NAME_SIZE) { + errno = EINVAL; + return -1; + } + + strncpy(config->consumer, consumer, GPIO_MAX_NAME_SIZE - 1); + config->consumer[GPIO_MAX_NAME_SIZE - 1] = '\0'; + + return 0; +} + +GPIOD_API int +gpiod_request_config_set_offsets(struct gpiod_request_config *config, + unsigned int num_lines, + unsigned int *offsets) +{ + unsigned int i; + + if (num_lines > GPIO_V2_LINES_MAX) { + errno = EINVAL; + return -1; + } + + for (i = 0; i < num_lines; i++) + config->offsets[i] = offsets[i]; + + config->num_lines = num_lines; + + return 0; +} + +GPIOD_API int +gpiod_request_config_set_event_buffer_size(struct gpiod_request_config *config, + unsigned int event_buffer_size) +{ + if (event_buffer_size == 0 || + event_buffer_size > (GPIO_V2_LINES_MAX * 16)) { + errno = EINVAL; + return -1; + } + + config->event_buffer_size = event_buffer_size; + return 0; +} + +void gpiod_request_config_to_kernel(struct gpiod_request_config *config, + struct gpio_v2_line_request *reqbuf) +{ + unsigned int i; + + for (i = 0; i < config->num_lines; i++) + reqbuf->offsets[i] = config->offsets[i]; + + reqbuf->num_lines = config->num_lines; + strcpy(reqbuf->consumer, config->consumer); + reqbuf->event_buffer_size = config->event_buffer_size; +} diff --git a/tests/tests-event.c b/tests/tests-event.c index cb4aac4..6908b04 100644 --- a/tests/tests-event.c +++ b/tests/tests-event.c @@ -36,7 +36,7 @@ GPIOD_TEST_CASE(rising_edge_good, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -79,7 +79,7 @@ GPIOD_TEST_CASE(falling_edge_good, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -127,17 +127,17 @@ GPIOD_TEST_CASE(rising_edge_ignore_falling, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf0); g_assert_cmpint(ret, ==, 0); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf1); g_assert_cmpint(ret, ==, 0); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf2); g_assert_cmpint(ret, ==, 0); @@ -186,7 +186,7 @@ GPIOD_TEST_CASE(both_edges, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -199,7 +199,7 @@ GPIOD_TEST_CASE(both_edges, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(event), ==, GPIOD_LINE_EVENT_RISING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -242,7 +242,7 @@ GPIOD_TEST_CASE(both_edges_active_low, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -255,7 +255,7 @@ GPIOD_TEST_CASE(both_edges_active_low, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(event), ==, GPIOD_LINE_EVENT_FALLING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -298,7 +298,7 @@ GPIOD_TEST_CASE(both_edges_bias_disable, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -311,7 +311,7 @@ GPIOD_TEST_CASE(both_edges_bias_disable, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(event), ==, GPIOD_LINE_EVENT_RISING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -354,7 +354,7 @@ GPIOD_TEST_CASE(both_edges_bias_pull_down, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -367,7 +367,7 @@ GPIOD_TEST_CASE(both_edges_bias_pull_down, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(event), ==, GPIOD_LINE_EVENT_RISING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -410,7 +410,7 @@ GPIOD_TEST_CASE(both_edges_bias_pull_up, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -423,7 +423,7 @@ GPIOD_TEST_CASE(both_edges_bias_pull_up, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(event), ==, GPIOD_LINE_EVENT_FALLING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -466,7 +466,7 @@ GPIOD_TEST_CASE(falling_edge_active_low, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -513,7 +513,7 @@ GPIOD_TEST_CASE(get_value, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -561,7 +561,7 @@ GPIOD_TEST_CASE(get_value_active_low, 0, { 8 }) ev_thread = gpiod_test_start_event_thread(0, 7, 100); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); ret = gpiod_line_event_read(line, event_buf); @@ -836,7 +836,7 @@ GPIOD_TEST_CASE(invalid_fd, 0, { 8 }) fd = gpiod_line_event_get_fd(line); close(fd); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, -1); g_assert_cmpint(errno, ==, EINVAL); @@ -890,7 +890,7 @@ GPIOD_TEST_CASE(read_events_individually, 0, { 8 }) } /* read them individually... */ - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); if (!ret) return; @@ -905,7 +905,7 @@ GPIOD_TEST_CASE(read_events_individually, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(event), ==, GPIOD_LINE_EVENT_FALLING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); if (!ret) return; @@ -920,7 +920,7 @@ GPIOD_TEST_CASE(read_events_individually, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(event), ==, GPIOD_LINE_EVENT_RISING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); if (!ret) return; @@ -935,7 +935,7 @@ GPIOD_TEST_CASE(read_events_individually, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(event), ==, GPIOD_LINE_EVENT_FALLING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 0); } @@ -978,7 +978,7 @@ GPIOD_TEST_CASE(read_multiple_events, 0, { 8 }) usleep(10000); } - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); if (!ret) return; @@ -1001,7 +1001,7 @@ GPIOD_TEST_CASE(read_multiple_events, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(events[2]), ==, GPIOD_LINE_EVENT_RISING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); if (!ret) return; @@ -1031,7 +1031,7 @@ GPIOD_TEST_CASE(read_multiple_events, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(events[3]), ==, GPIOD_LINE_EVENT_RISING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 0); } @@ -1067,7 +1067,7 @@ GPIOD_TEST_CASE(read_multiple_events_fd, 0, { 8 }) usleep(10000); } - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); if (!ret) return; @@ -1093,7 +1093,7 @@ GPIOD_TEST_CASE(read_multiple_events_fd, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(events[2]), ==, GPIOD_LINE_EVENT_RISING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 1); if (!ret) return; @@ -1123,6 +1123,6 @@ GPIOD_TEST_CASE(read_multiple_events_fd, 0, { 8 }) g_assert_cmpint(gpiod_line_event_get_event_type(events[3]), ==, GPIOD_LINE_EVENT_RISING_EDGE); - ret = gpiod_line_event_wait(line, timeout); + ret = gpiod_request_handle_event_wait(line, timeout); g_assert_cmpint(ret, ==, 0); } diff --git a/tools/gpio-tools-test.bats b/tools/gpio-tools-test.bats index a5b97e1..e1b79b4 100755 --- a/tools/gpio-tools-test.bats +++ b/tools/gpio-tools-test.bats @@ -370,7 +370,7 @@ teardown() { run_tool gpioget "$(gpio_mockup_chip_name 1)" 0 1 2 3 4 test "$status" -eq "1" - output_regex_match ".*unable to retrieve GPIO lines from chip" + output_regex_match ".*unable to request lines.*" } @test "gpioget: same line twice" { @@ -379,7 +379,7 @@ teardown() { run_tool gpioget "$(gpio_mockup_chip_name 1)" 0 0 test "$status" -eq "1" - output_regex_match ".*unable to request lines.*" + output_regex_match ".*unable to request lines" } @test "gpioget: invalid bias" { @@ -578,7 +578,7 @@ teardown() { run_tool gpioset "$(gpio_mockup_chip_name 1)" 0=1 1=1 2=1 3=1 4=1 5=1 test "$status" -eq "1" - output_regex_match ".*unable to retrieve GPIO lines from chip" + output_regex_match ".*unable to request lines.*" } @test "gpioset: use --sec without --mode=time" { @@ -667,7 +667,7 @@ teardown() { run_tool gpioset "$(gpio_mockup_chip_name 1)" 0=1 0=1 test "$status" -eq "1" - output_regex_match ".*unable to request lines.*" + output_regex_match ".*unable to request lines" } # @@ -887,7 +887,7 @@ teardown() { run_tool gpiomon "$(gpio_mockup_chip_name 1)" 0 0 test "$status" -eq "1" - output_regex_match ".*unable to request GPIO lines for events" + output_regex_match ".*unable to request lines" } @test "gpiomon: no arguments" { @@ -912,7 +912,7 @@ teardown() { run_tool gpiomon "$(gpio_mockup_chip_name 0)" 5 test "$status" -eq "1" - output_regex_match ".*unable to retrieve GPIO lines from chip" + output_regex_match ".*unable to request lines" } @test "gpiomon: invalid bias" { diff --git a/tools/gpiodetect.c b/tools/gpiodetect.c index 10706e2..f053afa 100644 --- a/tools/gpiodetect.c +++ b/tools/gpiodetect.c @@ -6,6 +6,7 @@ #include <getopt.h> #include <gpiod.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "tools-common.h" @@ -66,14 +67,8 @@ int main(int argc, char **argv) for (i = 0; i < num_chips; i++) { chip = chip_open_by_name(entries[i]->d_name); - if (!chip) { - if (errno == EACCES) - printf("%s Permission denied\n", - entries[i]->d_name); - else - die_perror("unable to open %s", - entries[i]->d_name); - } + if (!chip) + die_perror("unable to open %s", entries[i]->d_name); printf("%s [%s] (%u lines)\n", gpiod_chip_get_name(chip), diff --git a/tools/gpiofind.c b/tools/gpiofind.c index 32b7852..a3df67c 100644 --- a/tools/gpiofind.c +++ b/tools/gpiofind.c @@ -6,6 +6,7 @@ #include <getopt.h> #include <gpiod.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "tools-common.h" diff --git a/tools/gpioget.c b/tools/gpioget.c index ceeec56..e404589 100644 --- a/tools/gpioget.c +++ b/tools/gpioget.c @@ -5,6 +5,7 @@ #include <gpiod.h> #include <limits.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "tools-common.h" @@ -38,11 +39,14 @@ static void print_help(void) int main(int argc, char **argv) { - struct gpiod_line_request_config config; - int *values, optc, opti, rv, flags = 0; + struct gpiod_request_config *req_cfg; + struct gpiod_request_handle *handle; unsigned int *offsets, i, num_lines; - struct gpiod_line_bulk *lines; + struct gpiod_line_config *line_cfg; + gpiod_line_mask values, lines; + int optc, opti, bias = 0, ret; struct gpiod_chip *chip; + bool active_low = false; char *device, *end; for (;;) { @@ -58,10 +62,10 @@ int main(int argc, char **argv) print_version(); return EXIT_SUCCESS; case 'l': - flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; + active_low = true; break; case 'B': - flags |= bias_flags(optarg); + bias = parse_bias(optarg); break; case '?': die("try %s --help", get_progname()); @@ -82,9 +86,8 @@ int main(int argc, char **argv) device = argv[0]; num_lines = argc - 1; - values = malloc(sizeof(*values) * num_lines); offsets = malloc(sizeof(*offsets) * num_lines); - if (!values || !offsets) + if (!offsets) die("out of memory"); for (i = 0; i < num_lines; i++) { @@ -97,35 +100,43 @@ int main(int argc, char **argv) if (!chip) die_perror("unable to open %s", device); - lines = gpiod_chip_get_lines(chip, offsets, num_lines); - if (!lines) - die_perror("unable to retrieve GPIO lines from chip"); + line_cfg = gpiod_line_config_new(); + if (!line_cfg) + die_perror("unable to allocate the line config structure"); - memset(&config, 0, sizeof(config)); + if (bias) + gpiod_line_config_set_bias(line_cfg, bias); + gpiod_line_config_set_active_low(line_cfg, active_low); + gpiod_line_config_set_request_type(line_cfg, + GPIOD_LINE_CONFIG_DIRECTION_INPUT); - config.consumer = "gpioget"; - config.request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT; - config.flags = flags; + req_cfg = gpiod_request_config_new(); + if (!req_cfg) + die_perror("unable to allocate the request config structure"); - rv = gpiod_line_request_bulk(lines, &config, NULL); - if (rv) + gpiod_request_config_set_consumer(req_cfg, "gpioget"); + gpiod_request_config_set_offsets(req_cfg, num_lines, offsets); + + handle = gpiod_chip_request_lines(chip, req_cfg, line_cfg); + if (!handle) die_perror("unable to request lines"); - rv = gpiod_line_get_value_bulk(lines, values); - if (rv < 0) - die_perror("error reading GPIO values"); + gpiod_line_mask_fill(&lines); + ret = gpiod_request_handle_get_values(handle, &values, lines); + if (ret) + die_perror("unable to read GPIO line values"); for (i = 0; i < num_lines; i++) { - printf("%d", values[i]); + printf("%d", !!gpiod_line_mask_test_bit(values, i)); if (i != num_lines - 1) printf(" "); } printf("\n"); - gpiod_line_release_bulk(lines); + gpiod_request_handle_unref(handle); + gpiod_request_config_unref(req_cfg); + gpiod_line_config_unref(line_cfg); gpiod_chip_unref(chip); - gpiod_line_bulk_free(lines); - free(values); free(offsets); return EXIT_SUCCESS; diff --git a/tools/gpioinfo.c b/tools/gpioinfo.c index 7901036..fb14d89 100644 --- a/tools/gpioinfo.c +++ b/tools/gpioinfo.c @@ -7,6 +7,7 @@ #include <gpiod.h> #include <stdarg.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "tools-common.h" @@ -221,14 +222,9 @@ int main(int argc, char **argv) for (i = 0; i < num_chips; i++) { chip = chip_open_by_name(entries[i]->d_name); - if (!chip) { - if (errno == EACCES) - printf("%s Permission denied\n", - entries[i]->d_name); - else - die_perror("unable to open %s", - entries[i]->d_name); - } + if (!chip) + die_perror("unable to open %s", + entries[i]->d_name); list_lines(chip); diff --git a/tools/gpiomon.c b/tools/gpiomon.c index 3e6b715..d426064 100644 --- a/tools/gpiomon.c +++ b/tools/gpiomon.c @@ -8,6 +8,7 @@ #include <poll.h> #include <signal.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> @@ -152,20 +153,19 @@ static void handle_signal(int signum UNUSED) int main(int argc, char **argv) { - unsigned int offsets[64], num_lines = 0, offset, - events_wanted = 0, events_done = 0, x; - bool watch_rising = false, watch_falling = false; + unsigned int offsets[64], num_lines = 0, offset, events_wanted = 0, + events_done = 0; + bool watch_rising = false, watch_falling = false, active_low = false; + struct gpiod_line_event_buffer *event_buffer; + int optc, opti, ret, i, event_type, bias = 0; uint64_t timeout = gpiod_sec_to_nsec(10); - int flags = 0; - int optc, opti, rv, i, y, event_type; - struct mon_ctx ctx; + struct gpiod_request_config *req_cfg; + struct gpiod_request_handle *handle; + struct gpiod_line_config *line_cfg; + struct gpiod_line_event *event; struct gpiod_chip *chip; - struct gpiod_line_bulk *lines, *evlines; + struct mon_ctx ctx; char *end; - struct gpiod_line_request_config config; - struct gpiod_line *line; - struct gpiod_line_event_buffer *event_buffer; - struct gpiod_line_event *event; /* * FIXME: use signalfd once the API has been converted to using a single file @@ -189,10 +189,10 @@ int main(int argc, char **argv) print_version(); return EXIT_SUCCESS; case 'l': - flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; + active_low = true; break; case 'B': - flags |= bias_flags(optarg); + bias = parse_bias(optarg); break; case 'n': events_wanted = strtoul(optarg, &end, 10); @@ -225,11 +225,11 @@ int main(int argc, char **argv) argv += optind; if (watch_rising && !watch_falling) - event_type = GPIOD_LINE_REQUEST_EVENT_RISING_EDGE; + event_type = GPIOD_LINE_CONFIG_EVENT_RISING_EDGE; else if (watch_falling && !watch_rising) - event_type = GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE; + event_type = GPIOD_LINE_CONFIG_EVENT_FALLING_EDGE; else - event_type = GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES; + event_type = GPIOD_LINE_CONFIG_EVENT_BOTH_EDGES; if (argc < 1) die("gpiochip must be specified"); @@ -253,70 +253,65 @@ int main(int argc, char **argv) if (!chip) die_perror("unable to open %s", argv[0]); - lines = gpiod_chip_get_lines(chip, offsets, num_lines); - if (!lines) - die_perror("unable to retrieve GPIO lines from chip"); + line_cfg = gpiod_line_config_new(); + if (!line_cfg) + die_perror("unable to allocate the line config structure"); - memset(&config, 0, sizeof(config)); + if (bias) + gpiod_line_config_set_bias(line_cfg, bias); + gpiod_line_config_set_active_low(line_cfg, active_low); + gpiod_line_config_set_request_type(line_cfg, event_type); - config.consumer = "gpiomon"; - config.request_type = event_type; - config.flags = flags; + req_cfg = gpiod_request_config_new(); + if (!req_cfg) + die_perror("unable to allocate the request config structure"); - rv = gpiod_line_request_bulk(lines, &config, NULL); - if (rv) - die_perror("unable to request GPIO lines for events"); + gpiod_request_config_set_consumer(req_cfg, "gpiomon"); + gpiod_request_config_set_offsets(req_cfg, num_lines, offsets); - evlines = gpiod_line_bulk_new(num_lines); - if (!evlines) - die("out of memory"); + handle = gpiod_chip_request_lines(chip, req_cfg, line_cfg); + if (!handle) + die_perror("unable to request lines"); event_buffer = gpiod_line_event_buffer_new(EVENT_BUF_SIZE); if (!event_buffer) die_perror("unable to allocate the line event buffer"); for (;;) { - gpiod_line_bulk_reset(evlines); - rv = gpiod_line_event_wait_bulk(lines, timeout, evlines); - if (rv < 0) + ret = gpiod_request_handle_event_wait(handle, timeout); + if (ret < 0) die_perror("error waiting for events"); - if (rv == 0) + if (ret == 0) continue; - num_lines = gpiod_line_bulk_num_lines(evlines); - - for (x = 0; x < num_lines; x++) { - line = gpiod_line_bulk_get_line(evlines, x); - - rv = gpiod_line_event_read_multiple(line, event_buffer, - EVENT_BUF_SIZE); - if (rv < 0) - die_perror("error reading line events"); + ret = gpiod_request_handle_event_read(handle, event_buffer, + EVENT_BUF_SIZE); + if (ret < 0) + die_perror("error reading line events"); - for (y = 0; y < rv; y++) { - event = gpiod_line_event_buffer_get_event( - event_buffer, y); - if (!event) - die_perror("unable to retrieve the event"); + for (i = 0; i < ret; i++) { + event = gpiod_line_event_buffer_get_event(event_buffer, + i); + if (!event) + die_perror("unable to retrieve the event from the buffer"); - handle_event(gpiod_line_offset(line), - gpiod_line_event_get_event_type(event), - gpiod_line_event_get_timestamp(event), - &ctx); + handle_event(gpiod_line_event_get_line_offset(event), + gpiod_line_event_get_event_type(event), + gpiod_line_event_get_timestamp(event), + &ctx); - events_done++; + events_done++; - if (events_wanted && - events_done >= events_wanted) - goto done; - } + if (events_wanted && events_done >= events_wanted) + goto done; } } done: - gpiod_line_release_bulk(lines); - gpiod_line_bulk_free(lines); - gpiod_line_bulk_free(evlines); + gpiod_line_event_buffer_unref(event_buffer); + gpiod_request_handle_unref(handle); + gpiod_request_config_unref(req_cfg); + gpiod_line_config_unref(line_cfg); gpiod_chip_unref(chip); return EXIT_SUCCESS; diff --git a/tools/gpioset.c b/tools/gpioset.c index 7882b53..72f77ae 100644 --- a/tools/gpioset.c +++ b/tools/gpioset.c @@ -7,6 +7,7 @@ #include <limits.h> #include <poll.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <sys/select.h> #include <unistd.h> @@ -175,12 +176,12 @@ static const struct mode_mapping *parse_mode(const char *mode) return NULL; } -static int drive_flags(const char *option) +static int parse_drive(const char *option) { if (strcmp(option, "open-drain") == 0) - return GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN; + return GPIOD_LINE_CONFIG_DRIVE_OPEN_DRAIN; if (strcmp(option, "open-source") == 0) - return GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE; + return GPIOD_LINE_CONFIG_DRIVE_OPEN_SOURCE; if (strcmp(option, "push-pull") != 0) die("invalid drive: %s", option); return 0; @@ -189,12 +190,16 @@ static int drive_flags(const char *option) int main(int argc, char **argv) { const struct mode_mapping *mode = &modes[MODE_EXIT]; - struct gpiod_line_request_config config; - int *values, rv, optc, opti, flags = 0; - unsigned int *offsets, num_lines, i; - struct gpiod_line_bulk *lines; + unsigned int *offsets, num_lines, i, val; + int ret, optc, opti, bias = 0, drive = 0; + struct gpiod_request_config *req_cfg; + struct gpiod_request_handle *handle; + struct gpiod_line_config *line_cfg; + struct gpiod_line_attr *def_vals; + gpiod_line_mask values, mask; struct callback_data cbdata; struct gpiod_chip *chip; + bool active_low = false; char *device, *end; memset(&cbdata, 0, sizeof(cbdata)); @@ -212,13 +217,13 @@ int main(int argc, char **argv) print_version(); return EXIT_SUCCESS; case 'l': - flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; + active_low = true; break; case 'B': - flags |= bias_flags(optarg); + bias = parse_bias(optarg); break; case 'D': - flags |= drive_flags(optarg); + drive = parse_drive(optarg); break; case 'm': mode = parse_mode(optarg); @@ -268,48 +273,74 @@ int main(int argc, char **argv) num_lines = argc - 1; offsets = malloc(sizeof(*offsets) * num_lines); - values = malloc(sizeof(*values) * num_lines); - if (!values || !offsets) + if (!offsets) die("out of memory"); + gpiod_line_mask_zero(&values); + gpiod_line_mask_zero(&mask); + for (i = 0; i < num_lines; i++) { - rv = sscanf(argv[i + 1], "%u=%d", &offsets[i], &values[i]); - if (rv != 2) + ret = sscanf(argv[i + 1], "%u=%d", &offsets[i], &val); + if (ret != 2) die("invalid offset<->value mapping: %s", argv[i + 1]); - if (values[i] != 0 && values[i] != 1) + if (val != 0 && val != 1) die("value must be 0 or 1: %s", argv[i + 1]); if (offsets[i] > INT_MAX) die("invalid offset: %s", argv[i + 1]); + + gpiod_line_mask_assign_bit(&values, i, val); + gpiod_line_mask_set_bit(&mask, i); } chip = chip_open_lookup(device); if (!chip) die_perror("unable to open %s", device); - lines = gpiod_chip_get_lines(chip, offsets, num_lines); - if (!lines) - die_perror("unable to retrieve GPIO lines from chip"); + def_vals = gpiod_line_attr_new(GPIOD_LINE_ATTR_TYPE_OUTPUT_VALUES); + if (!def_vals) + die_perror("unable to allocate the line attribute structure"); + + gpiod_line_attr_set_output_values(def_vals, values); + gpiod_line_attr_set_line_mask(def_vals, mask); + + line_cfg = gpiod_line_config_new(); + if (!line_cfg) + die_perror("unable to allocate the line config structure"); + + if (bias) + gpiod_line_config_set_bias(line_cfg, bias); + if (drive) + gpiod_line_config_set_drive(line_cfg, drive); + gpiod_line_config_set_active_low(line_cfg, active_low); + gpiod_line_config_set_request_type(line_cfg, + GPIOD_LINE_CONFIG_DIRECTION_OUTPUT); + + ret = gpiod_line_config_add_attribute(line_cfg, def_vals); + if (ret) + die_perror("unable to set line attribute"); - memset(&config, 0, sizeof(config)); + req_cfg = gpiod_request_config_new(); + if (!req_cfg) + die_perror("unable to allocate the request config structure"); - config.consumer = "gpioset"; - config.request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; - config.flags = flags; + gpiod_request_config_set_consumer(req_cfg, "gpioset"); + gpiod_request_config_set_offsets(req_cfg, num_lines, offsets); - rv = gpiod_line_request_bulk(lines, &config, values); - if (rv) + handle = gpiod_chip_request_lines(chip, req_cfg, line_cfg); + if (!handle) die_perror("unable to request lines"); if (mode->callback) mode->callback(&cbdata); - gpiod_line_release_bulk(lines); + gpiod_request_handle_unref(handle); + gpiod_request_config_unref(req_cfg); + gpiod_line_config_unref(line_cfg); + gpiod_line_attr_unref(def_vals); gpiod_chip_unref(chip); - gpiod_line_bulk_free(lines); free(offsets); - free(values); return EXIT_SUCCESS; } diff --git a/tools/tools-common.c b/tools/tools-common.c index 80087ee..36724d5 100644 --- a/tools/tools-common.c +++ b/tools/tools-common.c @@ -57,14 +57,14 @@ void print_version(void) printf("There is NO WARRANTY, to the extent permitted by law.\n"); } -int bias_flags(const char *option) +int parse_bias(const char *option) { if (strcmp(option, "pull-down") == 0) - return GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN; + return GPIOD_LINE_CONFIG_BIAS_PULL_DOWN; if (strcmp(option, "pull-up") == 0) - return GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP; + return GPIOD_LINE_CONFIG_BIAS_PULL_UP; if (strcmp(option, "disable") == 0) - return GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLED; + return GPIOD_LINE_CONFIG_BIAS_DISABLED; if (strcmp(option, "as-is") != 0) die("invalid bias: %s", option); return 0; diff --git a/tools/tools-common.h b/tools/tools-common.h index 5d5b505..f059440 100644 --- a/tools/tools-common.h +++ b/tools/tools-common.h @@ -25,7 +25,7 @@ const char *get_progname(void); void die(const char *fmt, ...) NORETURN PRINTF(1, 2); void die_perror(const char *fmt, ...) NORETURN PRINTF(1, 2); void print_version(void); -int bias_flags(const char *option); +int parse_bias(const char *option); void print_bias_help(void); int make_signalfd(void); int chip_dir_filter(const struct dirent *entry); -- 2.30.1