@@ -785,6 +785,31 @@ struct gpiod_line_settings *
gpiod_line_config_get_line_settings(struct gpiod_line_config *config,
unsigned int offset);
+/**
+ * @brief Set output values for a number of lines.
+ * @param config Line config object.
+ * @param values Buffer containing the output values.
+ * @param num_values Number of values in the buffer.
+ * @return 0 on success, -1 on error.
+ *
+ * This is a helper that allows users to set multiple (potentially different)
+ * output values at once while using the same line settings object. Instead of
+ * modifying the output value in the settings object and calling
+ * ::gpiod_line_config_add_line_settings multiple times, we can specify the
+ * settings, add them for a set of offsets and then call this function to
+ * set the output values.
+ *
+ * Values set by this function override whatever values were specified in the
+ * regular line settings.
+ *
+ * Each value must be associated with the line identified by the corresponding
+ * entry in the offset array filled by
+ * ::gpiod_line_request_get_requested_offsets.
+ */
+int gpiod_line_config_set_output_values(struct gpiod_line_config *config,
+ const enum gpiod_line_value *values,
+ size_t num_values);
+
/**
* @brief Get the number of configured line offsets.
* @param config Line config object.
@@ -25,6 +25,8 @@ struct per_line_config {
struct gpiod_line_config {
struct per_line_config line_configs[LINES_MAX];
size_t num_configs;
+ enum gpiod_line_value output_values[LINES_MAX];
+ size_t num_output_values;
struct settings_node *sref_list;
};
@@ -136,23 +138,60 @@ GPIOD_API struct gpiod_line_settings *
gpiod_line_config_get_line_settings(struct gpiod_line_config *config,
unsigned int offset)
{
+ struct gpiod_line_settings *settings;
struct per_line_config *per_line;
size_t i;
+ int ret;
assert(config);
for (i = 0; i < config->num_configs; i++) {
per_line = &config->line_configs[i];
- if (per_line->offset == offset)
- return gpiod_line_settings_copy(
+ if (per_line->offset == offset) {
+ settings = gpiod_line_settings_copy(
per_line->node->settings);
+ if (!settings)
+ return NULL;
+
+ /*
+ * If a global output value was set for this line - use
+ * it and override the one stored in settings.
+ */
+ if (config->num_output_values > i) {
+ ret = gpiod_line_settings_set_output_value(
+ settings,
+ config->output_values[i]);
+ if (ret) {
+ gpiod_line_settings_free(settings);
+ return NULL;
+ }
+ }
+
+ return settings;
+ }
}
errno = ENOENT;
return NULL;
}
+GPIOD_API int
+gpiod_line_config_set_output_values(struct gpiod_line_config *config,
+ const enum gpiod_line_value *values,
+ size_t num_values)
+{
+ if (num_values > LINES_MAX) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ memcpy(config->output_values, values, num_values * sizeof(*values));
+ config->num_output_values = num_values;
+
+ return 0;
+}
+
GPIOD_API size_t
gpiod_line_config_get_num_configured_offsets(struct gpiod_line_config *config)
{
@@ -206,6 +245,13 @@ static bool has_at_least_one_output_direction(struct gpiod_line_config *config)
return false;
}
+static void set_output_value(uint64_t *vals, size_t bit,
+ enum gpiod_line_value value)
+{
+ gpiod_line_mask_assign_bit(vals, bit,
+ value == GPIOD_LINE_VALUE_ACTIVE ? 1 : 0);
+}
+
static void set_kernel_output_values(uint64_t *mask, uint64_t *vals,
struct gpiod_line_config *config)
{
@@ -227,8 +273,14 @@ static void set_kernel_output_values(uint64_t *mask, uint64_t *vals,
gpiod_line_mask_set_bit(mask, i);
value = gpiod_line_settings_get_output_value(
per_line->node->settings);
- gpiod_line_mask_assign_bit(
- vals, i, value == GPIOD_LINE_VALUE_ACTIVE ? 1 : 0);
+ set_output_value(vals, i, value);
+ }
+
+ /* "Global" output values override the ones from per-line settings. */
+ for (i = 0; i < config->num_output_values; i++) {
+ gpiod_line_mask_set_bit(mask, i);
+ value = config->output_values[i];
+ set_output_value(vals, i, value);
}
}
@@ -136,6 +136,16 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_edge_event_buffer,
_settings; \
})
+#define gpiod_test_line_config_set_output_values_or_fail(_line_cfg, _values, \
+ _num_values) \
+ do { \
+ gint _ret = gpiod_line_config_set_output_values(_line_cfg, \
+ _values, \
+ _num_values); \
+ g_assert_cmpint(_ret, ==, 0); \
+ gpiod_test_return_if_failed(); \
+ } while (0)
+
#define gpiod_test_create_request_config_or_fail() \
({ \
struct gpiod_request_config *_config = \
@@ -280,3 +280,70 @@ GPIOD_TEST_CASE(get_more_offsets_than_configured)
g_assert_cmpuint(retrieved[2], ==, 2);
g_assert_cmpuint(retrieved[3], ==, 3);
}
+
+GPIOD_TEST_CASE(set_global_output_values)
+{
+ static const guint offsets[] = { 0, 1, 2, 3 };
+ static const enum gpiod_line_value values[] = {
+ GPIOD_LINE_VALUE_ACTIVE,
+ GPIOD_LINE_VALUE_INACTIVE,
+ GPIOD_LINE_VALUE_ACTIVE,
+ GPIOD_LINE_VALUE_INACTIVE,
+ };
+
+ g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 4, NULL);
+ g_autoptr(struct_gpiod_line_settings) settings = NULL;
+ g_autoptr(struct_gpiod_line_config) config = NULL;
+ g_autoptr(struct_gpiod_line_request) request = NULL;
+ g_autoptr(struct_gpiod_chip) chip = NULL;
+
+ chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim));
+ settings = gpiod_test_create_line_settings_or_fail();
+ config = gpiod_test_create_line_config_or_fail();
+
+ gpiod_line_settings_set_direction(settings,
+ GPIOD_LINE_DIRECTION_OUTPUT);
+ gpiod_test_line_config_add_line_settings_or_fail(config, offsets, 4,
+ settings);
+ gpiod_test_line_config_set_output_values_or_fail(config, values, 4);
+
+ request = gpiod_test_request_lines_or_fail(chip, NULL, config);
+
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 0), ==,
+ GPIOD_LINE_VALUE_ACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 1), ==,
+ GPIOD_LINE_VALUE_INACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 2), ==,
+ GPIOD_LINE_VALUE_ACTIVE);
+ g_assert_cmpint(g_gpiosim_chip_get_value(sim, 3), ==,
+ GPIOD_LINE_VALUE_INACTIVE);
+}
+
+GPIOD_TEST_CASE(read_back_global_output_values)
+{
+ static const guint offsets[] = { 0, 1, 2, 3 };
+ static const enum gpiod_line_value values[] = {
+ GPIOD_LINE_VALUE_ACTIVE,
+ GPIOD_LINE_VALUE_INACTIVE,
+ GPIOD_LINE_VALUE_ACTIVE,
+ GPIOD_LINE_VALUE_INACTIVE,
+ };
+
+ g_autoptr(struct_gpiod_line_settings) settings = NULL;
+ g_autoptr(struct_gpiod_line_settings) retrieved = NULL;
+ g_autoptr(struct_gpiod_line_config) config = NULL;
+
+ settings = gpiod_test_create_line_settings_or_fail();
+ config = gpiod_test_create_line_config_or_fail();
+
+ gpiod_line_settings_set_direction(settings,
+ GPIOD_LINE_DIRECTION_OUTPUT);
+ gpiod_line_settings_set_output_value(settings, GPIOD_LINE_VALUE_ACTIVE);
+ gpiod_test_line_config_add_line_settings_or_fail(config, offsets, 4,
+ settings);
+ gpiod_test_line_config_set_output_values_or_fail(config, values, 4);
+
+ retrieved = gpiod_test_line_config_get_line_settings_or_fail(config, 1);
+ g_assert_cmpint(gpiod_line_settings_get_output_value(retrieved), ==,
+ GPIOD_LINE_VALUE_INACTIVE);
+}