From: Kent Gibson <warthog618@gmail.com>
To: Bartosz Golaszewski <brgl@bgdev.pl>
Cc: Linus Walleij <linus.walleij@linaro.org>,
Andy Shevchenko <andriy.shevchenko@linux.intel.com>,
linux-gpio@vger.kernel.org
Subject: Re: [libgpiod v2][PATCH v2 1/2] line-config: expose the override logic to users
Date: Tue, 22 Feb 2022 19:40:44 +0800 [thread overview]
Message-ID: <20220222114044.GA37327@sol> (raw)
In-Reply-To: <20220221154055.654819-2-brgl@bgdev.pl>
On Mon, Feb 21, 2022 at 04:40:54PM +0100, Bartosz Golaszewski wrote:
> We've already added getters for line-config but without exposing some
> parts of the internal logic of the object, the user can't really get
> the full picture and inspect the contents. This patch reworks the
> accessors further by providing access to the underlying override
> mechanism.
>
> For every setting, we expose a getter and setter for the default value
> as well as a set of four functions for setting, getting, clearing and
> checking per-offset overrides.
>
> An override can initially have the same value as the defaults but will
> retain the overridden value should the defaults change.
>
> We also complete the API by providing functions that allow to identify
> offsets for which at least one setting is overriden.
>
> This way the caller can fully inspect the line_config and high-level
> language bindings can provide stringification methods.
>
> While at it: we fix a couple bugs in the implementation of struct
> line_config.
>
TLDR: I've got a lot of nitpicks on the doco, but it might be simpler
for you to apply this patch as is and for me to submit a patch with doco
tweaks than for you to try to sort out my comments!
The actual code looks good, so
Reviewed-by: Kent Gibson <warthog618@gmail.com>
with or without the suggested doco changes.
> Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
> ---
> include/gpiod.h | 509 ++++++++++++++++++++-----------
> lib/line-config.c | 752 +++++++++++++++++++++++++++++-----------------
> tools/gpioget.c | 6 +-
> tools/gpiomon.c | 6 +-
> tools/gpioset.c | 9 +-
> 5 files changed, 821 insertions(+), 461 deletions(-)
>
> diff --git a/include/gpiod.h b/include/gpiod.h
> index 074e395..f8747e7 100644
> --- a/include/gpiod.h
> +++ b/include/gpiod.h
> @@ -450,12 +450,37 @@ gpiod_info_event_get_line_info(struct gpiod_info_event *event);
> *
> * The line-config object stores the configuration for lines that can be used
> * in two cases: when making a line request and when reconfiguring a set of
> - * already requested lines. The mutators for the line request don't return
> - * errors. If the set of options is too complex to be translated into kernel
> - * uAPI structures - the error will be returned at the time of the request or
> - * reconfiguration. If an invalid value was passed to any of the getters - the
> - * default value will be silently used instead. Each option can be set
> - * globally, for a single line offset or for multiple line offsets.
> + * already requested lines.
> + *
> + * A new line-config object is instantiated containing a set of sane defaults
> + * for all supported configuration settings. Those defaults can be modified by
> + * the caller. Default values can be overridden by applying different values
> + * for specific line offsets. When making a request or reconfiguring an
> + * existing one, the overridden settings for specific offsets will be taken
> + * into account first and for every other offset and setting the defaults will
> + * be used.
> + *
> + * For every setting there are two mutators (one setting the default and one
> + * for the per-offset override), two getters (one for reading the global
> + * default and one for retrieving the effective value for given offset), a
> + * function for testing if a setting is overridden for given offset and finally
> + * a function for clearing the overrides (per offset).
> + *
> + * The mutators don't return errors. If the set of options is too complex to
> + * be translated into kernel uAPI structures - the error will be returned at
> + * the time of the request or reconfiguration. If an invalid value was passed
> + * to any of the mutators - the default value will be silently used instead.
> + *
> + * Operating on offsets in struct line_config has no effect on real GPIOs. It
> + * only manipulates the object in memory and is only applied to the hardware
> + * at the time of the request or reconfiguration.
> + *
> + * Overrides set for offsets that don't end up being requested are silently
> + * ignored both in ::gpiod_chip_request_lines as well as in
> + * ::gpiod_line_request_reconfigure_lines.
> + *
This should be noted in the function documentation, not just here.
If only one place, then there not here.
> + * In cases where all requested lines are using the global defaults, the
> + * offsets can be entirely ignored when preparing the line configuration.
> */
>
> /**
> @@ -480,329 +505,424 @@ void gpiod_line_config_free(struct gpiod_line_config *config);
> void gpiod_line_config_reset(struct gpiod_line_config *config);
>
> /**
> - * @brief Set the direction of all lines.
> + * @brief Set the default direction setting.
Nitpick: "setting" feels redundant given the "Set". Same thoughout,
including the "Get" cases.
> * @param config Line config object.
> * @param direction New direction.
> */
> -void gpiod_line_config_set_direction(struct gpiod_line_config *config,
> - int direction);
> +void gpiod_line_config_set_direction_default(struct gpiod_line_config *config,
> + int direction);
>
> /**
> - * @brief Set the direction for a single line at given offset.
> + * @brief Set the direction override at given offset.
Nitpick: "at given offset" -> "for the line" throughout.
Similarly "at this offset" -> "for this line",
and "line at given offset" -> "line",
and "for a given offset" -> "for a line".
> * @param config Line config object.
> * @param direction New direction.
> - * @param offset Offset of the line for which to set the direction.
> + * @param offset Offset of the line for which to set the override.
> */
> -void gpiod_line_config_set_direction_offset(struct gpiod_line_config *config,
> - int direction, unsigned int offset);
> +void gpiod_line_config_set_direction_override(struct gpiod_line_config *config,
> + int direction,
> + unsigned int offset);
>
> /**
> - * @brief Set the direction for a subset of lines.
> + * @brief Clear the direction override at given offset.
> * @param config Line config object.
> - * @param direction New direction.
> - * @param num_offsets Number of offsets in the array.
> - * @param offsets Array of line offsets for which to set the direction.
> + * @param offset Offset of the line for which to clear the override.
> + * @note Does nothing if no override is set for this line.
> */
> -void gpiod_line_config_set_direction_subset(struct gpiod_line_config *config,
> - int direction,
> - unsigned int num_offsets,
> - const unsigned int *offsets);
> +void
> +gpiod_line_config_clear_direction_override(struct gpiod_line_config *config,
> + unsigned int offset);
> +
> +/**
> + * @brief Check if the direction setting is overridden at given offset.
> + * @param config Line config object.
> + * @param offset Offset of the line for which to check the override.
> + * @return True if direction is overridden at this offset, false otherwise.
> + */
> +bool gpiod_line_config_direction_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Get the direction setting for a given line.
> + * @brief Get the default direction setting.
> + * @param config Line config object.
> + * @return Direction setting that would have been used for any non-overridden
> + * offset.
> + */
> +int gpiod_line_config_get_direction_default(struct gpiod_line_config *config);
> +
> +/**
> + * @brief Get the direction setting for a given offset.
Preferred "given line". The direction is an attribute of the line, not
the offset. The offset is the line identifier.
Same throughout.
> * @param config Line config object.
> * @param offset Line offset for which to read the direction setting.
Nitpick: "Line offset" -> "Offset of the line"
> * @return Direction setting that would have been used for given offset if the
> * config object was used in a request at the time of the call.
> - * @note If an offset is used for which no config was provided, the function
> - * will return the global default value.
> */
> -int gpiod_line_config_get_direction(struct gpiod_line_config *config,
> - unsigned int offset);
> +int gpiod_line_config_get_direction_offset(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Set the edge event detection for all lines.
> + * @brief Set the default edge event detection.
> * @param config Line config object.
> * @param edge Type of edge events to detect.
> */
> -void gpiod_line_config_set_edge_detection(struct gpiod_line_config *config,
> - int edge);
> +void
> +gpiod_line_config_set_edge_detection_default(struct gpiod_line_config *config,
> + int edge);
>
> /**
> - * @brief Set the edge event detection for a single line at given offset.
> + * @brief Set the edge detection override at given offset.
> * @param config Line config object.
> - * @param edge Type of edge events to detect.
> - * @param offset Offset of the line for which to set the edge detection.
> + * @param edge New edge detection setting.
> + * @param offset Offset of the line for which to set the override.
> */
> void
Nitpicking here, but I prefer the
@param edge Type of edge events to detect.
style, that is an actual short description of the edge parameter, as used
above over
@param edge New edge detection setting.
as the latter is very bland boiler plate that doesn't really say
anything.
And can the @param edge description be consistent throughout all the
edge functions?
Similar elsewhere, on both points.
And again, this is being really nitpicky so feel free to leave it for a
subsequent doco tweaking pass.
> -gpiod_line_config_set_edge_detection_offset(struct gpiod_line_config *config,
> - int edge, unsigned int offset);
> +gpiod_line_config_set_edge_detection_override(struct gpiod_line_config *config,
> + int edge, unsigned int offset);
>
> /**
> - * @brief Set the edge event detection for a subset of lines.
> + * @brief Clear the edge detection override at given offset.
> * @param config Line config object.
> - * @param edge Type of edge events to detect.
> - * @param num_offsets Number of offsets in the array.
> - * @param offsets Array of line offsets for which to set the edge detection.
> + * @param offset Offset of the line for which to clear the override.
> + * @note Does nothing if no override is set for this line.
> */
> void
> -gpiod_line_config_set_edge_detection_subset(struct gpiod_line_config *config,
> - int edge, unsigned int num_offsets,
> - const unsigned int *offsets);
> +gpiod_line_config_clear_edge_detection_override(
> + struct gpiod_line_config *config, unsigned int offset);
> +
> +/**
> + * @brief Check if the edge detection setting is overridden at given offset.
> + * @param config Line config object.
> + * @param offset Offset of the line for which to check the override.
> + * @return True if edge detection is overridden at this offset, false otherwise.
> + */
> +bool
> +gpiod_line_config_edge_detection_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Get the edge event detection setting for a given line.
> + * @brief Get the default edge detection setting.
> + * @param config Line config object.
> + * @return Edge detection setting that would have been used for any offset not
> + * assigned its own direction value.
> + */
> +int
> +gpiod_line_config_get_edge_detection_default(struct gpiod_line_config *config);
> +
> +/**
> + * @brief Get the edge event detection setting for a given offset.
> * @param config Line config object.
> * @param offset Line offset for which to read the edge event detection setting.
> * @return Edge event detection setting that would have been used for given
> * offset if the config object was used in a request at the time of
> * the call.
> - * @note If an offset is used for which no config was provided, the function
> - * will return the global default value.
> */
> -int gpiod_line_config_get_edge_detection(struct gpiod_line_config *config,
> - unsigned int offset);
> +int
> +gpiod_line_config_get_edge_detection_offset(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Set the bias of all lines.
> + * @brief Set the default bias setting.
> * @param config Line config object.
> * @param bias New bias.
> */
> -void gpiod_line_config_set_bias(struct gpiod_line_config *config, int bias);
> +void gpiod_line_config_set_bias_default(struct gpiod_line_config *config,
> + int bias);
>
> /**
> - * @brief Set the bias for a single line at given offset.
> + * @brief Set the bias override at given offset.
> * @param config Line config object.
> - * @param bias New bias.
> - * @param offset Offset of the line for which to set the bias.
> + * @param bias New bias setting.
> + * @param offset Offset of the line for which to set the override.
> */
> -void gpiod_line_config_set_bias_offset(struct gpiod_line_config *config,
> - int bias, unsigned int offset);
> +void gpiod_line_config_set_bias_override(struct gpiod_line_config *config,
> + int bias, unsigned int offset);
>
> /**
> - * @brief Set the bias for a subset of lines.
> + * @brief Clear the bias override at given offset.
> * @param config Line config object.
> - * @param bias New bias.
> - * @param num_offsets Number of offsets in the array.
> - * @param offsets Array of line offsets for which to set the bias.
> + * @param offset Offset of the line for which to clear the override.
> + * @note Does nothing if no override is set for this line.
> + */
> +void gpiod_line_config_clear_bias_override(struct gpiod_line_config *config,
> + unsigned int offset);
> +
> +/**
> + * @brief Check if the bias setting is overridden at given offset.
> + * @param config Line config object.
> + * @param offset Offset of the line for which to check the override.
> + * @return True if bias is overridden at this offset, false otherwise.
> + */
> +bool gpiod_line_config_bias_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset);
> +/**
> + * @brief Get the default bias setting.
> + * @param config Line config object.
> + * @return Bias setting that would have been used for any offset not assigned
> + * its own direction value.
> */
> -void gpiod_line_config_set_bias_subset(struct gpiod_line_config *config,
> - int bias, unsigned int num_offsets,
> - const unsigned int *offsets);
> +int gpiod_line_config_get_bias_default(struct gpiod_line_config *config);
>
> /**
> - * @brief Get the bias setting for a given line.
> + * @brief Get the bias setting for a given offset.
> * @param config Line config object.
> * @param offset Line offset for which to read the bias setting.
> * @return Bias setting that would have been used for given offset if the
> * config object was used in a request at the time of the call.
> - * @note If an offset is used for which no config was provided, the function
> - * will return the global default value.
> */
> -int gpiod_line_config_get_bias(struct gpiod_line_config *config,
> +int gpiod_line_config_get_bias_offset(struct gpiod_line_config *config,
> unsigned int offset);
>
> /**
> - * @brief Set the drive of all lines.
> + * @brief Set the default drive setting.
> * @param config Line config object.
> * @param drive New drive.
> */
> -void gpiod_line_config_set_drive(struct gpiod_line_config *config, int drive);
> +void gpiod_line_config_set_drive_default(struct gpiod_line_config *config,
> + int drive);
>
> /**
> - * @brief Set the drive for a single line at given offset.
> + * @brief Set the drive override at given offset.
> * @param config Line config object.
> - * @param drive New drive.
> - * @param offset Offset of the line for which to set the drive.
> + * @param drive New drive setting.
> + * @param offset Offset of the line for which to set the override.
> */
> -void gpiod_line_config_set_drive_offset(struct gpiod_line_config *config,
> - int drive, unsigned int offset);
> +void gpiod_line_config_set_drive_override(struct gpiod_line_config *config,
> + int drive, unsigned int offset);
>
> /**
> - * @brief Set the drive for a subset of lines.
> + * @brief Clear the drive override at given offset.
> * @param config Line config object.
> - * @param drive New drive.
> - * @param num_offsets Number of offsets in the array.
> - * @param offsets Array of line offsets for which to set the drive.
> + * @param offset Offset of the line for which to clear the override.
> + * @note Does nothing if no override is set for this line.
> */
> -void gpiod_line_config_set_drive_subset(struct gpiod_line_config *config,
> - int drive, unsigned int num_offsets,
> - const unsigned int *offsets);
> +void gpiod_line_config_clear_drive_override(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Get the drive setting for a given line.
> + * @brief Check if the drive setting is overridden at given offset.
> * @param config Line config object.
> - * @param offset Line offset for which to read the drive setting.
> - * @return Drive setting that would have been used for given offset if the
> - * config object was used in a request at the time of the call.
> - * @note If an offset is used for which no config was provided, the function
> - * will return the global default value.
> + * @param offset Offset of the line for which to check the override.
> + * @return True if drive is overridden at this offset, false otherwise.
> */
> -int gpiod_line_config_get_drive(struct gpiod_line_config *config,
> - unsigned int offset);
> +bool gpiod_line_config_drive_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Set all lines as active-low.
> + * @brief Get the default drive setting.
> * @param config Line config object.
> + * @return Drive setting that would have been used for any offset not assigned
> + * its own direction value.
> */
> -void gpiod_line_config_set_active_low(struct gpiod_line_config *config);
> +int gpiod_line_config_get_drive_default(struct gpiod_line_config *config);
>
> /**
> - * @brief Set a single line as active-low.
> + * @brief Get the drive setting for a given offset.
> * @param config Line config object.
> - * @param offset Offset of the line for which to set the active setting.
> + * @param offset Line offset for which to read the drive setting.
> + * @return Drive setting that would have been used for given offset if the
> + * config object was used in a request at the time of the call.
> */
> -void gpiod_line_config_set_active_low_offset(struct gpiod_line_config *config,
> - unsigned int offset);
> +int gpiod_line_config_get_drive_offset(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Set a subset of lines as active-low.
> + * @brief Set lines to active-low by default.
> * @param config Line config object.
> - * @param num_offsets Number of offsets in the array.
> - * @param offsets Array of line offsets for which to set the active setting.
> + * @param active_low New active-low setting.
> */
> -void gpiod_line_config_set_active_low_subset(struct gpiod_line_config *config,
> - unsigned int num_offsets,
> - const unsigned int *offsets);
> +void gpiod_line_config_set_active_low_default(struct gpiod_line_config *config,
> + bool active_low);
>
> /**
> - * @brief Check if the line at given offset was configured as active-low.
> + * @brief Override the active-low setting at given offset.
> * @param config Line config object.
> - * @param offset Line offset for which to read the active-low setting.
> - * @return Active-low setting that would have been used for given offset if the
> - * config object was used in a request at the time of the call.
> - * @note If an offset is used for which no config was provided, the function
> - * will return the global default value.
> + * @param active_low New active-low setting.
> + * @param offset Offset of the line for which to set the override.
> */
> -bool gpiod_line_config_get_active_low(struct gpiod_line_config *config,
> - unsigned int offset);
> +void gpiod_line_config_set_active_low_override(struct gpiod_line_config *config,
> + bool active_low,
> + unsigned int offset);
>
> /**
> - * @brief Set all lines as active-high.
> + * @brief Clear the active-low override at given offset.
> * @param config Line config object.
> + * @param offset Offset of the line for which to clear the override.
> + * @note Does nothing if no override is set for this line.
> */
> -void gpiod_line_config_set_active_high(struct gpiod_line_config *config);
> +void
> +gpiod_line_config_clear_active_low_override(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Set a single line as active-high.
> + * @brief Check if the active-low setting is overridden at given offset.
> * @param config Line config object.
> - * @param offset Offset of the line for which to set the active setting.
> + * @param offset Offset of the line for which to check the override.
> + * @return True if active-low is overridden at this offset, false otherwise.
> */
> -void gpiod_line_config_set_active_high_offset(struct gpiod_line_config *config,
> - unsigned int offset);
> +bool
> +gpiod_line_config_active_low_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Set a subset of lines as active-high.
> + * @brief Check if active-low is the default setting.
> * @param config Line config object.
> - * @param num_offsets Number of offsets in the array.
> - * @param offsets Array of line offsets for which to set the active setting.
> + * @return Active-low setting that would have been used for any offset not
> + * assigned its own value.
> */
> -void gpiod_line_config_set_active_high_subset(struct gpiod_line_config *config,
> - unsigned int num_offsets,
> - const unsigned int *offsets);
> +bool gpiod_line_config_get_active_low_default(struct gpiod_line_config *config);
>
> /**
> - * @brief Set the debounce period for all lines.
> + * @brief Check if the line at given offset was configured as active-low.
> * @param config Line config object.
> - * @param period New debounce period. Disables debouncing if 0.
> + * @param offset Line offset for which to read the active-low setting.
> + * @return Active-low setting that would have been used for given offset if the
> + * config object was used in a request at the time of the call.
> */
> -void gpiod_line_config_set_debounce_period_us(struct gpiod_line_config *config,
> - unsigned long period);
> +bool gpiod_line_config_get_active_low_offset(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Set the debounce period for a single line at given offset.
> + * @brief Set the default debounce period.
> * @param config Line config object.
> * @param period New debounce period. Disables debouncing if 0.
> - * @param offset Offset of the line for which to set the debounce period.
> + */
> +void gpiod_line_config_set_debounce_period_us_default(
> + struct gpiod_line_config *config, unsigned long period);
> +
> +/**
> + * @brief Override the debounce period setting at given offset.
> + * @param config Line config object.
> + * @param period New debounce period in microseconds.
> + * @param offset Offset of the line for which to set the override.
> */
> void
> -gpiod_line_config_set_debounce_period_us_offset(
> +gpiod_line_config_set_debounce_period_us_override(
> struct gpiod_line_config *config,
> unsigned long period,
> unsigned int offset);
>
> /**
> - * @brief Set the debounce period for a subset of lines.
> + * @brief Clear the debounce period override at given offset.
> * @param config Line config object.
> - * @param period New debounce period. Disables debouncing if 0.
> - * @param num_offsets Number of offsets in the array.
> - * @param offsets Array of line offsets for which to set the debounce period.
> + * @param offset Offset of the line for which to clear the override.
> + * @note Does nothing if no override is set for this line.
> */
> -void
> -gpiod_line_config_set_debounce_period_us_subset(
> +void gpiod_line_config_clear_debounce_period_us_override(
> struct gpiod_line_config *config,
> - unsigned long period,
> - unsigned int num_offsets,
> - const unsigned int *offsets);
> + unsigned int offset);
> +
> +/**
> + * @brief Check if the debounce period setting is overridden at given offset.
> + * @param config Line config object.
> + * @param offset Offset of the line for which to check the override.
> + * @return True if debounce period is overridden at this offset, false
> + * otherwise.
> + */
> +bool gpiod_line_config_debounce_period_us_is_overridden(
> + struct gpiod_line_config *config,
> + unsigned int offset);
> +
> +/**
> + * @brief Get the default debounce period.
> + * @param config Line config object.
> + * @return Debounce period that would have been used for any offset not
> + * assigned its own debounce period. 0 if not debouncing is disabled.
> + */
> +unsigned long gpiod_line_config_get_debounce_period_us_default(
> + struct gpiod_line_config *config);
>
> /**
> - * @brief Get the debounce period for a given line.
> + * @brief Get the debounce period for a given offset.
> * @param config Line config object.
> * @param offset Line offset for which to read the debounce period.
> * @return Debounce period that would have been used for given offset if the
> - * config object was used in a request at the time of the call.
> - * @note If an offset is used for which no config was provided, the function
> - * will return the global default value.
> + * config object was used in a request at the time of the call. 0 if
> + * debouncing is disabled.
> */
> unsigned long
> -gpiod_line_config_get_debounce_us_period(struct gpiod_line_config *config,
> - unsigned int offset);
> +gpiod_line_config_get_debounce_period_us_offset(
> + struct gpiod_line_config *config, unsigned int offset);
>
> /**
> - * @brief Set the event timestamp clock for all lines.
> + * @brief Set the default event timestamp clock.
> * @param config Line config object.
> * @param clock New clock to use.
> */
> -void gpiod_line_config_set_event_clock(struct gpiod_line_config *config,
> - int clock);
> +void gpiod_line_config_set_event_clock_default(struct gpiod_line_config *config,
> + int clock);
>
> /**
> - * @brief Set the event clock for a single line at given offset.
> + * @brief Override the event clock setting at given offset.
> * @param config Line config object.
> * @param clock New event clock to use.
> - * @param offset Offset of the line for which to set the event clock type.
> + * @param offset Offset of the line for which to set the override.
> */
> -void gpiod_line_config_set_event_clock_offset(struct gpiod_line_config *config,
> - int clock, unsigned int offset);
> +void
> +gpiod_line_config_set_event_clock_override(struct gpiod_line_config *config,
> + int clock, unsigned int offset);
>
> /**
> - * @brief Set the event clock for a subset of lines.
> + * @brief Clear the event clock override at given offset.
> * @param config Line config object.
> - * @param clock New event clock to use.
> - * @param num_offsets Number of offsets in the array.
> - * @param offsets Array of line offsets for which to set the event clock type.
> + * @param offset Offset of the line for which to clear the override.
> + * @note Does nothing if no override is set for this line.
> + */
> +void
> +gpiod_line_config_clear_event_clock_override(struct gpiod_line_config *config,
> + unsigned int offset);
> +
> +/**
> + * @brief Check if the event clock setting is overridden at given offset.
> + * @param config Line config object.
> + * @param offset Offset of the line for which to check the override.
> + * @return True if event clock period is overridden at this offset, false
> + * otherwise.
> */
> -void gpiod_line_config_set_event_clock_subset(struct gpiod_line_config *config,
> - int clock,
> - unsigned int num_offsets,
> - const unsigned int *offsets);
> +bool
> +gpiod_line_config_event_clock_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset);
>
> /**
> - * @brief Get the event clock setting for a given line.
> + * @brief Get the default event clock setting.
> + * @param config Line config object.
> + * @return Event clock setting that would have been used for any offset not
> + * assigned its own direction value.
> + */
> +int gpiod_line_config_get_event_clock_default(struct gpiod_line_config *config);
> +
> +/**
> + * @brief Get the event clock setting for a given offset.
> * @param config Line config object.
> * @param offset Line offset for which to read the event clock setting.
> * @return Event clock setting that would have been used for given offset if
> * the config object was used in a request at the time of the call.
> - * @note If an offset is used for which no config was provided, the function
> - * will return the global default value.
> */
> -int gpiod_line_config_get_event_clock(struct gpiod_line_config *config,
> - unsigned int offset);
> +int gpiod_line_config_get_event_clock_offset(struct gpiod_line_config *config,
> + unsigned int offset);
> +
> +/**
> + * @brief Set the default output value.
> + * @param config Line config object.
> + * @param value New value.
> + */
> +void
> +gpiod_line_config_set_output_value_default(struct gpiod_line_config *config,
> + int value);
>
> /**
> - * @brief Set the output value for a single offset.
> + * @brief Override the output value for a single offset.
> * @param config Line config object.
> * @param offset Offset of the line.
> * @param value Output value to set.
> */
> -void gpiod_line_config_set_output_value(struct gpiod_line_config *config,
> - unsigned int offset, int value);
> +void
> +gpiod_line_config_set_output_value_override(struct gpiod_line_config *config,
> + unsigned int offset, int value);
>
> /**
> - * @brief Set the output values for a set of offsets.
> + * @brief Override the output values for multiple offsets.
> * @param config Line config object.
> - * @param num_values Number of offsets for which to set values.
> - * @param offsets Array of line offsets to set values for.
> + * @param num_values Number of offsets for which to override values.
> + * @param offsets Array of line offsets to override values for.
> * @param values Array of output values associated with the offsets passed in
> * the previous argument.
> */
> @@ -811,14 +931,65 @@ void gpiod_line_config_set_output_values(struct gpiod_line_config *config,
> const unsigned int *offsets,
> const int *values);
>
> +/**
> + * @brief Clear the output value override at given offset.
> + * @param config Line config object.
> + * @param offset Offset of the line for which to clear the override.
> + * @note Does nothing if no override is set for this line.
> + */
> +void
> +gpiod_line_config_clear_output_value_override(struct gpiod_line_config *config,
> + unsigned int offset);
> +
> +/**
> + * @brief Check if the output value is overridden at given offset.
> + * @param config Line config object.
> + * @param offset Offset of the line for which to check the override.
> + * @return True if output value period is overridden at this offset, false
> + * otherwise.
> + */
> +bool
> +gpiod_line_config_output_value_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset);
> +
> +/**
> + * @brief Get the default output value.
> + * @param config Line config object.
> + * @return Output value that would have been used for any offset not
> + * assigned its own output value.
> + */
> +int
> +gpiod_line_config_get_output_value_default(struct gpiod_line_config *config);
> +
> /**
> * @brief Get the output value configured for a given line.
> * @param config Line config object.
> * @param offset Line offset for which to read the value.
> - * @return 1 or 0 if the value was configured for this line, -1 otherwise.
> + * @return Output value that would have been used for given offset if the
> + * config object was used in a request at the time of the call.
> */
> -int gpiod_line_config_get_output_value(struct gpiod_line_config *config,
> - unsigned int offset);
> +int gpiod_line_config_get_output_value_offset(struct gpiod_line_config *config,
> + unsigned int offset);
> +
> +/**
> + * @brief Get the number of offsets for which this config object contains
> + * at least one setting that is different from the defaults.
> + * @param config Line config object.
> + * @return Number of offsets with at least one overridden setting.
> + */
> +unsigned int
> +gpiod_line_config_get_num_overridden_offsets(struct gpiod_line_config *config);
> +
> +/**
> + * @brief Get the offsets for which this config object contains at least one
> + * setting that is different from the defaults.
> + * @param config Line config object.
> + * @param offsets Array to store the offsets. Must hold at least the number
> + * of offsets returned by
> + * ::gpiod_line_config_get_num_overridden_offsets.
> + */
> +void gpiod_line_config_get_overridden_offsets(struct gpiod_line_config *config,
> + unsigned int *offsets);
>
> /**
> * @}
> diff --git a/lib/line-config.c b/lib/line-config.c
> index 346d331..2a656ae 100644
> --- a/lib/line-config.c
> +++ b/lib/line-config.c
> @@ -18,6 +18,7 @@ struct base_config {
> bool active_low : 1;
> unsigned int clock : 2;
> unsigned long debounce_period_us;
> + unsigned int value : 1;
> } GPIOD_PACKED;
>
> #define OVERRIDE_FLAG_DIRECTION GPIOD_BIT(0)
> @@ -27,6 +28,7 @@ struct base_config {
> #define OVERRIDE_FLAG_ACTIVE_LOW GPIOD_BIT(4)
> #define OVERRIDE_FLAG_CLOCK GPIOD_BIT(5)
> #define OVERRIDE_FLAG_DEBOUNCE_PERIOD GPIOD_BIT(6)
> +#define OVERRIDE_FLAG_OUTPUT_VALUE GPIOD_BIT(7)
>
> /*
> * Config overriding the defaults for a single line offset. Only flagged
> @@ -35,16 +37,13 @@ struct base_config {
> struct override_config {
> struct base_config base;
> unsigned int offset;
> - bool value_set : 1;
> - unsigned int value : 1;
> - unsigned int override_flags : 7;
> + unsigned int override_flags : 8;
> } GPIOD_PACKED;
>
> struct gpiod_line_config {
> bool too_complex;
> struct base_config defaults;
> struct override_config overrides[GPIO_V2_LINES_MAX];
> - unsigned int num_overrides;
> };
>
> static void init_base_config(struct base_config *config)
> @@ -58,6 +57,17 @@ static void init_base_config(struct base_config *config)
> config->debounce_period_us = 0;
> }
>
> +static void init_override_config(struct override_config *override)
> +{
> + override->override_flags = 0;
> + init_base_config(&override->base);
> +}
> +
> +static bool override_used(struct override_config *override)
> +{
> + return !!override->override_flags;
> +}
> +
> GPIOD_API struct gpiod_line_config *gpiod_line_config_new(void)
> {
> struct gpiod_line_config *config;
> @@ -82,8 +92,8 @@ GPIOD_API void gpiod_line_config_reset(struct gpiod_line_config *config)
>
> memset(config, 0, sizeof(*config));
> init_base_config(&config->defaults);
> - for (i = 0; i < GPIO_V2_LINE_NUM_ATTRS_MAX; i++)
> - init_base_config(&config->overrides[i].base);
> + for (i = 0; i < GPIO_V2_LINES_MAX; i++)
> + init_override_config(&config->overrides[i]);
> }
>
> static struct override_config *
> @@ -92,7 +102,7 @@ get_override_by_offset(struct gpiod_line_config *config, unsigned int offset)
> struct override_config *override;
> unsigned int i;
>
> - for (i = 0; i < config->num_overrides; i++) {
> + for (i = 0; i < GPIO_V2_LINES_MAX; i++) {
> override = &config->overrides[i];
>
> if (override->offset == offset)
> @@ -103,19 +113,24 @@ get_override_by_offset(struct gpiod_line_config *config, unsigned int offset)
> }
>
> static struct override_config *
> -get_new_override(struct gpiod_line_config *config, unsigned int offset)
> +get_free_override(struct gpiod_line_config *config, unsigned int offset)
> {
> struct override_config *override;
> + unsigned int i;
>
> - if (config->num_overrides == GPIO_V2_LINES_MAX) {
> - config->too_complex = true;
> - return NULL;
> - }
> + for (i = 0; i < GPIO_V2_LINES_MAX; i++) {
> + override = &config->overrides[i];
>
> - override = &config->overrides[config->num_overrides++];
> - override->offset = offset;
> + if (override->override_flags)
> + continue;
>
> - return override;
> + override->offset = offset;
> + return override;
> + }
> +
> + /* No more free overrides. */
> + config->too_complex = true;
> + return NULL;
> }
>
> static struct override_config *
> @@ -129,7 +144,7 @@ get_override_config_for_writing(struct gpiod_line_config *config,
>
> override = get_override_by_offset(config, offset);
> if (!override) {
> - override = get_new_override(config, offset);
> + override = get_free_override(config, offset);
> if (!override)
> return NULL;
> }
> @@ -139,7 +154,7 @@ get_override_config_for_writing(struct gpiod_line_config *config,
>
> static struct base_config *
> get_base_config_for_reading(struct gpiod_line_config *config,
> - unsigned int offset, unsigned int flag)
> + unsigned int offset, unsigned int flag)
> {
> struct override_config *override;
>
> @@ -150,6 +165,35 @@ get_base_config_for_reading(struct gpiod_line_config *config,
> return &config->defaults;
> }
>
> +static void clear_override(struct gpiod_line_config *config,
> + unsigned int offset, int flag)
> +{
> + struct override_config *override;
> +
> + override = get_override_config_for_writing(config, offset);
> + if (!override)
> + return;
> +
> + if (override->override_flags & flag) {
> + override->override_flags &= ~flag;
> +
> + if (!override->override_flags)
> + init_override_config(override);
> + }
> +}
> +
> +static bool check_override(struct gpiod_line_config *config,
> + unsigned int offset, int flag)
> +{
> + struct override_config *override;
> +
> + override = get_override_config_for_writing(config, offset);
> + if (!override)
> + return false;
> +
> + return override->override_flags & flag;
> +}
> +
> static void set_direction(struct base_config *config, int direction)
> {
> switch (direction) {
> @@ -165,38 +209,49 @@ static void set_direction(struct base_config *config, int direction)
> }
>
> GPIOD_API void
> -gpiod_line_config_set_direction(struct gpiod_line_config *config, int direction)
> +gpiod_line_config_set_direction_default(struct gpiod_line_config *config,
> + int direction)
> {
> set_direction(&config->defaults, direction);
> }
>
> GPIOD_API void
> -gpiod_line_config_set_direction_offset(struct gpiod_line_config *config,
> +gpiod_line_config_set_direction_override(struct gpiod_line_config *config,
> int direction, unsigned int offset)
> {
> - gpiod_line_config_set_direction_subset(config, direction, 1, &offset);
> + struct override_config *override;
> +
> + override = get_override_config_for_writing(config, offset);
> + if (!override)
> + return;
> +
> + set_direction(&override->base, direction);
> + override->override_flags |= OVERRIDE_FLAG_DIRECTION;
> }
>
> GPIOD_API void
> -gpiod_line_config_set_direction_subset(struct gpiod_line_config *config,
> - int direction, unsigned int num_offsets,
> - const unsigned int *offsets)
> +gpiod_line_config_clear_direction_override(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> - struct override_config *override;
> - unsigned int i;
> + clear_override(config, offset, OVERRIDE_FLAG_DIRECTION);
> +}
>
> - for (i = 0; i < num_offsets; i++) {
> - override = get_override_config_for_writing(config, offsets[i]);
> - if (!override)
> - return;
> +GPIOD_API bool
> +gpiod_line_config_direction_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset)
> +{
> + return check_override(config, offset, OVERRIDE_FLAG_DIRECTION);
> +}
>
> - set_direction(&override->base, direction);
> - override->override_flags |= OVERRIDE_FLAG_DIRECTION;
> - }
> +GPIOD_API int
> +gpiod_line_config_get_direction_default(struct gpiod_line_config *config)
> +{
> + return config->defaults.direction;
> }
>
> -GPIOD_API int gpiod_line_config_get_direction(struct gpiod_line_config *config,
> - unsigned int offset)
> +GPIOD_API int
> +gpiod_line_config_get_direction_offset(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> struct base_config *base;
>
> @@ -222,39 +277,49 @@ static void set_edge_detection(struct base_config *config, int edge)
> }
>
> GPIOD_API void
> -gpiod_line_config_set_edge_detection(struct gpiod_line_config *config, int edge)
> +gpiod_line_config_set_edge_detection_default(struct gpiod_line_config *config,
> + int edge)
> {
> set_edge_detection(&config->defaults, edge);
> }
>
> GPIOD_API void
> -gpiod_line_config_set_edge_detection_offset(struct gpiod_line_config *config,
> - int edge, unsigned int offset)
> +gpiod_line_config_set_edge_detection_override(struct gpiod_line_config *config,
> + int edge, unsigned int offset)
> {
> - gpiod_line_config_set_edge_detection_subset(config, edge, 1, &offset);
> + struct override_config *override;
> +
> + override = get_override_config_for_writing(config, offset);
> + if (!override)
> + return;
> +
> + set_edge_detection(&override->base, edge);
> + override->override_flags |= OVERRIDE_FLAG_EDGE;
> }
>
> GPIOD_API void
> -gpiod_line_config_set_edge_detection_subset(struct gpiod_line_config *config,
> - int edge, unsigned int num_offsets,
> - const unsigned int *offsets)
> +gpiod_line_config_clear_edge_detection_override(
> + struct gpiod_line_config *config, unsigned int offset)
> {
> - struct override_config *override;
> - unsigned int i;
> + clear_override(config, offset, OVERRIDE_FLAG_EDGE);
> +}
>
> - for (i = 0; i < num_offsets; i++) {
> - override = get_override_config_for_writing(config, offsets[i]);
> - if (!override)
> - return;
> +GPIOD_API bool
> +gpiod_line_config_edge_detection_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset)
> +{
> + return check_override(config, offset, OVERRIDE_FLAG_EDGE);
> +}
>
> - set_edge_detection(&override->base, edge);
> - override->override_flags |= OVERRIDE_FLAG_EDGE;
> - }
> +GPIOD_API int
> +gpiod_line_config_get_edge_detection_default(struct gpiod_line_config *config)
> +{
> + return config->defaults.edge;
> }
>
> GPIOD_API int
> -gpiod_line_config_get_edge_detection(struct gpiod_line_config *config,
> - unsigned int offset)
> +gpiod_line_config_get_edge_detection_offset(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> struct base_config *base;
>
> @@ -279,38 +344,48 @@ static void set_bias(struct base_config *config, int bias)
> }
>
> GPIOD_API void
> -gpiod_line_config_set_bias(struct gpiod_line_config *config, int bias)
> +gpiod_line_config_set_bias_default(struct gpiod_line_config *config, int bias)
> {
> set_bias(&config->defaults, bias);
> }
>
> GPIOD_API void
> -gpiod_line_config_set_bias_offset(struct gpiod_line_config *config,
> +gpiod_line_config_set_bias_override(struct gpiod_line_config *config,
> int bias, unsigned int offset)
> {
> - gpiod_line_config_set_bias_subset(config, bias, 1, &offset);
> + struct override_config *override;
> +
> + override = get_override_config_for_writing(config, offset);
> + if (!override)
> + return;
> +
> + set_bias(&override->base, bias);
> + override->override_flags |= OVERRIDE_FLAG_BIAS;
> }
>
> GPIOD_API void
> -gpiod_line_config_set_bias_subset(struct gpiod_line_config *config,
> - int bias, unsigned int num_offsets,
> - const unsigned int *offsets)
> +gpiod_line_config_clear_bias_override(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> - struct override_config *override;
> - unsigned int i;
> + clear_override(config, offset, OVERRIDE_FLAG_BIAS);
> +}
>
> - for (i = 0; i < num_offsets; i++) {
> - override = get_override_config_for_writing(config, offsets[i]);
> - if (!override)
> - return;
> +GPIOD_API bool
> +gpiod_line_config_bias_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset)
> +{
> + return check_override(config, offset, OVERRIDE_FLAG_BIAS);
> +}
>
> - set_bias(&override->base, bias);
> - override->override_flags |= OVERRIDE_FLAG_BIAS;
> - }
> +GPIOD_API int
> +gpiod_line_config_get_bias_default(struct gpiod_line_config *config)
> +{
> + return config->defaults.bias;
> }
>
> -GPIOD_API int gpiod_line_config_get_bias(struct gpiod_line_config *config,
> - unsigned int offset)
> +GPIOD_API int
> +gpiod_line_config_get_bias_offset(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> struct base_config *base;
>
> @@ -334,38 +409,48 @@ static void set_drive(struct base_config *config, int drive)
> }
>
> GPIOD_API void
> -gpiod_line_config_set_drive(struct gpiod_line_config *config, int drive)
> +gpiod_line_config_set_drive_default(struct gpiod_line_config *config, int drive)
> {
> set_drive(&config->defaults, drive);
> }
>
> GPIOD_API void
> -gpiod_line_config_set_drive_offset(struct gpiod_line_config *config,
> - int drive, unsigned int offset)
> +gpiod_line_config_set_drive_override(struct gpiod_line_config *config,
> + int drive, unsigned int offset)
> {
> - gpiod_line_config_set_drive_subset(config, drive, 1, &offset);
> + struct override_config *override;
> +
> + override = get_override_config_for_writing(config, offset);
> + if (!override)
> + return;
> +
> + set_drive(&override->base, drive);
> + override->override_flags |= OVERRIDE_FLAG_DRIVE;
> }
>
> GPIOD_API void
> -gpiod_line_config_set_drive_subset(struct gpiod_line_config *config,
> - int drive, unsigned int num_offsets,
> - const unsigned int *offsets)
> +gpiod_line_config_clear_drive_override(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> - struct override_config *override;
> - unsigned int i;
> + clear_override(config, offset, OVERRIDE_FLAG_DRIVE);
> +}
>
> - for (i = 0; i < num_offsets; i++) {
> - override = get_override_config_for_writing(config, offsets[i]);
> - if (!override)
> - return;
> +GPIOD_API bool
> +gpiod_line_config_drive_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset)
> +{
> + return check_override(config, offset, OVERRIDE_FLAG_DRIVE);
> +}
>
> - set_drive(&override->base, drive);
> - override->override_flags |= OVERRIDE_FLAG_DRIVE;
> - }
> +GPIOD_API int
> +gpiod_line_config_get_drive_default(struct gpiod_line_config *config)
> +{
> + return config->defaults.drive;
> }
>
> -GPIOD_API int gpiod_line_config_get_drive(struct gpiod_line_config *config,
> - unsigned int offset)
> +GPIOD_API int
> +gpiod_line_config_get_drive_offset(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> struct base_config *base;
>
> @@ -375,49 +460,50 @@ GPIOD_API int gpiod_line_config_get_drive(struct gpiod_line_config *config,
> }
>
> GPIOD_API void
> -gpiod_line_config_set_active_low(struct gpiod_line_config *config)
> +gpiod_line_config_set_active_low_default(struct gpiod_line_config *config,
> + bool active_low)
> {
> - config->defaults.active_low = true;
> + config->defaults.active_low = active_low;
> }
>
> GPIOD_API void
> -gpiod_line_config_set_active_low_offset(struct gpiod_line_config *config,
> - unsigned int offset)
> -{
> - gpiod_line_config_set_active_low_subset(config, 1, &offset);
> -}
> -
> -static void
> -gpiod_line_config_set_active_setting(struct gpiod_line_config *config,
> - unsigned int num_offsets,
> - const unsigned int *offsets,
> - bool active_low)
> +gpiod_line_config_set_active_low_override(struct gpiod_line_config *config,
> + bool active_low,
> + unsigned int offset)
> {
> struct override_config *override;
> - unsigned int i;
>
> - for (i = 0; i < num_offsets; i++) {
> - override = get_override_config_for_writing(config, offsets[i]);
> - if (!override)
> - return;
> + override = get_override_config_for_writing(config, offset);
> + if (!override)
> + return;
>
> - override->base.active_low = active_low;
> - override->override_flags |= OVERRIDE_FLAG_ACTIVE_LOW;
> - }
> + override->base.active_low = active_low;
> + override->override_flags |= OVERRIDE_FLAG_ACTIVE_LOW;
> }
>
> GPIOD_API void
> -gpiod_line_config_set_active_low_subset(struct gpiod_line_config *config,
> - unsigned int num_offsets,
> - const unsigned int *offsets)
> +gpiod_line_config_clear_active_low_override(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> - gpiod_line_config_set_active_setting(config,
> - num_offsets, offsets, true);
> + clear_override(config, offset, OVERRIDE_FLAG_ACTIVE_LOW);
> }
>
> GPIOD_API bool
> -gpiod_line_config_get_active_low(struct gpiod_line_config *config,
> - unsigned int offset)
> +gpiod_line_config_active_low_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset)
> +{
> + return check_override(config, offset, OVERRIDE_FLAG_ACTIVE_LOW);
> +}
> +
> +GPIOD_API bool
> +gpiod_line_config_get_active_low_default(struct gpiod_line_config *config)
> +{
> + return config->defaults.active_low;
> +}
> +
> +GPIOD_API bool
> +gpiod_line_config_get_active_low_offset(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> struct base_config *base;
>
> @@ -428,69 +514,52 @@ gpiod_line_config_get_active_low(struct gpiod_line_config *config,
> }
>
> GPIOD_API void
> -gpiod_line_config_set_active_high(struct gpiod_line_config *config)
> +gpiod_line_config_set_debounce_period_us_default(
> + struct gpiod_line_config *config, unsigned long period)
> {
> - config->defaults.active_low = false;
> + config->defaults.debounce_period_us = period;
> }
>
> GPIOD_API void
> -gpiod_line_config_set_active_high_offset(struct gpiod_line_config *config,
> - unsigned int offset)
> +gpiod_line_config_set_debounce_period_us_override(
> + struct gpiod_line_config *config,
> + unsigned long period,
> + unsigned int offset)
> {
> - gpiod_line_config_set_active_high_subset(config, 1, &offset);
> -}
> + struct override_config *override;
>
> -GPIOD_API void
> -gpiod_line_config_set_active_high_subset(struct gpiod_line_config *config,
> - unsigned int num_offsets,
> - const unsigned int *offsets)
> -{
> - gpiod_line_config_set_active_setting(config,
> - num_offsets, offsets, false);
> -}
> + override = get_override_config_for_writing(config, offset);
> + if (!override)
> + return;
>
> -GPIOD_API void
> -gpiod_line_config_set_debounce_period_us(struct gpiod_line_config *config,
> - unsigned long period)
> -{
> - config->defaults.debounce_period_us = period;
> + override->base.debounce_period_us = period;
> + override->override_flags |= OVERRIDE_FLAG_DEBOUNCE_PERIOD;
> }
>
> -GPIOD_API void
> -gpiod_line_config_set_debounce_period_us_offset(
> +GPIOD_API void gpiod_line_config_clear_debounce_period_us_override(
> struct gpiod_line_config *config,
> - unsigned long period,
> unsigned int offset)
> {
> - gpiod_line_config_set_debounce_period_us_subset(config, period,
> - 1, &offset);
> + clear_override(config, offset, OVERRIDE_FLAG_DEBOUNCE_PERIOD);
> }
>
> -GPIOD_API void
> -gpiod_line_config_set_debounce_period_us_subset(
> +GPIOD_API bool gpiod_line_config_debounce_period_us_is_overridden(
> struct gpiod_line_config *config,
> - unsigned long period,
> - unsigned int num_offsets,
> - const unsigned int *offsets)
> + unsigned int offset)
> {
> - struct override_config *override;
> - unsigned int i, offset;
> -
> - for (i = 0; i < num_offsets; i++) {
> - offset = offsets[i];
> -
> - override = get_override_config_for_writing(config, offset);
> - if (!override)
> - return;
> + return check_override(config, offset, OVERRIDE_FLAG_DEBOUNCE_PERIOD);
> +}
>
> - override->base.debounce_period_us = period;
> - override->override_flags |= OVERRIDE_FLAG_DEBOUNCE_PERIOD;
> - }
> +GPIOD_API unsigned long
> +gpiod_line_config_get_debounce_period_us_default(
> + struct gpiod_line_config *config)
> +{
> + return config->defaults.debounce_period_us;
> }
>
> GPIOD_API unsigned long
> -gpiod_line_config_get_debounce_us_period(struct gpiod_line_config *config,
> - unsigned int offset)
> +gpiod_line_config_get_debounce_period_us_offset(
> + struct gpiod_line_config *config, unsigned int offset)
> {
> struct base_config *base;
>
> @@ -514,38 +583,49 @@ static void set_event_clock(struct base_config *config, int clock)
> }
>
> GPIOD_API void
> -gpiod_line_config_set_event_clock(struct gpiod_line_config *config, int clock)
> +gpiod_line_config_set_event_clock_default(struct gpiod_line_config *config,
> + int clock)
> {
> set_event_clock(&config->defaults, clock);
> }
>
> GPIOD_API void
> -gpiod_line_config_set_event_clock_offset(struct gpiod_line_config *config,
> - int clock, unsigned int offset)
> +gpiod_line_config_set_event_clock_override(struct gpiod_line_config *config,
> + int clock, unsigned int offset)
> {
> - gpiod_line_config_set_event_clock_subset(config, clock, 1, &offset);
> + struct override_config *override;
> +
> + override = get_override_config_for_writing(config, offset);
> + if (!override)
> + return;
> +
> + set_event_clock(&override->base, clock);
> + override->override_flags |= OVERRIDE_FLAG_CLOCK;
> }
>
> GPIOD_API void
> -gpiod_line_config_set_event_clock_subset(struct gpiod_line_config *config,
> - int clock, unsigned int num_offsets,
> - const unsigned int *offsets)
> +gpiod_line_config_clear_event_clock_override(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> - struct override_config *override;
> - unsigned int i;
> + clear_override(config, offset, OVERRIDE_FLAG_CLOCK);
> +}
>
> - for (i = 0; i < num_offsets; i++) {
> - override = get_override_config_for_writing(config, offsets[i]);
> - if (!override)
> - return;
> +GPIOD_API bool
> +gpiod_line_config_event_clock_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset)
> +{
> + return check_override(config, offset, OVERRIDE_FLAG_CLOCK);
> +}
>
> - set_event_clock(&override->base, clock);
> - }
> +GPIOD_API int
> +gpiod_line_config_get_event_clock_default(struct gpiod_line_config *config)
> +{
> + return config->defaults.clock;
> }
>
> GPIOD_API int
> -gpiod_line_config_get_event_clock(struct gpiod_line_config *config,
> - unsigned int offset)
> +gpiod_line_config_get_event_clock_offset(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> struct base_config *base;
>
> @@ -554,17 +634,25 @@ gpiod_line_config_get_event_clock(struct gpiod_line_config *config,
> return base->clock;
> }
>
> -static void set_output_value(struct override_config *override, int value)
> +GPIOD_API void
> +gpiod_line_config_set_output_value_default(struct gpiod_line_config *config,
> + int value)
> {
> - override->value = !!value;
> - override->value_set = true;
> + config->defaults.value = value;
> }
>
> GPIOD_API void
> -gpiod_line_config_set_output_value(struct gpiod_line_config *config,
> - unsigned int offset, int value)
> +gpiod_line_config_set_output_value_override(struct gpiod_line_config *config,
> + unsigned int offset, int value)
> {
> - gpiod_line_config_set_output_values(config, 1, &offset, &value);
> + struct override_config *override;
> +
> + override = get_override_config_for_writing(config, offset);
> + if (!override)
> + return;
> +
> + override->base.value = !!value;
> + override->override_flags |= OVERRIDE_FLAG_OUTPUT_VALUE;
> }
>
> GPIOD_API void
> @@ -573,36 +661,103 @@ gpiod_line_config_set_output_values(struct gpiod_line_config *config,
> const unsigned int *offsets,
> const int *values)
> {
> - struct override_config *override;
> - unsigned int i, offset, val;
> + unsigned int i;
>
> - for (i = 0; i < num_values; i++) {
> - offset = offsets[i];
> - val = values[i];
> + for (i = 0; i < num_values; i++)
> + gpiod_line_config_set_output_value_override(config,
> + offsets[i],
> + values[i]);
> +}
>
> - override = get_override_by_offset(config, offset);
> - if (!override) {
> - override = get_new_override(config, offset);
> - if (!override)
> - return;
> - }
> +GPIOD_API void
> +gpiod_line_config_clear_output_value_override(struct gpiod_line_config *config,
> + unsigned int offset)
> +{
> + clear_override(config, offset, OVERRIDE_FLAG_OUTPUT_VALUE);
> +}
>
> - set_output_value(override, val);
> - }
> +GPIOD_API bool
> +gpiod_line_config_output_value_is_overridden(struct gpiod_line_config *config,
> + unsigned int offset)
> +{
> + return check_override(config, offset, OVERRIDE_FLAG_OUTPUT_VALUE);
> }
>
> GPIOD_API int
> -gpiod_line_config_get_output_value(struct gpiod_line_config *config,
> - unsigned int offset)
> +gpiod_line_config_get_output_value_default(struct gpiod_line_config *config)
> +{
> + return config->defaults.value;
> +}
> +
> +GPIOD_API int
> +gpiod_line_config_get_output_value_offset(struct gpiod_line_config *config,
> + unsigned int offset)
> {
> struct override_config *override;
>
> override = get_override_by_offset(config, offset);
> - if (override && override->value_set)
> - return override->value;
> + if (override && (override->override_flags & OVERRIDE_FLAG_OUTPUT_VALUE))
> + return override->base.value;
>
> - errno = ENXIO;
> - return -1;
> + return config->defaults.value;
> +}
> +
> +static bool base_config_flags_are_equal(struct base_config *base,
> + struct override_config *override)
> +{
> + if (((override->override_flags & OVERRIDE_FLAG_DIRECTION) &&
> + base->direction != override->base.direction) ||
> + ((override->override_flags & OVERRIDE_FLAG_EDGE) &&
> + base->edge != override->base.edge) ||
> + ((override->override_flags & OVERRIDE_FLAG_DRIVE) &&
> + base->drive != override->base.drive) ||
> + ((override->override_flags & OVERRIDE_FLAG_BIAS) &&
> + base->bias != override->base.bias) ||
> + ((override->override_flags & OVERRIDE_FLAG_ACTIVE_LOW) &&
> + base->active_low != override->base.active_low) ||
> + ((override->override_flags & OVERRIDE_FLAG_CLOCK) &&
> + base->clock != override->base.clock))
> + return false;
> +
> + return true;
> +}
> +
> +static bool base_debounce_period_is_equal(struct base_config *base,
> + struct override_config *override)
> +{
> + if ((override->override_flags & OVERRIDE_FLAG_DEBOUNCE_PERIOD) &&
> + base->debounce_period_us != override->base.debounce_period_us)
> + return false;
> +
> + return true;
> +}
> +
> +GPIOD_API unsigned int
> +gpiod_line_config_get_num_overridden_offsets(struct gpiod_line_config *config)
> +{
> + unsigned int i, ret = 0;
> +
> + for (i = 0; i < GPIO_V2_LINES_MAX; i++) {
> + if (override_used(&config->overrides[i]))
> + ret++;
> + }
> +
> + return ret;
> +}
> +
> +GPIOD_API void
> +gpiod_line_config_get_overridden_offsets(struct gpiod_line_config *config,
> + unsigned int *offsets)
> +{
> + struct override_config *override;
> + unsigned int i, j;
> +
> + for (i = 0, j = 0; i < GPIO_V2_LINES_MAX; i++) {
> + override = &config->overrides[i];
> +
> + if (override_used(override))
> + offsets[j++] = override->offset;
> + }
> }
>
> static uint64_t make_kernel_flags(const struct base_config *config)
> @@ -683,10 +838,10 @@ static int find_bitmap_index(unsigned int needle, unsigned int num_lines,
> return -1;
> }
>
> -static int set_kernel_output_values(uint64_t *mask, uint64_t *vals,
> - struct gpiod_line_config *config,
> - unsigned int num_lines,
> - const unsigned int *offsets)
> +static void set_kernel_output_values(uint64_t *mask, uint64_t *vals,
> + struct gpiod_line_config *config,
> + unsigned int num_lines,
> + const unsigned int *offsets)
> {
> struct override_config *override;
> unsigned int i;
> @@ -695,44 +850,69 @@ static int set_kernel_output_values(uint64_t *mask, uint64_t *vals,
> gpiod_line_mask_zero(mask);
> gpiod_line_mask_zero(vals);
>
> - for (i = 0; i < config->num_overrides; i++) {
> - override = &config->overrides[i];
> + if (config->defaults.direction == GPIOD_LINE_DIRECTION_OUTPUT) {
> + /*
> + * Default direction is output - assign the default output
> + * value to all lines. Overrides that may set some lines to
> + * input will be handled later and may re-assign the output
> + * values.
> + */
> + for (i = 0; i < num_lines; i++) {
> + gpiod_line_mask_set_bit(mask, i);
> + gpiod_line_mask_assign_bit(vals, i,
> + config->defaults.value);
> + }
> + } else {
> + /*
> + * Default output value is not output. Iterate over overrides
> + * and set the default output value for those that override the
> + * direction to output. Don't touch the ones which override
> + * the output value.
> + */
> + for (i = 0; i < GPIO_V2_LINES_MAX; i++) {
> + override = &config->overrides[i];
> +
> + if (override->base.direction !=
> + GPIOD_LINE_DIRECTION_OUTPUT ||
> + !(override->override_flags &
> + OVERRIDE_FLAG_DIRECTION) ||
> + (override->override_flags &
> + OVERRIDE_FLAG_OUTPUT_VALUE))
> + continue;
>
> - if (override->value_set) {
> idx = find_bitmap_index(override->offset,
> num_lines, offsets);
> - if (idx < 0) {
> - errno = EINVAL;
> - return -1;
> - }
> + if (idx < 0)
> + continue;
>
> gpiod_line_mask_set_bit(mask, idx);
> gpiod_line_mask_assign_bit(vals, idx,
> - !!override->value);
> + !!config->defaults.value);
> }
> }
>
> - return 0;
> -}
> + /*
> + * Finally iterate over the overrides again and set the overridden
> + * output values.
> + */
> + for (i = 0; i < GPIO_V2_LINES_MAX; i++) {
> + override = &config->overrides[i];
>
> -static bool base_config_flags_are_equal(struct base_config *base,
> - struct override_config *override)
> -{
> - if (((override->override_flags & OVERRIDE_FLAG_DIRECTION) &&
> - base->direction != override->base.direction) ||
> - ((override->override_flags & OVERRIDE_FLAG_EDGE) &&
> - base->edge != override->base.edge) ||
> - ((override->override_flags & OVERRIDE_FLAG_DRIVE) &&
> - base->drive != override->base.drive) ||
> - ((override->override_flags & OVERRIDE_FLAG_BIAS) &&
> - base->bias != override->base.bias) ||
> - ((override->override_flags & OVERRIDE_FLAG_ACTIVE_LOW) &&
> - base->active_low != override->base.active_low) ||
> - ((override->override_flags & OVERRIDE_FLAG_CLOCK) &&
> - base->clock != override->base.clock))
> - return false;
> + if (!(override->override_flags & OVERRIDE_FLAG_OUTPUT_VALUE))
> + continue;
>
> - return true;
> + if (config->defaults.direction != GPIOD_LINE_DIRECTION_OUTPUT &&
> + (!(override->override_flags & OVERRIDE_FLAG_DIRECTION) ||
> + override->base.direction != GPIOD_LINE_DIRECTION_OUTPUT))
> + continue;
> +
> + idx = find_bitmap_index(override->offset, num_lines, offsets);
> + if (idx < 0)
> + continue;
> +
> + gpiod_line_mask_set_bit(mask, idx);
> + gpiod_line_mask_assign_bit(vals, idx, !!override->base.value);
> + }
> }
>
> static bool override_config_flags_are_equal(struct override_config *a,
> @@ -771,16 +951,6 @@ static void set_base_config_flags(struct gpio_v2_line_attribute *attr,
> attr->flags = make_kernel_flags(&base);
> }
>
> -static bool base_debounce_period_is_equal(struct base_config *base,
> - struct override_config *override)
> -{
> - if ((override->override_flags & OVERRIDE_FLAG_DEBOUNCE_PERIOD) &&
> - base->debounce_period_us != override->base.debounce_period_us)
> - return false;
> -
> - return true;
> -}
> -
> static bool override_config_debounce_period_is_equal(struct override_config *a,
> struct override_config *b)
> {
> @@ -801,36 +971,40 @@ set_base_config_debounce_period(struct gpio_v2_line_attribute *attr,
> attr->debounce_period_us = override->base.debounce_period_us;
> }
>
> -static int set_kernel_attr_mask(uint64_t *out, const uint64_t *in,
> - unsigned int num_lines,
> - const unsigned int *offsets,
> - const struct gpiod_line_config *config)
> +static void set_kernel_attr_mask(uint64_t *out, const uint64_t *in,
> + unsigned int num_lines,
> + const unsigned int *offsets,
> + struct gpiod_line_config *config)
> {
> + struct override_config *override;
> unsigned int i, j;
> int off;
>
> gpiod_line_mask_zero(out);
>
> - for (i = 0; i < config->num_overrides; i++) {
> - if (!gpiod_line_mask_test_bit(in, i))
> + for (i = 0; i < GPIO_V2_LINES_MAX; i++) {
> + override = &config->overrides[i];
> +
> + if (!override_used(override) ||
> + !gpiod_line_mask_test_bit(in, i))
> continue;
>
> for (j = 0, off = -1; j < num_lines; j++) {
> - if (offsets[j] == config->overrides[i].offset) {
> + if (offsets[j] == override->offset) {
> off = j;
> break;
> }
> }
>
> - if (off < 0) {
> - errno = EINVAL;
> - return -1;
> - }
> + /*
> + * Overridden offsets that are not in the list of offsets to
> + * request (or already requested) are silently ignored.
> + */
> + if (off < 0)
> + continue;
>
> gpiod_line_mask_set_bit(out, off);
> }
> -
> - return 0;
> }
>
> static int process_overrides(struct gpiod_line_config *config,
> @@ -852,11 +1026,18 @@ static int process_overrides(struct gpiod_line_config *config,
> struct override_config *current, *next;
> unsigned int i, j;
>
> - for (i = 0; i < config->num_overrides; i++) {
> - if (gpiod_line_mask_test_bit(&processed, i))
> + for (i = 0; i < GPIO_V2_LINES_MAX; i++) {
> + current = &config->overrides[i];
> +
> + if (!override_used(current) ||
> + gpiod_line_mask_test_bit(&processed, i))
> continue;
>
> - current = &config->overrides[i];
> + if (*attr_idx == GPIO_V2_LINE_NUM_ATTRS_MAX) {
> + errno = E2BIG;
> + return -1;
> + }
> +
> gpiod_line_mask_set_bit(&processed, i);
>
> if (defaults_equal_func(&config->defaults, current))
> @@ -865,12 +1046,13 @@ static int process_overrides(struct gpiod_line_config *config,
> marked = 0;
> gpiod_line_mask_set_bit(&marked, i);
>
> - for (j = i + 1; j < config->num_overrides; j++) {
> - if (gpiod_line_mask_test_bit(&processed, j))
> - continue;
> -
> + for (j = i + 1; j < GPIO_V2_LINES_MAX; j++) {
> next = &config->overrides[j];
>
> + if (!override_used(next) ||
> + gpiod_line_mask_test_bit(&processed, j))
> + continue;
> +
> if (override_equal_func(current, next)) {
> gpiod_line_mask_set_bit(&marked, j);
> gpiod_line_mask_set_bit(&processed, j);
> @@ -878,10 +1060,6 @@ static int process_overrides(struct gpiod_line_config *config,
> }
>
> attr = &cfgbuf->attrs[(*attr_idx)++];
> - if (*attr_idx == GPIO_V2_LINE_NUM_ATTRS_MAX) {
> - errno = E2BIG;
> - return -1;
> - }
>
> set_kernel_attr_mask(&mask, &marked,
> num_lines, offsets, config);
> @@ -892,14 +1070,31 @@ static int process_overrides(struct gpiod_line_config *config,
> return 0;
> }
>
> +static bool has_at_least_one_output_direction(struct gpiod_line_config *config)
> +{
> + struct override_config *override;
> + unsigned int i;
> +
> + if (config->defaults.direction == GPIOD_LINE_DIRECTION_OUTPUT)
> + return true;
> +
> + for (i = 0; i < GPIO_V2_LINES_MAX; i++) {
> + override = &config->overrides[i];
> +
> + if (override->base.direction == GPIOD_LINE_DIRECTION_OUTPUT)
> + return true;
> + }
> +
> + return false;
> +}
> +
> int gpiod_line_config_to_kernel(struct gpiod_line_config *config,
> struct gpio_v2_line_config *cfgbuf,
> unsigned int num_lines,
> const unsigned int *offsets)
> {
> struct gpio_v2_line_config_attribute *attr;
> - struct override_config *override;
> - unsigned int attr_idx = 0, i;
> + unsigned int attr_idx = 0;
> uint64_t mask, values;
> int ret;
>
> @@ -907,26 +1102,19 @@ int gpiod_line_config_to_kernel(struct gpiod_line_config *config,
> goto err_2big;
>
> /*
> - * First check if we have at least one default output value configured.
> + * First check if we have at least one line configured in output mode.
> * If so, let's take one attribute for the default values.
> */
> - for (i = 0; i < config->num_overrides; i++) {
> - override = &config->overrides[i];
> -
> - if (override->value_set) {
> - attr = &cfgbuf->attrs[attr_idx++];
> - attr->attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
> + if (has_at_least_one_output_direction(config)) {
> + attr = &cfgbuf->attrs[attr_idx++];
> + attr->attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
>
> - ret = set_kernel_output_values(&mask, &values, config,
> - num_lines, offsets);
> - if (ret)
> - return ret;
> + set_kernel_output_values(&mask, &values, config,
> + num_lines, offsets);
>
> - attr->attr.values = values;
> - attr->mask = mask;
> + attr->attr.values = values;
> + attr->mask = mask;
>
> - break;
> - }
> }
>
> /* If we have a default debounce period - use another attribute. */
> diff --git a/tools/gpioget.c b/tools/gpioget.c
> index 965af3b..112257c 100644
> --- a/tools/gpioget.c
> +++ b/tools/gpioget.c
> @@ -110,13 +110,13 @@ int main(int argc, char **argv)
> if (!line_cfg)
> die_perror("unable to allocate the line config structure");
>
> - gpiod_line_config_set_direction(line_cfg, direction);
> + gpiod_line_config_set_direction_default(line_cfg, direction);
>
> if (bias)
> - gpiod_line_config_set_bias(line_cfg, bias);
> + gpiod_line_config_set_bias_default(line_cfg, bias);
>
> if (active_low)
> - gpiod_line_config_set_active_low(line_cfg);
> + gpiod_line_config_set_active_low_default(line_cfg, true);
>
> req_cfg = gpiod_request_config_new();
> if (!req_cfg)
> diff --git a/tools/gpiomon.c b/tools/gpiomon.c
> index c09f2f5..31ea294 100644
> --- a/tools/gpiomon.c
> +++ b/tools/gpiomon.c
> @@ -259,10 +259,10 @@ int main(int argc, char **argv)
> die_perror("unable to allocate the line config structure");
>
> if (bias)
> - gpiod_line_config_set_bias(line_cfg, bias);
> + gpiod_line_config_set_bias_default(line_cfg, bias);
> if (active_low)
> - gpiod_line_config_set_active_low(line_cfg);
> - gpiod_line_config_set_edge_detection(line_cfg, edge);
> + gpiod_line_config_set_active_low_default(line_cfg, true);
> + gpiod_line_config_set_edge_detection_default(line_cfg, edge);
>
> req_cfg = gpiod_request_config_new();
> if (!req_cfg)
> diff --git a/tools/gpioset.c b/tools/gpioset.c
> index 55fcfe9..c27525a 100644
> --- a/tools/gpioset.c
> +++ b/tools/gpioset.c
> @@ -296,12 +296,13 @@ int main(int argc, char **argv)
> die_perror("unable to allocate the line config structure");
>
> if (bias)
> - gpiod_line_config_set_bias(line_cfg, bias);
> + gpiod_line_config_set_bias_default(line_cfg, bias);
> if (drive)
> - gpiod_line_config_set_drive(line_cfg, drive);
> + gpiod_line_config_set_drive_default(line_cfg, drive);
> if (active_low)
> - gpiod_line_config_set_active_low(line_cfg);
> - gpiod_line_config_set_direction(line_cfg, GPIOD_LINE_DIRECTION_OUTPUT);
> + gpiod_line_config_set_active_low_default(line_cfg, true);
> + gpiod_line_config_set_direction_default(line_cfg,
> + GPIOD_LINE_DIRECTION_OUTPUT);
> gpiod_line_config_set_output_values(line_cfg, num_lines,
> offsets, values);
>
> --
> 2.30.1
>
next prev parent reply other threads:[~2022-02-22 11:40 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-02-21 15:40 [libgpiod v2][PATCH v2 0/2] libgpiod v2: rewrite tests for the C library Bartosz Golaszewski
2022-02-21 15:40 ` [libgpiod v2][PATCH v2 1/2] line-config: expose the override logic to users Bartosz Golaszewski
2022-02-22 11:40 ` Kent Gibson [this message]
2022-02-23 11:10 ` Andy Shevchenko
2022-03-02 12:59 ` Bartosz Golaszewski
2022-03-02 14:32 ` Kent Gibson
2022-03-02 15:54 ` Bartosz Golaszewski
2022-02-21 15:40 ` [libgpiod v2][PATCH v2 2/2] tests: rewrite core C tests using libgpiosim Bartosz Golaszewski
2022-02-23 10:18 ` Kent Gibson
2022-02-24 20:33 ` Bartosz Golaszewski
2022-02-24 23:52 ` Kent Gibson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220222114044.GA37327@sol \
--to=warthog618@gmail.com \
--cc=andriy.shevchenko@linux.intel.com \
--cc=brgl@bgdev.pl \
--cc=linus.walleij@linaro.org \
--cc=linux-gpio@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).