@@ -1,11 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/* SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com> */
+/* SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl> */
#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);
/**
@@ -2,14 +2,19 @@
# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com>
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
new file mode 100644
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl>
+
+/* 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;
+ }
+}
new file mode 100644
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl>
+
+/* 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);
+}
new file mode 100644
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl>
+
+/* 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]);
+}
deleted file mode 100644
@@ -1,1131 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1-or-later
-// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com>
-
-/* 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);
-}
@@ -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;
new file mode 100644
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl>
+
+/* 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;
+}
deleted file mode 100644
@@ -1,306 +0,0 @@
-// SPDX-License-Identifier: LGPL-2.1-or-later
-// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com>
-
-/*
- * 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);
-}
@@ -2,6 +2,7 @@
// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl>
#include <gpiod.h>
+#include <stdlib.h>
#include <string.h>
#include "internal.h"
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl>
+#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;
+}
@@ -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__ */
new file mode 100644
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl>
+
+/* 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);
+}
@@ -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;
new file mode 100644
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2021 Bartosz Golaszewski <brgl@bgdev.pl>
+
+/* 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;
+}
@@ -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);
}
@@ -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" {
@@ -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),
@@ -6,6 +6,7 @@
#include <getopt.h>
#include <gpiod.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "tools-common.h"
@@ -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;
@@ -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);
@@ -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;
@@ -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;
}
@@ -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;
@@ -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);
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@bgdev.pl> --- 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