Linux-GPIO Archive on lore.kernel.org
 help / color / Atom feed
* [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG
@ 2019-11-25 14:31 Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 01/14] core: add support for bias flags Kent Gibson
                   ` (13 more replies)
  0 siblings, 14 replies; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Changes v2 -> v3:
 - rebase on core: deprecate gpiod_needs_update()
 - rework needs_update related logic in core: add support for SET_CONFIG

Changes v1 -> v2:
 - address all v1 review comments
 - tools accept bias flag field rather than individual flags
 - add tests for tool changes
 
This patch series adds support for changes to the GPIO uAPI that are on
track to be included in the v5.5 kernel.  There are two components to the
uAPI changes - the addition of bias flags and a new SET_CONFIG ioctl.  This
series adds support to the libgpiod API, and to both C++ and Python
bindings, for both of those components.

The libgpiod tools are also updated, where appropriate, to support the bias
flags.

The series is based on the current libgpiod master:
 master@2964441 core: deprecate gpiod_needs_update()

Kent Gibson (14):
  core: add support for bias flags
  tests: add tests for bias flags
  bindings: cxx: add support for bias flags
  bindings: cxx: tests: add tests for bias flags
  bindings: python: add support for bias flags
  bindings: python: tests: add tests for bias flags
  core: add support for SET_CONFIG
  tests: add tests for SET_CONFIG
  bindings: cxx: add support for SET_CONFIG
  bindings: cxx: tests: add tests for SET_CONFIG methods
  bindings: python: add support for SET_CONFIG
  bindings: python: tests: add tests for SET_CONFIG methods
  tools: add support for bias flags
  tools: add tests for bias and drive flags

 bindings/cxx/gpiod.hpp                 |  81 +++++
 bindings/cxx/line.cpp                  |  56 +++
 bindings/cxx/line_bulk.cpp             |  89 +++++
 bindings/cxx/tests/tests-line.cpp      | 215 ++++++++++++
 bindings/python/gpiodmodule.c          | 463 +++++++++++++++++++++++-
 bindings/python/tests/gpiod_py_test.py | 254 ++++++++++++++
 include/gpiod.h                        | 306 ++++++++++++++++
 lib/core.c                             | 219 +++++++++++-
 lib/ctxless.c                          | 114 +++++-
 tests/tests-ctxless.c                  |  64 +++-
 tests/tests-event.c                    | 120 +++++++
 tests/tests-line.c                     | 468 ++++++++++++++++++++++++-
 tools/gpio-tools-test.bats             | 139 ++++++++
 tools/gpioget.c                        |  32 +-
 tools/gpiomon.c                        |  36 +-
 tools/gpioset.c                        |  54 ++-
 16 files changed, 2669 insertions(+), 41 deletions(-)

-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 01/14] core: add support for bias flags
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
@ 2019-11-25 14:31 ` Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 02/14] tests: add tests " Kent Gibson
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Extend the libgpiod API to support the bias flags recently added to the
kernel GPIO uAPI.  The core change is the addition of
GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE, GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP
and GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN flags to be passed into
line_request functions, and the addition of gpiod_line_bias to return the
bias state of lines.

Variants of the ctxless functions that accept an active_low flag
are added to also accept other flags. The variant names add a "_ext"
suffix to the name of the original function.

Based on initial work by Drew Fustini <drew@pdp7.com>.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 include/gpiod.h | 183 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/core.c      |  32 ++++++---
 lib/ctxless.c   | 114 +++++++++++++++++++++++++++---
 3 files changed, 311 insertions(+), 18 deletions(-)

diff --git a/include/gpiod.h b/include/gpiod.h
index 3fc1c0c..86c3ea9 100644
--- a/include/gpiod.h
+++ b/include/gpiod.h
@@ -84,6 +84,22 @@ struct gpiod_line_bulk;
  * the need to use the gpiod_* structures or to keep track of resources.
  */
 
+/**
+ * @brief Miscellaneous GPIO flags.
+ */
+enum {
+	GPIOD_CTXLESS_FLAG_OPEN_DRAIN		= GPIOD_BIT(0),
+	/**< The line is an open-drain port. */
+	GPIOD_CTXLESS_FLAG_OPEN_SOURCE		= GPIOD_BIT(1),
+	/**< The line is an open-source port. */
+	GPIOD_CTXLESS_FLAG_BIAS_DISABLE		= GPIOD_BIT(2),
+	/**< The line has neither either pull-up nor pull-down resistor */
+	GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN	= GPIOD_BIT(3),
+	/**< The line has pull-down resistor enabled */
+	GPIOD_CTXLESS_FLAG_BIAS_PULL_UP		= GPIOD_BIT(4),
+	/**< The line has pull-up resistor enabled */
+};
+
 /**
  * @brief Read current value from a single GPIO line.
  * @param device Name, path, number or label of the gpiochip.
@@ -95,6 +111,19 @@ struct gpiod_line_bulk;
 int gpiod_ctxless_get_value(const char *device, unsigned int offset,
 			    bool active_low, const char *consumer) GPIOD_API;
 
+/**
+ * @brief Read current value from a single GPIO line.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offset Offset of the GPIO line.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param flags The flags for the line.
+ * @return 0 or 1 (GPIO value) if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_get_value_ext(const char *device, unsigned int offset,
+				bool active_low, const char *consumer,
+				int flags) GPIOD_API;
+
 /**
  * @brief Read current values from a set of GPIO lines.
  * @param device Name, path, number or label of the gpiochip.
@@ -110,6 +139,23 @@ int gpiod_ctxless_get_value_multiple(const char *device,
 				     unsigned int num_lines, bool active_low,
 				     const char *consumer) GPIOD_API;
 
+/**
+ * @brief Read current values from a set of GPIO lines.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offsets Array of offsets of lines whose values should be read.
+ * @param values Buffer in which the values will be stored.
+ * @param num_lines Number of lines, must be > 0.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param flags The flags for the lines.
+ * @return 0 if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_get_value_multiple_ext(const char *device,
+					 const unsigned int *offsets,
+					 int *values, unsigned int num_lines,
+					 bool active_low, const char *consumer,
+					 int flags) GPIOD_API;
+
 /**
  * @brief Simple set value callback signature.
  */
@@ -133,6 +179,26 @@ int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value,
 			    gpiod_ctxless_set_value_cb cb,
 			    void *data) GPIOD_API;
 
+/**
+ * @brief Set value of a single GPIO line.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offset The offset of the GPIO line.
+ * @param value New value (0 or 1).
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param cb Optional callback function that will be called right after setting
+ *           the value. Users can use this, for example, to pause the execution
+ *           after toggling a GPIO.
+ * @param data Optional user data that will be passed to the callback function.
+ * @param flags The flags for the line.
+ * @return 0 if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_set_value_ext(const char *device, unsigned int offset,
+				int value, bool active_low,
+				const char *consumer,
+				gpiod_ctxless_set_value_cb cb,
+				void *data, int flags) GPIOD_API;
+
 /**
  * @brief Set values of multiple GPIO lines.
  * @param device Name, path, number or label of the gpiochip.
@@ -153,6 +219,29 @@ int gpiod_ctxless_set_value_multiple(const char *device,
 				     gpiod_ctxless_set_value_cb cb,
 				     void *data) GPIOD_API;
 
+/**
+ * @brief Set values of multiple GPIO lines.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param offsets Array of offsets of lines the values of which should be set.
+ * @param values Array of integers containing new values.
+ * @param num_lines Number of lines, must be > 0.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param cb Optional callback function that will be called right after setting
+ *           all values. Works the same as in ::gpiod_ctxless_set_value.
+ * @param data Optional user data that will be passed to the callback function.
+ * @param flags The flags for the lines.
+ * @return 0 if the operation succeeds, -1 on error.
+ */
+int gpiod_ctxless_set_value_multiple_ext(const char *device,
+					 const unsigned int *offsets,
+					 const int *values,
+					 unsigned int num_lines,
+					 bool active_low,
+					 const char *consumer,
+					 gpiod_ctxless_set_value_cb cb,
+					 void *data, int flags) GPIOD_API;
+
 /**
  * @brief Event types that the ctxless event monitor can wait for.
  */
@@ -327,6 +416,31 @@ int gpiod_ctxless_event_monitor(const char *device, int event_type,
 				gpiod_ctxless_event_handle_cb event_cb,
 				void *data) GPIOD_API;
 
+/**
+ * @brief Wait for events on a single GPIO line.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param event_type Type of events to listen for.
+ * @param offset GPIO line offset to monitor.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param timeout Maximum wait time for each iteration.
+ * @param poll_cb Callback function to call when waiting for events.
+ * @param event_cb Callback function to call for each line event.
+ * @param data User data passed to the callback.
+ * @param flags The flags for the line.
+ * @return 0 if no errors were encountered, -1 if an error occurred.
+ * @note The way the ctxless event loop works is described in detail in
+ *       ::gpiod_ctxless_event_monitor_multiple - this is just a wrapper aound
+ *       this routine which calls it for a single GPIO line.
+ */
+int gpiod_ctxless_event_monitor_ext(const char *device, int event_type,
+				    unsigned int offset, bool active_low,
+				    const char *consumer,
+				    const struct timespec *timeout,
+				    gpiod_ctxless_event_poll_cb poll_cb,
+				    gpiod_ctxless_event_handle_cb event_cb,
+				    void *data, int flags) GPIOD_API;
+
 /**
  * @brief Wait for events on multiple GPIO lines.
  * @param device Name, path, number or label of the gpiochip.
@@ -366,6 +480,47 @@ int gpiod_ctxless_event_monitor_multiple(
 			gpiod_ctxless_event_handle_cb event_cb,
 			void *data) GPIOD_API;
 
+/**
+ * @brief Wait for events on multiple GPIO lines.
+ * @param device Name, path, number or label of the gpiochip.
+ * @param event_type Type of events to listen for.
+ * @param offsets Array of GPIO line offsets to monitor.
+ * @param num_lines Number of lines to monitor.
+ * @param active_low The active state of this line - true if low.
+ * @param consumer Name of the consumer.
+ * @param timeout Maximum wait time for each iteration.
+ * @param poll_cb Callback function to call when waiting for events. Can
+ *                be NULL.
+ * @param event_cb Callback function to call on event occurrence.
+ * @param data User data passed to the callback.
+ * @param flags The flags for the lines.
+ * @return 0 no errors were encountered, -1 if an error occurred.
+ * @note The poll callback can be NULL in which case the routine will fall
+ *       back to a basic, ppoll() based callback.
+ *
+ * Internally this routine opens the GPIO chip, requests the set of lines for
+ * the type of events specified in the event_type parameter and calls the
+ * polling callback in a loop. The role of the polling callback is to detect
+ * input events on a set of file descriptors and notify the caller about the
+ * fds ready for reading.
+ *
+ * The ctxless event loop then reads each queued event from marked descriptors
+ * and calls the event callback. Both callbacks can stop the loop at any
+ * point.
+ *
+ * The poll_cb argument can be NULL in which case the function falls back to
+ * a default, ppoll() based callback.
+ */
+int gpiod_ctxless_event_monitor_multiple_ext(
+			const char *device, int event_type,
+			const unsigned int *offsets,
+			unsigned int num_lines, bool active_low,
+			const char *consumer, const struct timespec *timeout,
+			gpiod_ctxless_event_poll_cb poll_cb,
+			gpiod_ctxless_event_handle_cb event_cb,
+			void *data, int flags) GPIOD_API;
+
+
 /**
  * @brief Determine the chip name and line offset of a line with given name.
  * @param name The name of the GPIO line to lookup.
@@ -658,6 +813,20 @@ enum {
 	/**< The active state of a GPIO is active-low. */
 };
 
+/**
+ * @brief Possible internal bias settings.
+ */
+enum {
+	GPIOD_LINE_BIAS_AS_IS = 1,
+	/**< The internal bias state is unknown. */
+	GPIOD_LINE_BIAS_DISABLE,
+	/**< The internal bias is disabled. */
+	GPIOD_LINE_BIAS_PULL_UP,
+	/**< The internal pull-up bias is enabled. */
+	GPIOD_LINE_BIAS_PULL_DOWN,
+	/**< The internal pull-down bias is enabled. */
+};
+
 /**
  * @brief Read the GPIO line offset.
  * @param line GPIO line object.
@@ -697,6 +866,14 @@ int gpiod_line_direction(struct gpiod_line *line) GPIOD_API;
  */
 int gpiod_line_active_state(struct gpiod_line *line) GPIOD_API;
 
+/**
+ * @brief Read the GPIO line bias setting.
+ * @param line GPIO line object.
+ * @return Returns GPIOD_LINE_BIAS_PULL_UP, GPIOD_LINE_BIAS_PULL_DOWN,
+ *         GPIOD_LINE_BIAS_DISABLE or GPIOD_LINE_BIAS_AS_IS.
+ */
+int gpiod_line_bias(struct gpiod_line *line) GPIOD_API;
+
 /**
  * @brief Check if the line is currently in use.
  * @param line GPIO line object.
@@ -787,6 +964,12 @@ enum {
 	/**< 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_DISABLE	= 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 */
 };
 
 /**
diff --git a/lib/core.c b/lib/core.c
index a21918c..0465de9 100644
--- a/lib/core.c
+++ b/lib/core.c
@@ -36,9 +36,7 @@ struct gpiod_line {
 	unsigned int offset;
 	int direction;
 	int active_state;
-	bool used;
-	bool open_source;
-	bool open_drain;
+	__u32 info_flags;
 
 	int state;
 
@@ -349,19 +347,31 @@ int gpiod_line_active_state(struct gpiod_line *line)
 	return line->active_state;
 }
 
+int gpiod_line_bias(struct gpiod_line *line)
+{
+	if (line->info_flags & GPIOLINE_FLAG_BIAS_DISABLE)
+		return GPIOD_LINE_BIAS_DISABLE;
+	if (line->info_flags & GPIOLINE_FLAG_BIAS_PULL_UP)
+		return GPIOD_LINE_BIAS_PULL_UP;
+	if (line->info_flags & GPIOLINE_FLAG_BIAS_PULL_DOWN)
+		return GPIOD_LINE_BIAS_PULL_DOWN;
+
+	return GPIOD_LINE_BIAS_AS_IS;
+}
+
 bool gpiod_line_is_used(struct gpiod_line *line)
 {
-	return line->used;
+	return line->info_flags & GPIOLINE_FLAG_KERNEL;
 }
 
 bool gpiod_line_is_open_drain(struct gpiod_line *line)
 {
-	return line->open_drain;
+	return line->info_flags & GPIOLINE_FLAG_OPEN_DRAIN;
 }
 
 bool gpiod_line_is_open_source(struct gpiod_line *line)
 {
-	return line->open_source;
+	return line->info_flags & GPIOLINE_FLAG_OPEN_SOURCE;
 }
 
 bool gpiod_line_needs_update(struct gpiod_line *line GPIOD_UNUSED)
@@ -388,9 +398,7 @@ int gpiod_line_update(struct gpiod_line *line)
 						? GPIOD_LINE_ACTIVE_STATE_LOW
 						: GPIOD_LINE_ACTIVE_STATE_HIGH;
 
-	line->used = info.flags & GPIOLINE_FLAG_KERNEL;
-	line->open_drain = info.flags & GPIOLINE_FLAG_OPEN_DRAIN;
-	line->open_source = info.flags & GPIOLINE_FLAG_OPEN_SOURCE;
+	line->info_flags = info.flags;
 
 	strncpy(line->name, info.name, sizeof(line->name));
 	strncpy(line->consumer, info.consumer, sizeof(line->consumer));
@@ -461,6 +469,12 @@ static __u32 line_request_flag_to_gpio_handleflag(int flags)
 		hflags |= GPIOHANDLE_REQUEST_OPEN_SOURCE;
 	if (flags & GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW)
 		hflags |= GPIOHANDLE_REQUEST_ACTIVE_LOW;
+	if (flags & GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE)
+		hflags |= GPIOHANDLE_REQUEST_BIAS_DISABLE;
+	if (flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN)
+		hflags |= GPIOHANDLE_REQUEST_BIAS_PULL_DOWN;
+	if (flags & GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP)
+		hflags |= GPIOHANDLE_REQUEST_BIAS_PULL_UP;
 
 	return hflags;
 }
diff --git a/lib/ctxless.c b/lib/ctxless.c
index ba85018..014475c 100644
--- a/lib/ctxless.c
+++ b/lib/ctxless.c
@@ -14,6 +14,26 @@
 #include <stdio.h>
 #include <string.h>
 
+static int ctxless_flags_to_line_request_flags(bool active_low, int flags)
+{
+	int req_flags = 0;
+
+	if (active_low)
+		req_flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
+	if (flags & GPIOD_CTXLESS_FLAG_OPEN_DRAIN)
+		req_flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN;
+	if (flags & GPIOD_CTXLESS_FLAG_OPEN_SOURCE)
+		req_flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE;
+	if (flags & GPIOD_CTXLESS_FLAG_BIAS_DISABLE)
+		req_flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE;
+	if (flags & GPIOD_CTXLESS_FLAG_BIAS_PULL_UP)
+		req_flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP;
+	if (flags & GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN)
+		req_flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN;
+
+	return req_flags;
+}
+
 int gpiod_ctxless_get_value(const char *device, unsigned int offset,
 			    bool active_low, const char *consumer)
 {
@@ -27,16 +47,44 @@ int gpiod_ctxless_get_value(const char *device, unsigned int offset,
 	return value;
 }
 
+int gpiod_ctxless_get_value_ext(const char *device, unsigned int offset,
+				bool active_low, const char *consumer,
+				int flags)
+{
+	int value, rv;
+
+	rv = gpiod_ctxless_get_value_multiple_ext(device, &offset, &value, 1,
+						  active_low, consumer, flags);
+	if (rv < 0)
+		return rv;
+
+	return value;
+}
+
 int gpiod_ctxless_get_value_multiple(const char *device,
 				     const unsigned int *offsets, int *values,
 				     unsigned int num_lines, bool active_low,
 				     const char *consumer)
+{
+	int rv;
+
+	rv = gpiod_ctxless_get_value_multiple_ext(device, offsets, values,
+						  num_lines, active_low,
+						  consumer, 0);
+	return rv;
+}
+
+int gpiod_ctxless_get_value_multiple_ext(const char *device,
+					 const unsigned int *offsets,
+					 int *values, unsigned int num_lines,
+					 bool active_low,
+					 const char *consumer, int flags)
 {
 	struct gpiod_line_bulk bulk;
 	struct gpiod_chip *chip;
 	struct gpiod_line *line;
 	unsigned int i;
-	int rv, flags;
+	int rv, req_flags;
 
 	if (!num_lines || num_lines > GPIOD_LINE_BULK_MAX_LINES) {
 		errno = EINVAL;
@@ -59,9 +107,8 @@ int gpiod_ctxless_get_value_multiple(const char *device,
 		gpiod_line_bulk_add(&bulk, line);
 	}
 
-	flags = active_low ? GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW : 0;
-
-	rv = gpiod_line_request_bulk_input_flags(&bulk, consumer, flags);
+	req_flags = ctxless_flags_to_line_request_flags(active_low, flags);
+	rv = gpiod_line_request_bulk_input_flags(&bulk, consumer, req_flags);
 	if (rv < 0) {
 		gpiod_chip_close(chip);
 		return -1;
@@ -83,17 +130,39 @@ int gpiod_ctxless_set_value(const char *device, unsigned int offset, int value,
 						active_low, consumer, cb, data);
 }
 
+int gpiod_ctxless_set_value_ext(const char *device, unsigned int offset,
+				int value, bool active_low,
+				const char *consumer,
+				gpiod_ctxless_set_value_cb cb,
+				void *data, int flags)
+{
+	return gpiod_ctxless_set_value_multiple_ext(device, &offset, &value,
+						    1, active_low, consumer,
+						    cb, data, flags);
+}
+
 int gpiod_ctxless_set_value_multiple(const char *device,
 				     const unsigned int *offsets,
 				     const int *values, unsigned int num_lines,
 				     bool active_low, const char *consumer,
 				     gpiod_ctxless_set_value_cb cb, void *data)
+{
+	return gpiod_ctxless_set_value_multiple_ext(device, offsets, values,
+						    num_lines, active_low,
+						    consumer, cb, data, 0);
+}
+
+int gpiod_ctxless_set_value_multiple_ext(
+			const char *device, const unsigned int *offsets,
+			const int *values, unsigned int num_lines,
+			bool active_low, const char *consumer,
+			gpiod_ctxless_set_value_cb cb, void *data, int flags)
 {
 	struct gpiod_line_bulk bulk;
 	struct gpiod_chip *chip;
 	struct gpiod_line *line;
 	unsigned int i;
-	int rv, flags;
+	int rv, req_flags;
 
 	if (!num_lines || num_lines > GPIOD_LINE_BULK_MAX_LINES) {
 		errno = EINVAL;
@@ -116,10 +185,9 @@ int gpiod_ctxless_set_value_multiple(const char *device,
 		gpiod_line_bulk_add(&bulk, line);
 	}
 
-	flags = active_low ? GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW : 0;
-
+	req_flags = ctxless_flags_to_line_request_flags(active_low, flags);
 	rv = gpiod_line_request_bulk_output_flags(&bulk, consumer,
-						  flags, values);
+						  req_flags, values);
 	if (rv < 0) {
 		gpiod_chip_close(chip);
 		return -1;
@@ -216,6 +284,19 @@ int gpiod_ctxless_event_monitor(const char *device, int event_type,
 						    poll_cb, event_cb, data);
 }
 
+int gpiod_ctxless_event_monitor_ext(const char *device, int event_type,
+				    unsigned int offset, bool active_low,
+				    const char *consumer,
+				    const struct timespec *timeout,
+				    gpiod_ctxless_event_poll_cb poll_cb,
+				    gpiod_ctxless_event_handle_cb event_cb,
+				    void *data, int flags)
+{
+	return gpiod_ctxless_event_monitor_multiple_ext(
+			device, event_type, &offset, 1, active_low,
+			consumer, timeout, poll_cb, event_cb, data, flags);
+}
+
 int gpiod_ctxless_event_monitor_multiple(
 			const char *device, int event_type,
 			const unsigned int *offsets,
@@ -225,6 +306,21 @@ int gpiod_ctxless_event_monitor_multiple(
 			gpiod_ctxless_event_poll_cb poll_cb,
 			gpiod_ctxless_event_handle_cb event_cb,
 			void *data)
+{
+	return gpiod_ctxless_event_monitor_multiple_ext(
+			device, event_type, offsets,
+			num_lines, active_low, consumer, timeout,
+			poll_cb, event_cb, data, 0);
+}
+
+int gpiod_ctxless_event_monitor_multiple_ext(
+			const char *device, int event_type,
+			const unsigned int *offsets,
+			unsigned int num_lines, bool active_low,
+			const char *consumer, const struct timespec *timeout,
+			gpiod_ctxless_event_poll_cb poll_cb,
+			gpiod_ctxless_event_handle_cb event_cb,
+			void *data, int flags)
 {
 	struct gpiod_ctxless_event_poll_fd fds[GPIOD_LINE_BULK_MAX_LINES];
 	struct gpiod_line_request_config conf;
@@ -259,7 +355,7 @@ int gpiod_ctxless_event_monitor_multiple(
 		gpiod_line_bulk_add(&bulk, line);
 	}
 
-	conf.flags = active_low ? GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW : 0;
+	conf.flags = ctxless_flags_to_line_request_flags(active_low, flags);
 	conf.consumer = consumer;
 
 	if (event_type == GPIOD_CTXLESS_EVENT_RISING_EDGE) {
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 02/14] tests: add tests for bias flags
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 01/14] core: add support for bias flags Kent Gibson
@ 2019-11-25 14:31 ` " Kent Gibson
  2019-11-28 10:28   ` Bartosz Golaszewski
  2019-11-25 14:31 ` [libgpiod][PATCH v3 03/14] bindings: cxx: add support " Kent Gibson
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Extend test coverage over the bias flags, gpiod_line_bias and the extended
ctxless functions.

Also update existing tests to check bias flags where line state is checked.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 tests/tests-ctxless.c |  64 +++++++++++++++++++++-
 tests/tests-event.c   | 120 ++++++++++++++++++++++++++++++++++++++++++
 tests/tests-line.c    |  98 ++++++++++++++++++++++++++++++++++
 3 files changed, 280 insertions(+), 2 deletions(-)

diff --git a/tests/tests-ctxless.c b/tests/tests-ctxless.c
index c1e1ca6..76b9a7c 100644
--- a/tests/tests-ctxless.c
+++ b/tests/tests-ctxless.c
@@ -26,11 +26,41 @@ GPIOD_TEST_CASE(get_value, 0, { 8 })
 	g_assert_cmpint(ret, ==, 1);
 }
 
-static void set_value_check(gpointer data G_GNUC_UNUSED)
+GPIOD_TEST_CASE(get_value_ext, 0, { 8 })
+{
+	gint ret;
+
+	ret = gpiod_ctxless_get_value_ext(gpiod_test_chip_name(0), 3,
+				false, GPIOD_TEST_CONSUMER,
+				GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN);
+	g_assert_cmpint(ret, ==, 0);
+
+	ret = gpiod_ctxless_get_value_ext(gpiod_test_chip_name(0), 3,
+				false, GPIOD_TEST_CONSUMER,
+				GPIOD_CTXLESS_FLAG_BIAS_PULL_UP);
+	g_assert_cmpint(ret, ==, 1);
+
+	ret = gpiod_ctxless_get_value_ext(gpiod_test_chip_name(0), 3,
+				true, GPIOD_TEST_CONSUMER
+				, GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN);
+	g_assert_cmpint(ret, ==, 1);
+
+	ret = gpiod_ctxless_get_value_ext(gpiod_test_chip_name(0), 3,
+				true, GPIOD_TEST_CONSUMER,
+				GPIOD_CTXLESS_FLAG_BIAS_PULL_UP);
+	g_assert_cmpint(ret, ==, 0);
+}
+
+static void set_value_check_hi(gpointer data G_GNUC_UNUSED)
 {
 	g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 1);
 }
 
+static void set_value_check_lo(gpointer data G_GNUC_UNUSED)
+{
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 0);
+}
+
 GPIOD_TEST_CASE(set_value, 0, { 8 })
 {
 	gint ret;
@@ -39,13 +69,43 @@ GPIOD_TEST_CASE(set_value, 0, { 8 })
 
 	ret = gpiod_ctxless_set_value(gpiod_test_chip_name(0), 3, 1,
 				      false, GPIOD_TEST_CONSUMER,
-				      set_value_check, NULL);
+				      set_value_check_hi, NULL);
 	gpiod_test_return_if_failed();
 	g_assert_cmpint(ret, ==, 0);
 
 	g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 0);
 }
 
+GPIOD_TEST_CASE(set_value_ext, 0, { 8 })
+{
+	gint ret;
+
+	gpiod_test_chip_set_pull(0, 3, 0);
+
+	ret = gpiod_ctxless_set_value_ext(gpiod_test_chip_name(0), 3, 1,
+			false, GPIOD_TEST_CONSUMER,
+			set_value_check_hi, NULL, 0);
+	gpiod_test_return_if_failed();
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 0);
+
+	/* test drive flags by checking that sets are caught by emulation */
+	ret = gpiod_ctxless_set_value_ext(gpiod_test_chip_name(0), 3, 1,
+			false, GPIOD_TEST_CONSUMER, set_value_check_lo,
+			NULL, GPIOD_CTXLESS_FLAG_OPEN_DRAIN);
+	gpiod_test_return_if_failed();
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 0);
+
+	gpiod_test_chip_set_pull(0, 3, 1);
+	ret = gpiod_ctxless_set_value_ext(gpiod_test_chip_name(0), 3, 0,
+			false, GPIOD_TEST_CONSUMER, set_value_check_hi,
+			NULL, GPIOD_CTXLESS_FLAG_OPEN_SOURCE);
+	gpiod_test_return_if_failed();
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 1);
+}
+
 static const guint get_value_multiple_offsets[] = {
 	1, 3, 4, 5, 6, 7, 8, 9, 13, 14
 };
diff --git a/tests/tests-event.c b/tests/tests-event.c
index 28b77ec..d425d1a 100644
--- a/tests/tests-event.c
+++ b/tests/tests-event.c
@@ -196,6 +196,126 @@ GPIOD_TEST_CASE(both_edges_active_low, 0, { 8 })
 	g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE);
 }
 
+GPIOD_TEST_CASE(both_edges_bias_disable, 0, { 8 })
+{
+	g_autoptr(GpiodTestEventThread) ev_thread = NULL;
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct timespec ts = { 1, 0 };
+	struct gpiod_line_event ev;
+	struct gpiod_line *line;
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line = gpiod_chip_get_line(chip, 7);
+	g_assert_nonnull(line);
+	gpiod_test_return_if_failed();
+
+	ret = gpiod_line_request_both_edges_events_flags(line,
+		GPIOD_TEST_CONSUMER, GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE);
+	g_assert_cmpint(ret, ==, 0);
+
+	ev_thread = gpiod_test_start_event_thread(0, 7, 100);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+
+	ret = gpiod_line_event_read(line, &ev);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+
+	ret = gpiod_line_event_read(line, &ev);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_FALLING_EDGE);
+}
+
+GPIOD_TEST_CASE(both_edges_bias_pull_down, 0, { 8 })
+{
+	g_autoptr(GpiodTestEventThread) ev_thread = NULL;
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct timespec ts = { 1, 0 };
+	struct gpiod_line_event ev;
+	struct gpiod_line *line;
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line = gpiod_chip_get_line(chip, 7);
+	g_assert_nonnull(line);
+	gpiod_test_return_if_failed();
+
+	ret = gpiod_line_request_both_edges_events_flags(line,
+		GPIOD_TEST_CONSUMER, GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN);
+	g_assert_cmpint(ret, ==, 0);
+
+	ev_thread = gpiod_test_start_event_thread(0, 7, 100);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+
+	ret = gpiod_line_event_read(line, &ev);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+
+	ret = gpiod_line_event_read(line, &ev);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_FALLING_EDGE);
+}
+
+GPIOD_TEST_CASE(both_edges_bias_pull_up, 0, { 8 })
+{
+	g_autoptr(GpiodTestEventThread) ev_thread = NULL;
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct timespec ts = { 1, 0 };
+	struct gpiod_line_event ev;
+	struct gpiod_line *line;
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line = gpiod_chip_get_line(chip, 7);
+	g_assert_nonnull(line);
+	gpiod_test_return_if_failed();
+
+	ret = gpiod_line_request_both_edges_events_flags(line,
+		GPIOD_TEST_CONSUMER, GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
+	g_assert_cmpint(ret, ==, 0);
+
+	ev_thread = gpiod_test_start_event_thread(0, 7, 100);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+
+	ret = gpiod_line_event_read(line, &ev);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_FALLING_EDGE);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+
+	ret = gpiod_line_event_read(line, &ev);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE);
+}
+
 GPIOD_TEST_CASE(falling_edge_active_low, 0, { 8 })
 {
 	g_autoptr(GpiodTestEventThread) ev_thread = NULL;
diff --git a/tests/tests-line.c b/tests/tests-line.c
index 205c622..4bf7c02 100644
--- a/tests/tests-line.c
+++ b/tests/tests-line.c
@@ -502,6 +502,7 @@ GPIOD_TEST_CASE(misc_flags, 0, { 8 })
 	g_assert_false(gpiod_line_is_used(line));
 	g_assert_false(gpiod_line_is_open_drain(line));
 	g_assert_false(gpiod_line_is_open_source(line));
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
 
 	config.request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT;
 	config.consumer = GPIOD_TEST_CONSUMER;
@@ -513,6 +514,7 @@ GPIOD_TEST_CASE(misc_flags, 0, { 8 })
 	g_assert_true(gpiod_line_is_used(line));
 	g_assert_true(gpiod_line_is_open_drain(line));
 	g_assert_false(gpiod_line_is_open_source(line));
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
 	g_assert_cmpint(gpiod_line_direction(line), ==,
 			GPIOD_LINE_DIRECTION_OUTPUT);
 
@@ -526,8 +528,11 @@ GPIOD_TEST_CASE(misc_flags, 0, { 8 })
 	g_assert_true(gpiod_line_is_used(line));
 	g_assert_false(gpiod_line_is_open_drain(line));
 	g_assert_true(gpiod_line_is_open_source(line));
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
 	g_assert_cmpint(gpiod_line_direction(line), ==,
 			GPIOD_LINE_DIRECTION_OUTPUT);
+
+	gpiod_line_release(line);
 }
 
 GPIOD_TEST_CASE(misc_flags_work_together, 0, { 8 })
@@ -561,6 +566,7 @@ GPIOD_TEST_CASE(misc_flags_work_together, 0, { 8 })
 	g_assert_true(gpiod_line_is_used(line));
 	g_assert_true(gpiod_line_is_open_drain(line));
 	g_assert_false(gpiod_line_is_open_source(line));
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
 	g_assert_cmpint(gpiod_line_active_state(line), ==,
 			GPIOD_LINE_ACTIVE_STATE_LOW);
 	g_assert_cmpint(gpiod_line_direction(line), ==,
@@ -577,8 +583,59 @@ GPIOD_TEST_CASE(misc_flags_work_together, 0, { 8 })
 	g_assert_true(gpiod_line_is_used(line));
 	g_assert_false(gpiod_line_is_open_drain(line));
 	g_assert_true(gpiod_line_is_open_source(line));
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
 	g_assert_cmpint(gpiod_line_active_state(line), ==,
 			GPIOD_LINE_ACTIVE_STATE_LOW);
+
+	gpiod_line_release(line);
+
+	/*
+	 * Verify that pull-up/down flags work together
+	 * with active_low.
+	 */
+
+	config.request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT;
+	config.flags = GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN |
+		       GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
+
+	ret = gpiod_line_request(line, &config, 0);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_true(gpiod_line_is_used(line));
+	g_assert_false(gpiod_line_is_open_drain(line));
+	g_assert_false(gpiod_line_is_open_source(line));
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_PULL_DOWN);
+	g_assert_cmpint(gpiod_line_active_state(line), ==,
+			GPIOD_LINE_ACTIVE_STATE_LOW);
+	g_assert_cmpint(gpiod_line_direction(line), ==,
+			GPIOD_LINE_DIRECTION_INPUT);
+
+	ret = gpiod_line_get_value(line);
+	g_assert_cmpint(ret, ==, 1);
+	g_assert_cmpint(errno, ==, 0);
+
+	gpiod_line_release(line);
+
+	config.flags = GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP |
+		       GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
+
+	ret = gpiod_line_request(line, &config, 0);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_true(gpiod_line_is_used(line));
+	g_assert_false(gpiod_line_is_open_drain(line));
+	g_assert_false(gpiod_line_is_open_source(line));
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_PULL_UP);
+	g_assert_cmpint(gpiod_line_active_state(line), ==,
+			GPIOD_LINE_ACTIVE_STATE_LOW);
+	g_assert_cmpint(gpiod_line_direction(line), ==,
+			GPIOD_LINE_DIRECTION_INPUT);
+
+	ret = gpiod_line_get_value(line);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(errno, ==, 0);
+
+	gpiod_line_release(line);
 }
 
 GPIOD_TEST_CASE(open_source_open_drain_input_mode, 0, { 8 })
@@ -627,6 +684,47 @@ GPIOD_TEST_CASE(open_source_open_drain_simultaneously, 0, { 8 })
 	g_assert_cmpint(errno, ==, EINVAL);
 }
 
+GPIOD_TEST_CASE(multiple_bias_flags, 0, { 8 })
+{
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct gpiod_line *line;
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line = gpiod_chip_get_line(chip, 2);
+	g_assert_nonnull(line);
+	gpiod_test_return_if_failed();
+
+	ret = gpiod_line_request_input_flags(line, GPIOD_TEST_CONSUMER,
+					GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE |
+					GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN);
+	g_assert_cmpint(ret, ==, -1);
+	g_assert_cmpint(errno, ==, EINVAL);
+
+	ret = gpiod_line_request_input_flags(line, GPIOD_TEST_CONSUMER,
+					GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE |
+					GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
+	g_assert_cmpint(ret, ==, -1);
+	g_assert_cmpint(errno, ==, EINVAL);
+
+	ret = gpiod_line_request_input_flags(line, GPIOD_TEST_CONSUMER,
+					GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN |
+					GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
+	g_assert_cmpint(ret, ==, -1);
+	g_assert_cmpint(errno, ==, EINVAL);
+
+	ret = gpiod_line_request_input_flags(line, GPIOD_TEST_CONSUMER,
+					GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE |
+					GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN |
+					GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
+	g_assert_cmpint(ret, ==, -1);
+	g_assert_cmpint(errno, ==, EINVAL);
+}
+
+
 /* Verify that the reference counting of the line fd handle works correctly. */
 GPIOD_TEST_CASE(release_one_use_another, 0, { 8 })
 {
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 03/14] bindings: cxx: add support for bias flags
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 01/14] core: add support for bias flags Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 02/14] tests: add tests " Kent Gibson
@ 2019-11-25 14:31 ` " Kent Gibson
  2019-11-28 10:29   ` Bartosz Golaszewski
  2019-11-25 14:31 ` [libgpiod][PATCH v3 04/14] bindings: cxx: tests: add tests " Kent Gibson
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Add support for bias flags in line requests and returning the line bias
setting via a bias accessor.

Based on initial work by Drew Fustini <drew@pdp7.com>.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 bindings/cxx/gpiod.hpp     | 26 ++++++++++++++++++++++++++
 bindings/cxx/line.cpp      | 19 +++++++++++++++++++
 bindings/cxx/line_bulk.cpp |  6 ++++++
 3 files changed, 51 insertions(+)

diff --git a/bindings/cxx/gpiod.hpp b/bindings/cxx/gpiod.hpp
index b5a9401..2b1a6ab 100644
--- a/bindings/cxx/gpiod.hpp
+++ b/bindings/cxx/gpiod.hpp
@@ -233,6 +233,12 @@ struct line_request
 	/**< The line is an open-source port. */
 	GPIOD_API static const ::std::bitset<32> FLAG_OPEN_DRAIN;
 	/**< The line is an open-drain port. */
+	GPIOD_API static const ::std::bitset<32> FLAG_BIAS_DISABLE;
+	/**< The line has neither pull-up nor pull-down resistor enabled */
+	GPIOD_API static const ::std::bitset<32> FLAG_BIAS_PULL_DOWN;
+	/**< The line has a configurable pull-down resistor enabled */
+	GPIOD_API static const ::std::bitset<32> FLAG_BIAS_PULL_UP;
+	/**< The line has a configurable pull-up resistor enabled */
 
 	::std::string consumer;
 	/**< Consumer name to pass to the request. */
@@ -320,6 +326,12 @@ public:
 	 */
 	GPIOD_API int active_state(void) const;
 
+	/**
+	 * @brief Get current bias of this line.
+	 * @return Current bias setting.
+	 */
+	GPIOD_API int bias(void) const;
+
 	/**
 	 * @brief Check if this line is used by the kernel or other user space
 	 *        process.
@@ -456,6 +468,20 @@ public:
 		/**< Line's active state is high. */
 	};
 
+	/**
+	 * @brief Possible bias settings.
+	 */
+	enum : int {
+		BIAS_AS_IS = 1,
+		/**< Line's bias state is unknown. */
+		BIAS_DISABLE,
+		/**< Line's internal bias is disabled. */
+		BIAS_PULL_UP,
+		/**< Line's internal pull-up bias is enabled. */
+		BIAS_PULL_DOWN,
+		/**< Line's internal pull-down bias is enabled. */
+	};
+
 private:
 
 	line(::gpiod_line* line, const chip& owner);
diff --git a/bindings/cxx/line.cpp b/bindings/cxx/line.cpp
index df6eada..dd6bb6a 100644
--- a/bindings/cxx/line.cpp
+++ b/bindings/cxx/line.cpp
@@ -67,6 +67,25 @@ int line::active_state(void) const
 	return active == GPIOD_LINE_ACTIVE_STATE_HIGH ? ACTIVE_HIGH : ACTIVE_LOW;
 }
 
+int line::bias(void) const
+{
+	this->throw_if_null();
+
+	int bias = ::gpiod_line_bias(this->_m_line);
+
+	switch (bias) {
+	case GPIOD_LINE_BIAS_PULL_UP:
+		return BIAS_PULL_UP;
+	case GPIOD_LINE_BIAS_PULL_DOWN:
+		return BIAS_PULL_DOWN;
+	case GPIOD_LINE_BIAS_DISABLE:
+		return BIAS_DISABLE;
+	case GPIOD_LINE_BIAS_AS_IS:
+	default:
+		return BIAS_AS_IS;
+	}
+}
+
 bool line::is_used(void) const
 {
 	this->throw_if_null();
diff --git a/bindings/cxx/line_bulk.cpp b/bindings/cxx/line_bulk.cpp
index c708c8b..5f1cac4 100644
--- a/bindings/cxx/line_bulk.cpp
+++ b/bindings/cxx/line_bulk.cpp
@@ -14,6 +14,9 @@ namespace gpiod {
 const ::std::bitset<32> line_request::FLAG_ACTIVE_LOW(GPIOD_BIT(0));
 const ::std::bitset<32> line_request::FLAG_OPEN_SOURCE(GPIOD_BIT(1));
 const ::std::bitset<32> line_request::FLAG_OPEN_DRAIN(GPIOD_BIT(2));
+const ::std::bitset<32> line_request::FLAG_BIAS_DISABLE(GPIOD_BIT(3));
+const ::std::bitset<32> line_request::FLAG_BIAS_PULL_DOWN(GPIOD_BIT(4));
+const ::std::bitset<32> line_request::FLAG_BIAS_PULL_UP(GPIOD_BIT(5));
 
 namespace {
 
@@ -38,6 +41,9 @@ const ::std::map<::std::bitset<32>, int, bitset_cmp> reqflag_mapping = {
 	{ line_request::FLAG_ACTIVE_LOW,	GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW, },
 	{ line_request::FLAG_OPEN_DRAIN,	GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN, },
 	{ line_request::FLAG_OPEN_SOURCE,	GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE, },
+	{ line_request::FLAG_BIAS_DISABLE,	GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE, },
+	{ line_request::FLAG_BIAS_PULL_DOWN,	GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN, },
+	{ line_request::FLAG_BIAS_PULL_UP,	GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP, },
 };
 
 } /* namespace */
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 04/14] bindings: cxx: tests: add tests for bias flags
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (2 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 03/14] bindings: cxx: add support " Kent Gibson
@ 2019-11-25 14:31 ` " Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 05/14] bindings: python: add support " Kent Gibson
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Extend test coverage over the bias flags in requests and the bias setting
returned by line.bias().

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 bindings/cxx/tests/tests-line.cpp | 87 +++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/bindings/cxx/tests/tests-line.cpp b/bindings/cxx/tests/tests-line.cpp
index fedaa05..9a0b488 100644
--- a/bindings/cxx/tests/tests-line.cpp
+++ b/bindings/cxx/tests/tests-line.cpp
@@ -52,6 +52,9 @@ TEST_CASE("Line information can be correctly retrieved", "[line]")
 		REQUIRE(line.consumer().empty());
 		REQUIRE_FALSE(line.is_requested());
 		REQUIRE_FALSE(line.is_used());
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+		REQUIRE(line.bias() == ::gpiod::line::BIAS_AS_IS);
 	}
 
 	SECTION("exported line")
@@ -68,6 +71,9 @@ TEST_CASE("Line information can be correctly retrieved", "[line]")
 		REQUIRE(line.active_state() == ::gpiod::line::ACTIVE_HIGH);
 		REQUIRE(line.is_requested());
 		REQUIRE(line.is_used());
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+		REQUIRE(line.bias() == ::gpiod::line::BIAS_AS_IS);
 	}
 
 	SECTION("exported line with flags")
@@ -88,6 +94,87 @@ TEST_CASE("Line information can be correctly retrieved", "[line]")
 		REQUIRE(line.is_used());
 		REQUIRE(line.is_open_drain());
 		REQUIRE_FALSE(line.is_open_source());
+		REQUIRE(line.bias() == ::gpiod::line::BIAS_AS_IS);
+	}
+
+	SECTION("exported open source line")
+	{
+		::gpiod::line_request config;
+
+		config.consumer = consumer.c_str();
+		config.request_type = ::gpiod::line_request::DIRECTION_OUTPUT;
+		config.flags = ::gpiod::line_request::FLAG_OPEN_SOURCE;
+		line.request(config);
+
+		REQUIRE(line.offset() == 4);
+		REQUIRE(line.name() == "gpio-mockup-A-4");
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(line.active_state() == ::gpiod::line::ACTIVE_HIGH);
+		REQUIRE(line.is_requested());
+		REQUIRE(line.is_used());
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE(line.is_open_source());
+		REQUIRE(line.bias() == ::gpiod::line::BIAS_AS_IS);
+	}
+
+	SECTION("exported bias disable line")
+	{
+		::gpiod::line_request config;
+
+		config.consumer = consumer.c_str();
+		config.request_type = ::gpiod::line_request::DIRECTION_OUTPUT;
+		config.flags = ::gpiod::line_request::FLAG_BIAS_DISABLE;
+		line.request(config);
+
+		REQUIRE(line.offset() == 4);
+		REQUIRE(line.name() == "gpio-mockup-A-4");
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(line.active_state() == ::gpiod::line::ACTIVE_HIGH);
+		REQUIRE(line.is_requested());
+		REQUIRE(line.is_used());
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+		REQUIRE(line.bias() == ::gpiod::line::BIAS_DISABLE);
+	}
+
+	SECTION("exported pull-down line")
+	{
+		::gpiod::line_request config;
+
+		config.consumer = consumer.c_str();
+		config.request_type = ::gpiod::line_request::DIRECTION_OUTPUT;
+		config.flags = ::gpiod::line_request::FLAG_BIAS_PULL_DOWN;
+		line.request(config);
+
+		REQUIRE(line.offset() == 4);
+		REQUIRE(line.name() == "gpio-mockup-A-4");
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(line.active_state() == ::gpiod::line::ACTIVE_HIGH);
+		REQUIRE(line.is_requested());
+		REQUIRE(line.is_used());
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+		REQUIRE(line.bias() == ::gpiod::line::BIAS_PULL_DOWN);
+	}
+
+	SECTION("exported pull-up line")
+	{
+		::gpiod::line_request config;
+
+		config.consumer = consumer.c_str();
+		config.request_type = ::gpiod::line_request::DIRECTION_OUTPUT;
+		config.flags = ::gpiod::line_request::FLAG_BIAS_PULL_UP;
+		line.request(config);
+
+		REQUIRE(line.offset() == 4);
+		REQUIRE(line.name() == "gpio-mockup-A-4");
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(line.active_state() == ::gpiod::line::ACTIVE_HIGH);
+		REQUIRE(line.is_requested());
+		REQUIRE(line.is_used());
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+		REQUIRE(line.bias() == ::gpiod::line::BIAS_PULL_UP);
 	}
 
 	SECTION("update line info")
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 05/14] bindings: python: add support for bias flags
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (3 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 04/14] bindings: cxx: tests: add tests " Kent Gibson
@ 2019-11-25 14:31 ` " Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 06/14] bindings: python: tests: add tests " Kent Gibson
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Add support for bias flags in line requests and returning the line bias
setting via a bias accessor.

Based on initial work by Drew Fustini <drew@pdp7.com>.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 bindings/python/gpiodmodule.c | 82 +++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/bindings/python/gpiodmodule.c b/bindings/python/gpiodmodule.c
index 2f6ef51..4723771 100644
--- a/bindings/python/gpiodmodule.c
+++ b/bindings/python/gpiodmodule.c
@@ -60,6 +60,9 @@ enum {
 	gpiod_LINE_REQ_FLAG_OPEN_DRAIN		= GPIOD_BIT(0),
 	gpiod_LINE_REQ_FLAG_OPEN_SOURCE		= GPIOD_BIT(1),
 	gpiod_LINE_REQ_FLAG_ACTIVE_LOW		= GPIOD_BIT(2),
+	gpiod_LINE_REQ_FLAG_BIAS_DISABLE	= GPIOD_BIT(3),
+	gpiod_LINE_REQ_FLAG_BIAS_PULL_DOWN	= GPIOD_BIT(4),
+	gpiod_LINE_REQ_FLAG_BIAS_PULL_UP	= GPIOD_BIT(5),
 };
 
 enum {
@@ -72,6 +75,13 @@ enum {
 	gpiod_ACTIVE_LOW,
 };
 
+enum {
+	gpiod_BIAS_AS_IS = 1,
+	gpiod_BIAS_DISABLE,
+	gpiod_BIAS_PULL_UP,
+	gpiod_BIAS_PULL_DOWN,
+};
+
 enum {
 	gpiod_RISING_EDGE = 1,
 	gpiod_FALLING_EDGE,
@@ -358,6 +368,34 @@ static PyObject *gpiod_Line_active_state(gpiod_LineObject *self,
 	return ret;
 }
 
+PyDoc_STRVAR(gpiod_Line_bias_doc,
+"bias() -> integer\n"
+"\n"
+"Get the bias setting of this GPIO line.");
+
+static PyObject *gpiod_Line_bias(gpiod_LineObject *self,
+				 PyObject *Py_UNUSED(ignored))
+{
+	int bias;
+
+	if (gpiod_ChipIsClosed(self->owner))
+		return NULL;
+
+	bias = gpiod_line_bias(self->line);
+
+	switch (bias) {
+	case GPIOD_LINE_BIAS_PULL_UP:
+		return Py_BuildValue("I", gpiod_BIAS_PULL_UP);
+	case GPIOD_LINE_BIAS_PULL_DOWN:
+		return Py_BuildValue("I", gpiod_BIAS_PULL_DOWN);
+	case GPIOD_LINE_BIAS_DISABLE:
+		return Py_BuildValue("I", gpiod_BIAS_DISABLE);
+	case GPIOD_LINE_BIAS_AS_IS:
+	default:
+		return Py_BuildValue("I", gpiod_BIAS_AS_IS);
+	}
+}
+
 PyDoc_STRVAR(gpiod_Line_is_used_doc,
 "is_used() -> boolean\n"
 "\n"
@@ -752,6 +790,12 @@ static PyMethodDef gpiod_Line_methods[] = {
 		.ml_flags = METH_NOARGS,
 		.ml_doc = gpiod_Line_active_state_doc,
 	},
+	{
+		.ml_name = "bias",
+		.ml_meth = (PyCFunction)gpiod_Line_bias,
+		.ml_flags = METH_NOARGS,
+		.ml_doc = gpiod_Line_bias_doc,
+	},
 	{
 		.ml_name = "is_used",
 		.ml_meth = (PyCFunction)gpiod_Line_is_used,
@@ -1030,6 +1074,12 @@ static void gpiod_MakeRequestConfig(struct gpiod_line_request_config *conf,
 		conf->flags |= GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE;
 	if (flags & gpiod_LINE_REQ_FLAG_ACTIVE_LOW)
 		conf->flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
+	if (flags & gpiod_LINE_REQ_FLAG_BIAS_DISABLE)
+		conf->flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE;
+	if (flags & gpiod_LINE_REQ_FLAG_BIAS_PULL_DOWN)
+		conf->flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN;
+	if (flags & gpiod_LINE_REQ_FLAG_BIAS_PULL_UP)
+		conf->flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP;
 }
 
 PyDoc_STRVAR(gpiod_LineBulk_request_doc,
@@ -2313,6 +2363,26 @@ static gpiod_ConstDescr gpiod_ConstList[] = {
 		.name = "ACTIVE_LOW",
 		.val = gpiod_ACTIVE_LOW,
 	},
+	{
+		.typeobj = &gpiod_LineType,
+		.name = "BIAS_AS_IS",
+		.val = gpiod_BIAS_AS_IS,
+	},
+	{
+		.typeobj = &gpiod_LineType,
+		.name = "BIAS_DISABLE",
+		.val = gpiod_BIAS_DISABLE,
+	},
+	{
+		.typeobj = &gpiod_LineType,
+		.name = "BIAS_PULL_UP",
+		.val = gpiod_BIAS_PULL_UP,
+	},
+	{
+		.typeobj = &gpiod_LineType,
+		.name = "BIAS_PULL_DOWN",
+		.val = gpiod_BIAS_PULL_DOWN,
+	},
 	{
 		.typeobj = &gpiod_LineEventType,
 		.name = "RISING_EDGE",
@@ -2381,6 +2451,18 @@ static gpiod_ModuleConst gpiod_ModuleConsts[] = {
 		.name = "LINE_REQ_FLAG_ACTIVE_LOW",
 		.value = gpiod_LINE_REQ_FLAG_ACTIVE_LOW,
 	},
+	{
+		.name = "LINE_REQ_FLAG_BIAS_DISABLE",
+		.value = gpiod_LINE_REQ_FLAG_BIAS_DISABLE,
+	},
+	{
+		.name = "LINE_REQ_FLAG_BIAS_PULL_DOWN",
+		.value = gpiod_LINE_REQ_FLAG_BIAS_PULL_DOWN,
+	},
+	{
+		.name = "LINE_REQ_FLAG_BIAS_PULL_UP",
+		.value = gpiod_LINE_REQ_FLAG_BIAS_PULL_UP,
+	},
 	{ }
 };
 
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 06/14] bindings: python: tests: add tests for bias flags
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (4 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 05/14] bindings: python: add support " Kent Gibson
@ 2019-11-25 14:31 ` " Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 07/14] core: add support for SET_CONFIG Kent Gibson
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Extend test coverage to cover the bias flags in requests and the bias
setting returned by line.bias().

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 bindings/python/tests/gpiod_py_test.py | 91 ++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/bindings/python/tests/gpiod_py_test.py b/bindings/python/tests/gpiod_py_test.py
index ed31c8e..9330b43 100755
--- a/bindings/python/tests/gpiod_py_test.py
+++ b/bindings/python/tests/gpiod_py_test.py
@@ -306,6 +306,97 @@ class LineInfo(MockupTestCase):
             self.assertTrue(line.is_requested())
             self.assertTrue(line.is_open_drain())
             self.assertFalse(line.is_open_source())
+            self.assertEqual(line.bias(), gpiod.Line.BIAS_AS_IS)
+
+    def test_exported_open_drain_line(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(4)
+            flags = gpiod.LINE_REQ_FLAG_OPEN_DRAIN
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_OUT,
+                         flags=flags)
+            self.assertEqual(line.offset(), 4)
+            self.assertEqual(line.name(), 'gpio-mockup-A-4')
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(line.active_state(), gpiod.Line.ACTIVE_HIGH)
+            self.assertEqual(line.consumer(), default_consumer)
+            self.assertTrue(line.is_used())
+            self.assertTrue(line.is_requested())
+            self.assertTrue(line.is_open_drain())
+            self.assertFalse(line.is_open_source())
+            self.assertEqual(line.bias(), gpiod.Line.BIAS_AS_IS)
+
+    def test_exported_open_source_line(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(4)
+            flags = gpiod.LINE_REQ_FLAG_OPEN_SOURCE
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_OUT,
+                         flags=flags)
+            self.assertEqual(line.offset(), 4)
+            self.assertEqual(line.name(), 'gpio-mockup-A-4')
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(line.active_state(), gpiod.Line.ACTIVE_HIGH)
+            self.assertEqual(line.consumer(), default_consumer)
+            self.assertTrue(line.is_used())
+            self.assertTrue(line.is_requested())
+            self.assertFalse(line.is_open_drain())
+            self.assertTrue(line.is_open_source())
+            self.assertEqual(line.bias(), gpiod.Line.BIAS_AS_IS)
+
+    def test_exported_bias_disable_line(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(4)
+            flags = gpiod.LINE_REQ_FLAG_BIAS_DISABLE
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_OUT,
+                         flags=flags)
+            self.assertEqual(line.offset(), 4)
+            self.assertEqual(line.name(), 'gpio-mockup-A-4')
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(line.active_state(), gpiod.Line.ACTIVE_HIGH)
+            self.assertEqual(line.consumer(), default_consumer)
+            self.assertTrue(line.is_used())
+            self.assertTrue(line.is_requested())
+            self.assertFalse(line.is_open_drain())
+            self.assertFalse(line.is_open_source())
+            self.assertEqual(line.bias(), gpiod.Line.BIAS_DISABLE)
+
+    def test_exported_bias_pull_down_line(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(4)
+            flags = gpiod.LINE_REQ_FLAG_BIAS_PULL_DOWN
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_OUT,
+                         flags=flags)
+            self.assertEqual(line.offset(), 4)
+            self.assertEqual(line.name(), 'gpio-mockup-A-4')
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(line.active_state(), gpiod.Line.ACTIVE_HIGH)
+            self.assertEqual(line.consumer(), default_consumer)
+            self.assertTrue(line.is_used())
+            self.assertTrue(line.is_requested())
+            self.assertFalse(line.is_open_drain())
+            self.assertFalse(line.is_open_source())
+            self.assertEqual(line.bias(), gpiod.Line.BIAS_PULL_DOWN)
+
+    def test_exported_bias_pull_up_line(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(4)
+            flags = gpiod.LINE_REQ_FLAG_BIAS_PULL_UP
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_OUT,
+                         flags=flags)
+            self.assertEqual(line.offset(), 4)
+            self.assertEqual(line.name(), 'gpio-mockup-A-4')
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(line.active_state(), gpiod.Line.ACTIVE_HIGH)
+            self.assertEqual(line.consumer(), default_consumer)
+            self.assertTrue(line.is_used())
+            self.assertTrue(line.is_requested())
+            self.assertFalse(line.is_open_drain())
+            self.assertFalse(line.is_open_source())
+            self.assertEqual(line.bias(), gpiod.Line.BIAS_PULL_UP)
 
     def test_update_line_info(self):
         with gpiod.Chip(mockup.chip_name(0)) as chip:
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 07/14] core: add support for SET_CONFIG
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (5 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 06/14] bindings: python: tests: add tests " Kent Gibson
@ 2019-11-25 14:31 ` Kent Gibson
  2019-11-28 10:29   ` Bartosz Golaszewski
  2019-11-25 14:31 ` [libgpiod][PATCH v3 08/14] tests: add tests " Kent Gibson
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Extend the libgpiod API to support the setting line configuration using the
GPIO GPIOHANDLE_SET_CONFIG_IOCTL uAPI ioctl.

The core change is the addition of gpiod_line_set_config, which provides a
low level wrapper around the ioctl.

Additionally, higher level helper functions, gpiod_line_set_flags,
gpiod_line_set_direction_input, and gpiod_line_set_direction_output provide
slightly simplified APIs for common use cases.

Bulk forms of all functions are also provided.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 include/gpiod.h | 123 +++++++++++++++++++++++++++++++
 lib/core.c      | 187 +++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 307 insertions(+), 3 deletions(-)

diff --git a/include/gpiod.h b/include/gpiod.h
index 86c3ea9..185e2f4 100644
--- a/include/gpiod.h
+++ b/include/gpiod.h
@@ -1246,6 +1246,15 @@ void gpiod_line_release_bulk(struct gpiod_line_bulk *bulk) GPIOD_API;
  */
 bool gpiod_line_is_requested(struct gpiod_line *line) GPIOD_API;
 
+/**
+ * @brief Check if the calling user has ownership of this line for values,
+ * not events.
+ * @param line GPIO line object.
+ * @return True if given line was requested for reading/setting values,
+ *         false otherwise.
+ */
+bool gpiod_line_is_requested_values(struct gpiod_line *line) GPIOD_API;
+
 /**
  * @brief Check if the calling user has neither requested ownership of this
  *        line nor configured any event notifications.
@@ -1296,6 +1305,7 @@ int gpiod_line_set_value(struct gpiod_line *line, int value) GPIOD_API;
  * @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.
  *
@@ -1305,6 +1315,119 @@ int gpiod_line_set_value(struct gpiod_line *line, int value) GPIOD_API;
 int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk,
 			      const int *values) GPIOD_API;
 
+/**
+ * @}
+ *
+ * @defgroup __line_config__ Setting line configuration
+ * @{
+ */
+
+/**
+ * @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) GPIOD_API;
+
+/**
+ * @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.
+ */
+int gpiod_line_set_config_bulk(struct gpiod_line_bulk *bulk,
+			       int direction, int flags,
+			       const int *values) GPIOD_API;
+
+
+/**
+ * @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.
+ */
+int gpiod_line_set_flags(struct gpiod_line *line, int flags) GPIOD_API;
+
+/**
+ * @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.
+ */
+int gpiod_line_set_flags_bulk(struct gpiod_line_bulk *bulk,
+			      int flags) GPIOD_API;
+
+/**
+ * @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.
+ */
+int gpiod_line_set_direction_input(struct gpiod_line *line) GPIOD_API;
+
+/**
+ * @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.
+ */
+int gpiod_line_set_direction_input_bulk(struct gpiod_line_bulk *bulk
+					) GPIOD_API;
+
+/**
+ * @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.
+ */
+int gpiod_line_set_direction_output(struct gpiod_line *line,
+				    int value) GPIOD_API;
+
+/**
+ * @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.
+ */
+int gpiod_line_set_direction_output_bulk(struct gpiod_line_bulk *bulk,
+					 const int *values) GPIOD_API;
+
 /**
  * @}
  *
diff --git a/lib/core.c b/lib/core.c
index 0465de9..71bb4fb 100644
--- a/lib/core.c
+++ b/lib/core.c
@@ -34,10 +34,26 @@ struct line_fd_handle {
 
 struct gpiod_line {
 	unsigned int offset;
+
+	/* The GPIOD_LINE_DIRECTION */
 	int direction;
+
+	/* The GPIOD_LINE_ACTIVE_STATE */
 	int active_state;
+
+	/* 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;
@@ -445,6 +461,20 @@ static bool line_bulk_all_requested(struct gpiod_line_bulk *bulk)
 	return true;
 }
 
+static bool line_bulk_all_requested_values(struct gpiod_line_bulk *bulk)
+{
+	struct gpiod_line *line, **lineptr;
+
+	gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
+		if (!gpiod_line_is_requested_values(line)) {
+			errno = EPERM;
+			return false;
+		}
+	}
+
+	return true;
+}
+
 static bool line_bulk_all_free(struct gpiod_line_bulk *bulk)
 {
 	struct gpiod_line *line, **lineptr;
@@ -459,6 +489,27 @@ static bool line_bulk_all_free(struct gpiod_line_bulk *bulk)
 	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 __u32 line_request_direction_to_gpio_handleflag(int direction)
+{
+	if (direction == GPIOD_LINE_REQUEST_DIRECTION_INPUT)
+		return GPIOHANDLE_REQUEST_INPUT;
+	if (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
+		return GPIOHANDLE_REQUEST_OUTPUT;
+
+	return 0;
+}
+
 static __u32 line_request_flag_to_gpio_handleflag(int flags)
 {
 	int hflags = 0;
@@ -483,7 +534,7 @@ static int line_request_values(struct gpiod_line_bulk *bulk,
 			       const struct gpiod_line_request_config *config,
 			       const int *default_vals)
 {
-	struct gpiod_line *line, **lineptr;
+	struct gpiod_line *line;
 	struct line_fd_handle *line_fd;
 	struct gpiohandle_request req;
 	unsigned int i;
@@ -512,7 +563,6 @@ static int line_request_values(struct gpiod_line_bulk *bulk,
 	else if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
 		req.flags |= GPIOHANDLE_REQUEST_OUTPUT;
 
-
 	gpiod_line_bulk_foreach_line_off(bulk, line, i) {
 		req.lineoffsets[i] = gpiod_line_offset(line);
 		if (config->request_type ==
@@ -536,8 +586,12 @@ static int line_request_values(struct gpiod_line_bulk *bulk,
 	if (!line_fd)
 		return -1;
 
-	gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
+	gpiod_line_bulk_foreach_line_off(bulk, line, i) {
 		line->state = LINE_REQUESTED_VALUES;
+		line->req_flags = config->flags;
+		if (config->request_type ==
+			GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
+			line->output_value = req.default_values[i];
 		line_set_fd(line, line_fd);
 
 		rv = gpiod_line_update(line);
@@ -583,6 +637,7 @@ static int line_request_event_single(struct gpiod_line *line,
 		return -1;
 
 	line->state = LINE_REQUESTED_EVENTS;
+	line->req_flags = config->flags;
 	line_set_fd(line, line_fd);
 
 	rv = gpiod_line_update(line);
@@ -686,6 +741,11 @@ bool gpiod_line_is_requested(struct gpiod_line *line)
 		line->state == LINE_REQUESTED_EVENTS);
 }
 
+bool gpiod_line_is_requested_values(struct gpiod_line *line)
+{
+	return (line->state == LINE_REQUESTED_VALUES);
+}
+
 bool gpiod_line_is_free(struct gpiod_line *line)
 {
 	return line->state == LINE_FREE;
@@ -766,9 +826,130 @@ int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, const int *values)
 	if (rv < 0)
 		return -1;
 
+	gpiod_line_bulk_foreach_line_off(bulk, line, i)
+		line->output_value = data.values[i];
+
 	return 0;
 }
 
+int gpiod_line_set_config(struct gpiod_line *line, int direction,
+			  int flags, int value)
+{
+	struct gpiod_line_bulk bulk;
+
+	gpiod_line_bulk_init(&bulk);
+	gpiod_line_bulk_add(&bulk, line);
+
+	return gpiod_line_set_config_bulk(&bulk, direction, flags, &value);
+}
+
+int gpiod_line_set_config_bulk(struct gpiod_line_bulk *bulk,
+			       int direction, int flags,
+			       const int *values)
+{
+	struct gpiohandle_config hcfg;
+	struct gpiod_line *line;
+	unsigned int i;
+	int rv, fd;
+
+	if (!line_bulk_same_chip(bulk) ||
+	    !line_bulk_all_requested_values(bulk))
+		return -1;
+
+	if (!line_request_direction_is_valid(direction))
+		return -1;
+
+	memset(&hcfg, 0, sizeof(hcfg));
+
+	hcfg.flags = line_request_flag_to_gpio_handleflag(flags);
+	hcfg.flags |= line_request_direction_to_gpio_handleflag(direction);
+	if (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT && values) {
+		for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++)
+			hcfg.default_values[i] = (uint8_t)!!values[i];
+	}
+
+	line = gpiod_line_bulk_get_line(bulk, 0);
+	fd = line_get_fd(line);
+
+	rv = ioctl(fd, GPIOHANDLE_SET_CONFIG_IOCTL, &hcfg);
+	if (rv < 0)
+		return -1;
+
+	gpiod_line_bulk_foreach_line_off(bulk, line, i) {
+		line->req_flags = flags;
+		if (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
+			line->output_value = hcfg.default_values[i];
+		rv = gpiod_line_update(line);
+		if (rv < 0)
+			return rv;
+	}
+	return 0;
+}
+
+int gpiod_line_set_flags(struct gpiod_line *line, int flags)
+{
+	struct gpiod_line_bulk bulk;
+
+	gpiod_line_bulk_init(&bulk);
+	gpiod_line_bulk_add(&bulk, line);
+
+	return gpiod_line_set_flags_bulk(&bulk, flags);
+}
+
+int gpiod_line_set_flags_bulk(struct gpiod_line_bulk *bulk, int flags)
+{
+	struct gpiod_line *line;
+	int values[GPIOD_LINE_BULK_MAX_LINES];
+	unsigned int i;
+	int direction;
+
+	line = gpiod_line_bulk_get_line(bulk, 0);
+	if (line->direction == GPIOD_LINE_DIRECTION_OUTPUT) {
+		gpiod_line_bulk_foreach_line_off(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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
 int gpiod_line_event_wait(struct gpiod_line *line,
 			  const struct timespec *timeout)
 {
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 08/14] tests: add tests for SET_CONFIG
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (6 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 07/14] core: add support for SET_CONFIG Kent Gibson
@ 2019-11-25 14:31 ` " Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 09/14] bindings: cxx: add support " Kent Gibson
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Extend test coverage over the SET_CONFIG functions.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 tests/tests-line.c | 370 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 369 insertions(+), 1 deletion(-)

diff --git a/tests/tests-line.c b/tests/tests-line.c
index 4bf7c02..97b7df9 100644
--- a/tests/tests-line.c
+++ b/tests/tests-line.c
@@ -265,6 +265,7 @@ GPIOD_TEST_CASE(set_value, 0, { 8 })
 
 	ret = gpiod_line_request_output(line, GPIOD_TEST_CONSUMER, 0);
 	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
 
 	ret = gpiod_line_set_value(line, 1);
 	g_assert_cmpint(ret, ==, 0);
@@ -327,6 +328,351 @@ GPIOD_TEST_CASE(set_value_bulk, 0, { 8 })
 	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
 }
 
+GPIOD_TEST_CASE(set_config_bulk_null_values, 0, { 8 })
+{
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct gpiod_line_bulk bulk = GPIOD_LINE_BULK_INITIALIZER;
+	struct gpiod_line *line0, *line1, *line2;
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line0 = gpiod_chip_get_line(chip, 0);
+	line1 = gpiod_chip_get_line(chip, 1);
+	line2 = gpiod_chip_get_line(chip, 2);
+
+	g_assert_nonnull(line0);
+	g_assert_nonnull(line1);
+	g_assert_nonnull(line2);
+	gpiod_test_return_if_failed();
+
+	gpiod_line_bulk_add(&bulk, line0);
+	gpiod_line_bulk_add(&bulk, line1);
+	gpiod_line_bulk_add(&bulk, line2);
+
+	ret = gpiod_line_request_bulk_output(&bulk, GPIOD_TEST_CONSUMER, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_active_state(line0), ==,
+			GPIOD_LINE_ACTIVE_STATE_HIGH);
+	g_assert_cmpint(gpiod_line_active_state(line1), ==,
+			GPIOD_LINE_ACTIVE_STATE_HIGH);
+	g_assert_cmpint(gpiod_line_active_state(line2), ==,
+			GPIOD_LINE_ACTIVE_STATE_HIGH);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 0), ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 1), ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+
+	ret = gpiod_line_set_config_bulk(&bulk,
+			GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
+			GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW, NULL);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_active_state(line0), ==,
+			GPIOD_LINE_ACTIVE_STATE_LOW);
+	g_assert_cmpint(gpiod_line_active_state(line1), ==,
+			GPIOD_LINE_ACTIVE_STATE_LOW);
+	g_assert_cmpint(gpiod_line_active_state(line2), ==,
+			GPIOD_LINE_ACTIVE_STATE_LOW);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 0), ==, 1);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 1), ==, 1);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+
+	ret = gpiod_line_set_config_bulk(&bulk,
+			GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, 0, NULL);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_active_state(line0), ==,
+			GPIOD_LINE_ACTIVE_STATE_HIGH);
+	g_assert_cmpint(gpiod_line_active_state(line1), ==,
+			GPIOD_LINE_ACTIVE_STATE_HIGH);
+	g_assert_cmpint(gpiod_line_active_state(line2), ==,
+			GPIOD_LINE_ACTIVE_STATE_HIGH);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 0), ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 1), ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+}
+
+GPIOD_TEST_CASE(set_flags_active_state, 0, { 8 })
+{
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct gpiod_line *line;
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line = gpiod_chip_get_line(chip, 2);
+	g_assert_nonnull(line);
+	gpiod_test_return_if_failed();
+
+	ret = gpiod_line_request_output(line, GPIOD_TEST_CONSUMER, 1);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_active_state(line), ==,
+			GPIOD_LINE_ACTIVE_STATE_HIGH);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+
+	ret = gpiod_line_set_flags(line, GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_active_state(line), ==,
+			GPIOD_LINE_ACTIVE_STATE_LOW);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+
+	ret = gpiod_line_set_flags(line, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_active_state(line), ==,
+			GPIOD_LINE_ACTIVE_STATE_HIGH);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+}
+
+GPIOD_TEST_CASE(set_flags_bias, 0, { 8 })
+{
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct gpiod_line *line;
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line = gpiod_chip_get_line(chip, 2);
+	g_assert_nonnull(line);
+	gpiod_test_return_if_failed();
+
+	ret = gpiod_line_request_input(line, GPIOD_TEST_CONSUMER);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
+
+	ret = gpiod_line_set_flags(line,
+		GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_DISABLE);
+
+	ret = gpiod_line_set_flags(line,
+		GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_PULL_UP);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+
+	ret = gpiod_line_set_flags(line,
+		GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_PULL_DOWN);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+}
+
+GPIOD_TEST_CASE(set_flags_drive, 0, { 8 })
+{
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct gpiod_line *line;
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line = gpiod_chip_get_line(chip, 2);
+	g_assert_nonnull(line);
+	gpiod_test_return_if_failed();
+
+	ret = gpiod_line_request_output(line, GPIOD_TEST_CONSUMER, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_is_open_drain(line), ==, false);
+	g_assert_cmpint(gpiod_line_is_open_source(line), ==, false);
+
+	ret = gpiod_line_set_flags(line,
+		GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_is_open_drain(line), ==, true);
+	g_assert_cmpint(gpiod_line_is_open_source(line), ==, false);
+
+	ret = gpiod_line_set_flags(line,
+		GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_is_open_drain(line), ==, false);
+	g_assert_cmpint(gpiod_line_is_open_source(line), ==, true);
+}
+
+GPIOD_TEST_CASE(set_direction, 0, { 8 })
+{
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct gpiod_line *line;
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line = gpiod_chip_get_line(chip, 2);
+	g_assert_nonnull(line);
+	gpiod_test_return_if_failed();
+
+	ret = gpiod_line_request_output(line, GPIOD_TEST_CONSUMER, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_direction(line), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+
+	ret = gpiod_line_set_direction_input(line);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_direction(line), ==,
+			GPIOD_LINE_DIRECTION_INPUT);
+
+	ret = gpiod_line_set_direction_output(line, 1);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_direction(line), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+}
+
+GPIOD_TEST_CASE(set_direction_bulk, 0, { 8 })
+{
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct gpiod_line_bulk bulk = GPIOD_LINE_BULK_INITIALIZER;
+	struct gpiod_line *line0, *line1, *line2;
+	int values[3];
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line0 = gpiod_chip_get_line(chip, 0);
+	line1 = gpiod_chip_get_line(chip, 1);
+	line2 = gpiod_chip_get_line(chip, 2);
+
+	g_assert_nonnull(line0);
+	g_assert_nonnull(line1);
+	g_assert_nonnull(line2);
+	gpiod_test_return_if_failed();
+
+	gpiod_line_bulk_add(&bulk, line0);
+	gpiod_line_bulk_add(&bulk, line1);
+	gpiod_line_bulk_add(&bulk, line2);
+
+	values[0] = 0;
+	values[1] = 1;
+	values[2] = 2;
+
+	ret = gpiod_line_request_bulk_output(&bulk,
+			GPIOD_TEST_CONSUMER, values);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_direction(line0), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_line_direction(line1), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_line_direction(line2), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 0), ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 1), ==, 1);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+
+	ret = gpiod_line_set_direction_input_bulk(&bulk);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_direction(line0), ==,
+			GPIOD_LINE_DIRECTION_INPUT);
+	g_assert_cmpint(gpiod_line_direction(line1), ==,
+			GPIOD_LINE_DIRECTION_INPUT);
+	g_assert_cmpint(gpiod_line_direction(line2), ==,
+			GPIOD_LINE_DIRECTION_INPUT);
+
+	values[0] = 2;
+	values[1] = 1;
+	values[2] = 0;
+
+	ret = gpiod_line_set_direction_output_bulk(&bulk, values);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_direction(line0), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_line_direction(line1), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_line_direction(line2), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 0), ==, 1);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 1), ==, 1);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+
+	ret = gpiod_line_set_direction_output_bulk(&bulk, NULL);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_line_direction(line0), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_line_direction(line1), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_line_direction(line2), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 0), ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 1), ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+}
+
+GPIOD_TEST_CASE(output_value_caching, 0, { 8 })
+{
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct gpiod_line *line;
+	struct gpiod_line_bulk bulk;
+	gint ret;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line = gpiod_chip_get_line(chip, 2);
+	g_assert_nonnull(line);
+	gpiod_test_return_if_failed();
+
+	/* check cached by request... */
+	ret = gpiod_line_request_output(line, GPIOD_TEST_CONSUMER, 1);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+
+	/* ...by checking cached value applied by set_flags */
+	ret = gpiod_line_set_flags(line, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+
+	/* check cached by set_value */
+	ret = gpiod_line_set_value(line, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+
+	ret = gpiod_line_set_flags(line, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+
+	ret = gpiod_line_set_value(line, 1);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+
+	ret = gpiod_line_set_flags(line, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+
+	/* check cached by set_config */
+	ret = gpiod_line_set_config(line, GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,
+				    0, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+
+	ret = gpiod_line_set_flags(line, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+
+	/* check cached by set_value_bulk default */
+	ret = gpiod_line_set_value(line, 1);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 1);
+
+	gpiod_line_bulk_init(&bulk);
+	gpiod_line_bulk_add(&bulk, line);
+	ret = gpiod_line_set_value_bulk(&bulk, NULL);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+
+	ret = gpiod_line_set_flags(line, 0);
+	g_assert_cmpint(ret, ==, 0);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0);
+}
+
 GPIOD_TEST_CASE(get_value_different_chips, 0, { 8, 8 })
 {
 	g_autoptr(gpiod_chip_struct) chipA = NULL;
@@ -441,10 +787,11 @@ GPIOD_TEST_CASE(direction, 0, { 8 })
 	g_assert_nonnull(line);
 	gpiod_test_return_if_failed();
 
-	ret = gpiod_line_request_output(line, GPIOD_TEST_CONSUMER, 0);
+	ret = gpiod_line_request_output(line, GPIOD_TEST_CONSUMER, 1);
 	g_assert_cmpint(ret, ==, 0);
 	g_assert_cmpint(gpiod_line_direction(line), ==,
 			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 5), ==, 1);
 
 	gpiod_line_release(line);
 
@@ -482,6 +829,27 @@ GPIOD_TEST_CASE(active_state, 0, { 8 })
 
 	g_assert_cmpint(gpiod_line_direction(line), ==,
 			GPIOD_LINE_DIRECTION_INPUT);
+
+	gpiod_line_release(line);
+
+	ret = gpiod_line_request_output_flags(line, GPIOD_TEST_CONSUMER,
+			GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW, 0);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(gpiod_line_direction(line), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 5), ==, 1);
+
+	gpiod_line_release(line);
+
+	ret = gpiod_line_request_output_flags(line,
+			GPIOD_TEST_CONSUMER, 0, 0);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(gpiod_line_direction(line), ==,
+			GPIOD_LINE_DIRECTION_OUTPUT);
+	g_assert_cmpint(gpiod_test_chip_get_value(0, 5), ==, 0);
+
 }
 
 GPIOD_TEST_CASE(misc_flags, 0, { 8 })
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 09/14] bindings: cxx: add support for SET_CONFIG
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (7 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 08/14] tests: add tests " Kent Gibson
@ 2019-11-25 14:31 ` " Kent Gibson
  2019-11-28 10:29   ` Bartosz Golaszewski
  2019-11-25 14:31 ` [libgpiod][PATCH v3 10/14] bindings: cxx: tests: add tests for SET_CONFIG methods Kent Gibson
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Add methods to support setting line configuration.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 bindings/cxx/gpiod.hpp     | 55 +++++++++++++++++++++++++
 bindings/cxx/line.cpp      | 37 +++++++++++++++++
 bindings/cxx/line_bulk.cpp | 83 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 175 insertions(+)

diff --git a/bindings/cxx/gpiod.hpp b/bindings/cxx/gpiod.hpp
index 2b1a6ab..dcae431 100644
--- a/bindings/cxx/gpiod.hpp
+++ b/bindings/cxx/gpiod.hpp
@@ -381,6 +381,32 @@ public:
 	 */
 	GPIOD_API void set_value(int val) const;
 
+	/**
+	 * @brief Set configuration of this line.
+	 * @param direction New direction.
+	 * @param flags Replacement flags.
+	 * @param value New value (0 or 1) - only matters for OUTPUT direction.
+	 */
+	GPIOD_API void set_config(int direction, ::std::bitset<32> flags,
+			int value = 0) const;
+
+	/**
+	 * @brief Set configuration flags of this line.
+	 * @param flags Replacement flags.
+	 */
+	GPIOD_API void set_flags(::std::bitset<32> flags) const;
+
+	/**
+	 * @brief Change the direction this line to input.
+	 */
+	GPIOD_API void set_direction_input() const;
+
+	/**
+	 * @brief Change the direction this lines to output.
+	 * @param value New value (0 or 1).
+	 */
+	GPIOD_API void set_direction_output(int value = 0) const;
+
 	/**
 	 * @brief Wait for an event on this line.
 	 * @param timeout Time to wait before returning if no event occurred.
@@ -648,6 +674,35 @@ public:
 	 */
 	GPIOD_API void set_values(const ::std::vector<int>& values) const;
 
+	/**
+	 * @brief Set configuration of all lines held by this object.
+	 * @param direction New direction.
+	 * @param flags Replacement flags.
+	 * @param values Vector of values to set. Must be the same size as the
+	 *        number of lines held by this line_bulk.
+	 * 	  Only relevant for output direction requests.
+	 */
+	GPIOD_API void set_config(int direction, ::std::bitset<32> flags,
+			const ::std::vector<int> values = std::vector<int>()) const;
+
+	/**
+	 * @brief Set configuration flags of all lines held by this object.
+	 * @param flags Replacement flags.
+	 */
+	GPIOD_API void set_flags(::std::bitset<32> flags) const;
+
+	/**
+	 * @brief Change the direction all lines held by this object to input.
+	 */
+	GPIOD_API void set_direction_input() const;
+
+	/**
+	 * @brief Change the direction all lines held by this object to output.
+	 * @param values Vector of values to set. Must be the same size as the
+	 *        number of lines held by this line_bulk.
+	 */
+	GPIOD_API void set_direction_output(const ::std::vector<int>& values) const;
+
 	/**
 	 * @brief Poll the set of lines for line events.
 	 * @param timeout Number of nanoseconds to wait before returning an
diff --git a/bindings/cxx/line.cpp b/bindings/cxx/line.cpp
index dd6bb6a..a688b5d 100644
--- a/bindings/cxx/line.cpp
+++ b/bindings/cxx/line.cpp
@@ -158,6 +158,43 @@ void line::set_value(int val) const
 	bulk.set_values({ val });
 }
 
+void line::set_config(int direction, ::std::bitset<32> flags,
+			int value) const
+{
+	this->throw_if_null();
+
+	line_bulk bulk({ *this });
+
+	bulk.set_config(direction, flags, { value });
+}
+
+void line::set_flags(::std::bitset<32> flags) const
+{
+	this->throw_if_null();
+
+	line_bulk bulk({ *this });
+
+	bulk.set_flags(flags);
+}
+
+void line::set_direction_input() const
+{
+	this->throw_if_null();
+
+	line_bulk bulk({ *this });
+
+	bulk.set_direction_input();
+}
+
+void line::set_direction_output(int value) const
+{
+	this->throw_if_null();
+
+	line_bulk bulk({ *this });
+
+	bulk.set_direction_output({ value });
+}
+
 bool line::event_wait(const ::std::chrono::nanoseconds& timeout) const
 {
 	this->throw_if_null();
diff --git a/bindings/cxx/line_bulk.cpp b/bindings/cxx/line_bulk.cpp
index 5f1cac4..b8f5eb7 100644
--- a/bindings/cxx/line_bulk.cpp
+++ b/bindings/cxx/line_bulk.cpp
@@ -176,6 +176,89 @@ void line_bulk::set_values(const ::std::vector<int>& values) const
 					  "error setting GPIO line values");
 }
 
+void line_bulk::set_config(int direction, ::std::bitset<32> flags,
+			   const ::std::vector<int> values) const
+{
+	this->throw_if_empty();
+
+	if (!values.empty() && this->_m_bulk.size() != values.size())
+		throw ::std::invalid_argument("the number of default values must correspond with the number of lines");
+
+	::gpiod_line_bulk bulk;
+	int rv, gflags;
+
+	gflags = 0;
+
+	for (auto& it: reqflag_mapping) {
+		if ((it.first & flags).to_ulong())
+			gflags |= it.second;
+	}
+
+	this->to_line_bulk(::std::addressof(bulk));
+
+	rv = ::gpiod_line_set_config_bulk(::std::addressof(bulk), direction,
+					  gflags, values.data());
+	if (rv)
+		throw ::std::system_error(errno, ::std::system_category(),
+					  "error setting GPIO line config");
+}
+
+void line_bulk::set_flags(::std::bitset<32> flags) const
+{
+	this->throw_if_empty();
+
+	::gpiod_line_bulk bulk;
+	int rv, gflags;
+
+	this->to_line_bulk(::std::addressof(bulk));
+
+	gflags = 0;
+
+	for (auto& it: reqflag_mapping) {
+		if ((it.first & flags).to_ulong())
+			gflags |= it.second;
+	}
+
+	rv = ::gpiod_line_set_flags_bulk(::std::addressof(bulk), gflags);
+	if (rv)
+		throw ::std::system_error(errno, ::std::system_category(),
+					  "error setting GPIO line flags");
+}
+
+void line_bulk::set_direction_input() const
+{
+	this->throw_if_empty();
+
+	::gpiod_line_bulk bulk;
+	int rv;
+
+	this->to_line_bulk(::std::addressof(bulk));
+
+	rv = ::gpiod_line_set_direction_input_bulk(::std::addressof(bulk));
+	if (rv)
+		throw ::std::system_error(errno, ::std::system_category(),
+			"error setting GPIO line direction to input");
+}
+
+void line_bulk::set_direction_output(const ::std::vector<int>& values) const
+{
+	this->throw_if_empty();
+
+	if (values.size() != this->_m_bulk.size())
+		throw ::std::invalid_argument("the size of values array must correspond with the number of lines");
+
+	::gpiod_line_bulk bulk;
+	int rv;
+
+	this->to_line_bulk(::std::addressof(bulk));
+
+	rv = ::gpiod_line_set_direction_output_bulk(::std::addressof(bulk),
+						    values.data());
+	if (rv)
+		throw ::std::system_error(errno, ::std::system_category(),
+			"error setting GPIO line direction to output");
+}
+
 line_bulk line_bulk::event_wait(const ::std::chrono::nanoseconds& timeout) const
 {
 	this->throw_if_empty();
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 10/14] bindings: cxx: tests: add tests for SET_CONFIG methods
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (8 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 09/14] bindings: cxx: add support " Kent Gibson
@ 2019-11-25 14:31 ` Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 11/14] bindings: python: add support for SET_CONFIG Kent Gibson
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Extend test coverage over set_config, set_flags, set_direction_input, and
set_direction_output methods.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 bindings/cxx/tests/tests-line.cpp | 128 ++++++++++++++++++++++++++++++
 1 file changed, 128 insertions(+)

diff --git a/bindings/cxx/tests/tests-line.cpp b/bindings/cxx/tests/tests-line.cpp
index 9a0b488..5353093 100644
--- a/bindings/cxx/tests/tests-line.cpp
+++ b/bindings/cxx/tests/tests-line.cpp
@@ -324,6 +324,134 @@ TEST_CASE("Line values can be set and read", "[line]")
 	}
 }
 
+TEST_CASE("Line can be reconfigured", "[line]")
+{
+	mockup::probe_guard mockup_chips({ 8 });
+	::gpiod::chip chip(mockup::instance().chip_name(0));
+	::gpiod::line_request config;
+
+	config.consumer = consumer.c_str();
+
+	SECTION("set config (single line, active-state)")
+	{
+		auto line = chip.get_line(3);
+		config.request_type = ::gpiod::line_request::DIRECTION_INPUT;
+		config.flags = 0;
+		line.request(config);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_INPUT);
+		REQUIRE(line.active_state() == ::gpiod::line::ACTIVE_HIGH);
+
+		line.set_config(::gpiod::line_request::DIRECTION_OUTPUT,
+			::gpiod::line_request::FLAG_ACTIVE_LOW,1);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(line.active_state() == ::gpiod::line::ACTIVE_LOW);
+		REQUIRE(mockup::instance().chip_get_value(0, 3) == 0);
+		line.set_value(0);
+		REQUIRE(mockup::instance().chip_get_value(0, 3) == 1);
+
+		line.set_config(::gpiod::line_request::DIRECTION_OUTPUT, 0);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(line.active_state() == ::gpiod::line::ACTIVE_HIGH);
+		REQUIRE(mockup::instance().chip_get_value(0, 3) == 0);
+		line.set_value(1);
+		REQUIRE(mockup::instance().chip_get_value(0, 3) == 1);
+	}
+
+	SECTION("set flags (single line, active-state)")
+	{
+		auto line = chip.get_line(3);
+		config.request_type = ::gpiod::line_request::DIRECTION_OUTPUT;
+		config.flags = 0;
+		line.request(config,1);
+		REQUIRE(mockup::instance().chip_get_value(0, 3) == 1);
+
+		line.set_flags(::gpiod::line_request::FLAG_ACTIVE_LOW);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(line.active_state() == ::gpiod::line::ACTIVE_LOW);
+		REQUIRE(mockup::instance().chip_get_value(0, 3) == 0);
+
+		line.set_flags(0);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(line.active_state() == ::gpiod::line::ACTIVE_HIGH);
+		REQUIRE(mockup::instance().chip_get_value(0, 3) == 1);
+	}
+
+	SECTION("set flags (single line, drive)")
+	{
+		auto line = chip.get_line(3);
+		config.request_type = ::gpiod::line_request::DIRECTION_OUTPUT;
+		config.flags = 0;
+		line.request(config);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+
+		line.set_flags(::gpiod::line_request::FLAG_OPEN_DRAIN);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+
+		line.set_flags(::gpiod::line_request::FLAG_OPEN_SOURCE);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE(line.is_open_source());
+
+		line.set_flags(0);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+	}
+
+	SECTION("set flags (single line, bias)")
+	{
+		auto line = chip.get_line(3);
+		config.request_type = ::gpiod::line_request::DIRECTION_OUTPUT;
+		config.flags = 0;
+		line.request(config);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+
+		line.set_flags(::gpiod::line_request::FLAG_OPEN_DRAIN);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+
+		line.set_flags(::gpiod::line_request::FLAG_OPEN_SOURCE);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE(line.is_open_source());
+
+		line.set_flags(0);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE_FALSE(line.is_open_drain());
+		REQUIRE_FALSE(line.is_open_source());
+	}
+
+	SECTION("set direction input (single line)")
+	{
+		auto line = chip.get_line(3);
+		config.request_type = ::gpiod::line_request::DIRECTION_OUTPUT;
+		config.flags = 0;
+		line.request(config);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		line.set_direction_input();
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_INPUT);
+	}
+
+	SECTION("set direction output (single line)")
+	{
+		auto line = chip.get_line(3);
+		config.request_type = ::gpiod::line_request::DIRECTION_INPUT;
+		config.flags = 0;
+		line.request(config);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_INPUT);
+		line.set_direction_output(1);
+		REQUIRE(line.direction() == ::gpiod::line::DIRECTION_OUTPUT);
+		REQUIRE(mockup::instance().chip_get_value(0, 3) == 1);
+	}
+}
+
 TEST_CASE("Exported line can be released", "[line]")
 {
 	mockup::probe_guard mockup_chips({ 8 });
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 11/14] bindings: python: add support for SET_CONFIG
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (9 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 10/14] bindings: cxx: tests: add tests for SET_CONFIG methods Kent Gibson
@ 2019-11-25 14:31 ` Kent Gibson
  2019-11-28 10:29   ` Bartosz Golaszewski
  2019-11-25 14:31 ` [libgpiod][PATCH v3 12/14] bindings: python: tests: add tests for SET_CONFIG methods Kent Gibson
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Add methods to support setting line configuration.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 bindings/python/gpiodmodule.c | 381 +++++++++++++++++++++++++++++++++-
 1 file changed, 379 insertions(+), 2 deletions(-)

diff --git a/bindings/python/gpiodmodule.c b/bindings/python/gpiodmodule.c
index 4723771..4f5e117 100644
--- a/bindings/python/gpiodmodule.c
+++ b/bindings/python/gpiodmodule.c
@@ -585,14 +585,149 @@ static PyObject *gpiod_Line_set_value(gpiod_LineObject *self, PyObject *args)
 	if (!bulk_obj)
 		return NULL;
 
-	vals = Py_BuildValue("((O))", val);
+	vals = Py_BuildValue("(O)", val);
 	if (!vals) {
 		Py_DECREF(bulk_obj);
 		return NULL;
 	}
 
 	ret = PyObject_CallMethod((PyObject *)bulk_obj,
-				  "set_values", "O", vals);
+				  "set_values", "(O)", vals);
+	Py_DECREF(bulk_obj);
+	Py_DECREF(vals);
+
+	return ret;
+}
+
+PyDoc_STRVAR(gpiod_Line_set_config_doc,
+"set_config(direction,flags,value) -> None\n"
+"\n"
+"Set the configuration of this GPIO line.\n"
+"\n"
+"  direction\n"
+"    New direction (integer)\n"
+"  flags\n"
+"    New flags (integer)\n"
+"  value\n"
+"    New value (integer)");
+
+static PyObject *gpiod_Line_set_config(gpiod_LineObject *self, PyObject *args)
+{
+	gpiod_LineBulkObject *bulk_obj;
+	PyObject *dirn, *flags, *val, *vals, *ret;
+	int rv;
+
+	val = NULL;
+	rv = PyArg_ParseTuple(args, "OO|O", &dirn, &flags, &val);
+	if (!rv)
+		return NULL;
+
+	bulk_obj = gpiod_LineToLineBulk(self);
+	if (!bulk_obj)
+		return NULL;
+
+	if (val) {
+		vals = Py_BuildValue("(O)", val);
+		if (!vals) {
+			Py_DECREF(bulk_obj);
+			return NULL;
+		}
+		ret = PyObject_CallMethod((PyObject *)bulk_obj,
+				"set_config", "OO(O)", dirn, flags, vals);
+		Py_DECREF(vals);
+	} else
+		ret = PyObject_CallMethod((PyObject *)bulk_obj,
+				"set_config", "OO", dirn, flags);
+
+	Py_DECREF(bulk_obj);
+
+	return ret;
+}
+
+PyDoc_STRVAR(gpiod_Line_set_flags_doc,
+"set_flags(flags) -> None\n"
+"\n"
+"Set the flags of this GPIO line.\n"
+"\n"
+"  flags\n"
+"    New flags (integer)");
+
+static PyObject *gpiod_Line_set_flags(gpiod_LineObject *self, PyObject *args)
+{
+	gpiod_LineBulkObject *bulk_obj;
+	PyObject *ret;
+
+	bulk_obj = gpiod_LineToLineBulk(self);
+	if (!bulk_obj)
+		return NULL;
+
+	ret = PyObject_CallMethod((PyObject *)bulk_obj,
+				  "set_flags", "O", args);
+	Py_DECREF(bulk_obj);
+
+	return ret;
+}
+
+PyDoc_STRVAR(gpiod_Line_set_direction_input_doc,
+"set_direction_input() -> None\n"
+"\n"
+"Set the direction of this GPIO line to input.\n");
+
+static PyObject *gpiod_Line_set_direction_input(gpiod_LineObject *self,
+						PyObject *Py_UNUSED(ignored))
+{
+	gpiod_LineBulkObject *bulk_obj;
+	PyObject *ret;
+
+	bulk_obj = gpiod_LineToLineBulk(self);
+	if (!bulk_obj)
+		return NULL;
+
+	ret = PyObject_CallMethod((PyObject *)bulk_obj,
+				  "set_direction_input", "");
+	Py_DECREF(bulk_obj);
+
+	return ret;
+}
+
+PyDoc_STRVAR(gpiod_Line_set_direction_output_doc,
+"set_direction_output(value) -> None\n"
+"\n"
+"Set the direction of this GPIO line to output.\n"
+"\n"
+"  value\n"
+"    New value (integer)");
+
+static PyObject *gpiod_Line_set_direction_output(gpiod_LineObject *self,
+						 PyObject *args)
+{
+	gpiod_LineBulkObject *bulk_obj;
+	PyObject *val, *vals, *ret;
+	int rv;
+	const char *fmt;
+
+	val = NULL;
+	rv = PyArg_ParseTuple(args, "|O", &val);
+	if (!rv)
+		return NULL;
+
+	if (val) {
+		fmt = "(O)";
+		vals = Py_BuildValue(fmt, val);
+	} else {
+		vals = Py_BuildValue("()");
+		fmt = "O"; /* pass empty args to bulk */
+	}
+	if (!vals)
+		return NULL;
+
+	bulk_obj = gpiod_LineToLineBulk(self);
+	if (!bulk_obj)
+		return NULL;
+
+	ret = PyObject_CallMethod((PyObject *)bulk_obj,
+				  "set_direction_output", fmt, vals);
+
 	Py_DECREF(bulk_obj);
 	Py_DECREF(vals);
 
@@ -838,6 +973,30 @@ static PyMethodDef gpiod_Line_methods[] = {
 		.ml_flags = METH_VARARGS,
 		.ml_doc = gpiod_Line_set_value_doc,
 	},
+	{
+		.ml_name = "set_config",
+		.ml_meth = (PyCFunction)gpiod_Line_set_config,
+		.ml_flags = METH_VARARGS,
+		.ml_doc = gpiod_Line_set_config_doc,
+	},
+	{
+		.ml_name = "set_flags",
+		.ml_meth = (PyCFunction)gpiod_Line_set_flags,
+		.ml_flags = METH_VARARGS,
+		.ml_doc = gpiod_Line_set_flags_doc,
+	},
+	{
+		.ml_name = "set_direction_input",
+		.ml_meth = (PyCFunction)gpiod_Line_set_direction_input,
+		.ml_flags = METH_NOARGS,
+		.ml_doc = gpiod_Line_set_direction_input_doc,
+	},
+	{
+		.ml_name = "set_direction_output",
+		.ml_meth = (PyCFunction)gpiod_Line_set_direction_output,
+		.ml_flags = METH_VARARGS,
+		.ml_doc = gpiod_Line_set_direction_output_doc,
+	},
 	{
 		.ml_name = "release",
 		.ml_meth = (PyCFunction)gpiod_Line_release,
@@ -1283,6 +1442,200 @@ static PyObject *gpiod_LineBulk_set_values(gpiod_LineBulkObject *self,
 	Py_RETURN_NONE;
 }
 
+static int convert_values(PyObject *src, int *dst, Py_ssize_t n)
+{
+	int val;
+	Py_ssize_t num_vals, i;
+	PyObject *iter, *next;
+
+	num_vals = PyObject_Size(src);
+	if (num_vals != n) {
+		PyErr_SetString(PyExc_TypeError,
+			"Number of values must correspond to the number of lines");
+		return -1;
+	}
+	iter = PyObject_GetIter(src);
+	if (!iter)
+		return -1;
+	for (i = 0;; i++) {
+		next = PyIter_Next(iter);
+		if (!next) {
+			Py_DECREF(iter);
+			break;
+		}
+		val = PyLong_AsLong(next);
+		Py_DECREF(next);
+		if (PyErr_Occurred()) {
+			Py_DECREF(iter);
+			return -1;
+		}
+		dst[i] = (int)val;
+	}
+	return 0;
+}
+
+PyDoc_STRVAR(gpiod_LineBulk_set_config_doc,
+"set_config(direction,flags,values) -> None\n"
+"\n"
+"Set the configuration of all the lines held by this LineBulk object.\n"
+"\n"
+"  direction\n"
+"    New direction (integer)\n"
+"  flags\n"
+"    New flags (integer)\n"
+"  values\n"
+"    List of values (integers) to set when direction is output.\n"
+"\n"
+"The number of values in the list passed as argument must be the same as\n"
+"the number of lines held by this gpiod.LineBulk object. The index of each\n"
+"value corresponds to the index of each line in the object.\n");
+
+static PyObject *gpiod_LineBulk_set_config(gpiod_LineBulkObject *self,
+					   PyObject *args)
+{
+	int rv, vals[GPIOD_LINE_BULK_MAX_LINES];
+	PyObject *val_list;
+	struct gpiod_line_bulk bulk;
+	const int *valp;
+	int dirn, flags;
+
+	if (gpiod_LineBulkOwnerIsClosed(self))
+		return NULL;
+
+	gpiod_LineBulkObjToCLineBulk(self, &bulk);
+
+	val_list = NULL;
+	rv = PyArg_ParseTuple(args, "ii|(O)", &dirn, &flags, &val_list);
+	if (!rv)
+		return NULL;
+
+	if (val_list == NULL)
+		valp = NULL;
+	else {
+		memset(vals, 0, sizeof(vals));
+		rv = convert_values(val_list, vals, self->num_lines);
+		if (rv)
+			return NULL;
+		valp = vals;
+	}
+
+	Py_BEGIN_ALLOW_THREADS;
+	rv = gpiod_line_set_config_bulk(&bulk, dirn, flags, valp);
+	Py_END_ALLOW_THREADS;
+	if (rv)
+		return PyErr_SetFromErrno(PyExc_OSError);
+
+	Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(gpiod_LineBulk_set_flags_doc,
+"set_flags(flags) -> None\n"
+"\n"
+"Set the flags of all the lines held by this LineBulk object.\n"
+"\n"
+"  flags\n"
+"    New flags (integer)");
+
+static PyObject *gpiod_LineBulk_set_flags(gpiod_LineBulkObject *self,
+					  PyObject *args)
+{
+	int rv;
+	struct gpiod_line_bulk bulk;
+	int flags;
+
+	if (gpiod_LineBulkOwnerIsClosed(self))
+		return NULL;
+
+	gpiod_LineBulkObjToCLineBulk(self, &bulk);
+
+	rv = PyArg_ParseTuple(args, "i", &flags);
+	if (!rv)
+		return NULL;
+
+	Py_BEGIN_ALLOW_THREADS;
+	rv = gpiod_line_set_flags_bulk(&bulk, flags);
+	Py_END_ALLOW_THREADS;
+	if (rv)
+		return PyErr_SetFromErrno(PyExc_OSError);
+
+	Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(gpiod_LineBulk_set_direction_input_doc,
+"set_direction_input() -> None\n"
+"\n"
+"Set the direction of all the lines held by this LineBulk object to input.\n");
+
+static PyObject *gpiod_LineBulk_set_direction_input(gpiod_LineBulkObject *self,
+						PyObject *Py_UNUSED(ignored))
+{
+	struct gpiod_line_bulk bulk;
+	int rv;
+
+	if (gpiod_LineBulkOwnerIsClosed(self))
+		return NULL;
+
+	gpiod_LineBulkObjToCLineBulk(self, &bulk);
+
+	Py_BEGIN_ALLOW_THREADS;
+	rv = gpiod_line_set_direction_input_bulk(&bulk);
+	Py_END_ALLOW_THREADS;
+	if (rv)
+		return PyErr_SetFromErrno(PyExc_OSError);
+
+	Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(gpiod_LineBulk_set_direction_output_doc,
+"set_direction_output(value) -> None\n"
+"\n"
+"Set the direction of all the lines held by this LineBulk object to output.\n"
+"\n"
+"  values\n"
+"    List of values (integers) to set when direction is output.\n"
+"\n"
+"The number of values in the list passed as argument must be the same as\n"
+"the number of lines held by this gpiod.LineBulk object. The index of each\n"
+"value corresponds to the index of each line in the object.\n");
+
+static PyObject *gpiod_LineBulk_set_direction_output(
+				gpiod_LineBulkObject *self,
+				PyObject *args)
+{
+	int rv, vals[GPIOD_LINE_BULK_MAX_LINES];
+	PyObject *val_list;
+	struct gpiod_line_bulk bulk;
+	const int *valp;
+
+	if (gpiod_LineBulkOwnerIsClosed(self))
+		return NULL;
+
+	gpiod_LineBulkObjToCLineBulk(self, &bulk);
+
+	val_list = NULL;
+	rv = PyArg_ParseTuple(args, "|O", &val_list);
+	if (!rv)
+		return NULL;
+
+	if (val_list == NULL)
+		valp = NULL;
+	else {
+		memset(vals, 0, sizeof(vals));
+		rv = convert_values(val_list, vals, self->num_lines);
+		if (rv)
+			return NULL;
+		valp = vals;
+	}
+
+	Py_BEGIN_ALLOW_THREADS;
+	rv = gpiod_line_set_direction_output_bulk(&bulk, valp);
+	Py_END_ALLOW_THREADS;
+	if (rv)
+		return PyErr_SetFromErrno(PyExc_OSError);
+
+	Py_RETURN_NONE;
+}
+
 PyDoc_STRVAR(gpiod_LineBulk_release_doc,
 "release() -> None\n"
 "\n"
@@ -1431,6 +1784,30 @@ static PyMethodDef gpiod_LineBulk_methods[] = {
 		.ml_doc = gpiod_LineBulk_set_values_doc,
 		.ml_flags = METH_VARARGS,
 	},
+	{
+		.ml_name = "set_config",
+		.ml_meth = (PyCFunction)gpiod_LineBulk_set_config,
+		.ml_flags = METH_VARARGS,
+		.ml_doc = gpiod_LineBulk_set_config_doc,
+	},
+	{
+		.ml_name = "set_flags",
+		.ml_meth = (PyCFunction)gpiod_LineBulk_set_flags,
+		.ml_flags = METH_VARARGS,
+		.ml_doc = gpiod_LineBulk_set_flags_doc,
+	},
+	{
+		.ml_name = "set_direction_input",
+		.ml_meth = (PyCFunction)gpiod_LineBulk_set_direction_input,
+		.ml_flags = METH_NOARGS,
+		.ml_doc = gpiod_LineBulk_set_direction_input_doc,
+	},
+	{
+		.ml_name = "set_direction_output",
+		.ml_meth = (PyCFunction)gpiod_LineBulk_set_direction_output,
+		.ml_flags = METH_VARARGS,
+		.ml_doc = gpiod_LineBulk_set_direction_output_doc,
+	},
 	{
 		.ml_name = "release",
 		.ml_meth = (PyCFunction)gpiod_LineBulk_release,
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 12/14] bindings: python: tests: add tests for SET_CONFIG methods
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (10 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 11/14] bindings: python: add support for SET_CONFIG Kent Gibson
@ 2019-11-25 14:31 ` Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 13/14] tools: add support for bias flags Kent Gibson
  2019-11-25 14:31 ` [libgpiod][PATCH v3 14/14] tools: add tests for bias and drive flags Kent Gibson
  13 siblings, 0 replies; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Extend test coverage over set_config, set_flags, set_direction_input, and
set_direction_output methods.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 bindings/python/tests/gpiod_py_test.py | 163 +++++++++++++++++++++++++
 1 file changed, 163 insertions(+)

diff --git a/bindings/python/tests/gpiod_py_test.py b/bindings/python/tests/gpiod_py_test.py
index 9330b43..704d916 100755
--- a/bindings/python/tests/gpiod_py_test.py
+++ b/bindings/python/tests/gpiod_py_test.py
@@ -493,6 +493,169 @@ class LineValues(MockupTestCase):
             line.set_value(0)
             self.assertEqual(mockup.chip_get_value(0, 3), 1)
 
+class LineConfig(MockupTestCase):
+
+    chip_sizes = ( 8, )
+
+    def test_set_config_direction(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(3)
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_IN)
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_INPUT)
+            line.set_config(gpiod.LINE_REQ_DIR_IN, 0, 0)
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_INPUT)
+            line.set_config(gpiod.LINE_REQ_DIR_OUT,0,0)
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT)
+
+    def test_set_config_flags(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(3)
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_OUT)
+            line.set_config(gpiod.LINE_REQ_DIR_OUT,
+                            gpiod.LINE_REQ_FLAG_ACTIVE_LOW, 0)
+            self.assertEqual(mockup.chip_get_value(0, 3), 1)
+            line.set_config(gpiod.LINE_REQ_DIR_OUT, 0, 0)
+            self.assertEqual(mockup.chip_get_value(0, 3), 0)
+
+    def test_set_config_output_value(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(3)
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_IN)
+            line.set_config(gpiod.LINE_REQ_DIR_OUT,0,1)
+            self.assertEqual(mockup.chip_get_value(0, 3), 1)
+            line.set_config(gpiod.LINE_REQ_DIR_OUT,0,0)
+            self.assertEqual(mockup.chip_get_value(0, 3), 0)
+
+    def test_set_config_output_no_value(self):
+         with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(3)
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_OUT,
+                         default_val=1)
+            self.assertEqual(mockup.chip_get_value(0, 3), 1)
+            line.set_config(gpiod.LINE_REQ_DIR_OUT,0)
+            self.assertEqual(mockup.chip_get_value(0, 3), 0)
+
+    def test_set_config_bulk_output_no_values(self):
+         with gpiod.Chip(mockup.chip_name(0)) as chip:
+            lines = chip.get_lines(( 0, 3, 4, 6 ))
+            lines.request(consumer=default_consumer,
+                          type=gpiod.LINE_REQ_DIR_OUT,
+                          default_vals=(1,1,1,1))
+            self.assertEqual(mockup.chip_get_value(0, 0), 1)
+            self.assertEqual(mockup.chip_get_value(0, 3), 1)
+            self.assertEqual(mockup.chip_get_value(0, 4), 1)
+            self.assertEqual(mockup.chip_get_value(0, 6), 1)
+            lines.set_config(gpiod.LINE_REQ_DIR_OUT,0)
+            self.assertEqual(mockup.chip_get_value(0, 0), 0)
+            self.assertEqual(mockup.chip_get_value(0, 3), 0)
+            self.assertEqual(mockup.chip_get_value(0, 4), 0)
+            self.assertEqual(mockup.chip_get_value(0, 6), 0)
+
+class LineFlags(MockupTestCase):
+
+    chip_sizes = ( 8, )
+
+    def test_set_flags(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(3)
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_OUT,
+                         default_val=1)
+            self.assertEqual(mockup.chip_get_value(0, 3), 1)
+            line.set_flags(gpiod.LINE_REQ_FLAG_ACTIVE_LOW)
+            self.assertEqual(mockup.chip_get_value(0, 3), 0)
+            line.set_flags(0)
+            self.assertEqual(mockup.chip_get_value(0, 3), 1)
+
+    def test_set_flags_bulk(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            lines = chip.get_lines(( 0, 3, 4, 6 ))
+            lines.request(consumer=default_consumer,
+                          type=gpiod.LINE_REQ_DIR_OUT,
+                          default_vals=(1,1,1,1))
+            self.assertEqual(mockup.chip_get_value(0, 0), 1)
+            self.assertEqual(mockup.chip_get_value(0, 3), 1)
+            self.assertEqual(mockup.chip_get_value(0, 4), 1)
+            self.assertEqual(mockup.chip_get_value(0, 6), 1)
+            lines.set_flags(gpiod.LINE_REQ_FLAG_ACTIVE_LOW)
+            self.assertEqual(mockup.chip_get_value(0, 0), 0)
+            self.assertEqual(mockup.chip_get_value(0, 3), 0)
+            self.assertEqual(mockup.chip_get_value(0, 4), 0)
+            self.assertEqual(mockup.chip_get_value(0, 6), 0)
+            lines.set_flags(0)
+            self.assertEqual(mockup.chip_get_value(0, 0), 1)
+            self.assertEqual(mockup.chip_get_value(0, 3), 1)
+            self.assertEqual(mockup.chip_get_value(0, 4), 1)
+            self.assertEqual(mockup.chip_get_value(0, 6), 1)
+
+class LineDirection(MockupTestCase):
+
+    chip_sizes = ( 8, )
+
+    def test_set_direction(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            line = chip.get_line(3)
+            line.request(consumer=default_consumer,
+                         type=gpiod.LINE_REQ_DIR_OUT)
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT)
+            line.set_direction_input()
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_INPUT)
+            line.set_direction_output(0)
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(mockup.chip_get_value(0, 3), 0)
+            line.set_direction_output(1)
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(mockup.chip_get_value(0, 3), 1)
+            line.set_direction_output()
+            self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(mockup.chip_get_value(0, 3), 0)
+
+    def test_set_direction_bulk(self):
+        with gpiod.Chip(mockup.chip_name(0)) as chip:
+            lines = chip.get_lines(( 0, 3, 4, 6 ))
+            lines.request(consumer=default_consumer,
+                          type=gpiod.LINE_REQ_DIR_OUT)
+            self.assertEqual(lines.to_list()[0].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[1].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[2].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[3].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            lines.set_direction_input()
+            self.assertEqual(lines.to_list()[0].direction(), gpiod.Line.DIRECTION_INPUT)
+            self.assertEqual(lines.to_list()[1].direction(), gpiod.Line.DIRECTION_INPUT)
+            self.assertEqual(lines.to_list()[2].direction(), gpiod.Line.DIRECTION_INPUT)
+            self.assertEqual(lines.to_list()[3].direction(), gpiod.Line.DIRECTION_INPUT)
+            lines.set_direction_output((0,0,1,0))
+            self.assertEqual(lines.to_list()[0].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[1].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[2].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[3].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(mockup.chip_get_value(0, 0), 0)
+            self.assertEqual(mockup.chip_get_value(0, 3), 0)
+            self.assertEqual(mockup.chip_get_value(0, 4), 1)
+            self.assertEqual(mockup.chip_get_value(0, 6), 0)
+            lines.set_direction_output((1,1,1,0))
+            self.assertEqual(lines.to_list()[0].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[1].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[2].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[3].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(mockup.chip_get_value(0, 0), 1)
+            self.assertEqual(mockup.chip_get_value(0, 3), 1)
+            self.assertEqual(mockup.chip_get_value(0, 4), 1)
+            self.assertEqual(mockup.chip_get_value(0, 6), 0)
+            lines.set_direction_output()
+            self.assertEqual(lines.to_list()[0].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[1].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[2].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(lines.to_list()[3].direction(), gpiod.Line.DIRECTION_OUTPUT)
+            self.assertEqual(mockup.chip_get_value(0, 0), 0)
+            self.assertEqual(mockup.chip_get_value(0, 3), 0)
+            self.assertEqual(mockup.chip_get_value(0, 4), 0)
+            self.assertEqual(mockup.chip_get_value(0, 6), 0)
+
 class LineRequestBehavior(MockupTestCase):
 
     chip_sizes = ( 8, )
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 13/14] tools: add support for bias flags
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (11 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 12/14] bindings: python: tests: add tests for SET_CONFIG methods Kent Gibson
@ 2019-11-25 14:31 ` Kent Gibson
  2019-11-28 10:29   ` Bartosz Golaszewski
  2019-11-25 14:31 ` [libgpiod][PATCH v3 14/14] tools: add tests for bias and drive flags Kent Gibson
  13 siblings, 1 reply; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Add support for bias flags to applicable tools - gpioget, gpioset, and
gpiomon.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 tools/gpioget.c | 32 +++++++++++++++++++++++++----
 tools/gpiomon.c | 36 +++++++++++++++++++++++++++------
 tools/gpioset.c | 54 ++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 107 insertions(+), 15 deletions(-)

diff --git a/tools/gpioget.c b/tools/gpioget.c
index 196ebeb..17614cb 100644
--- a/tools/gpioget.c
+++ b/tools/gpioget.c
@@ -17,10 +17,11 @@ static const struct option longopts[] = {
 	{ "help",	no_argument,	NULL,	'h' },
 	{ "version",	no_argument,	NULL,	'v' },
 	{ "active-low",	no_argument,	NULL,	'l' },
+	{ "bias", required_argument,	NULL,	'B' },
 	{ GETOPT_NULL_LONGOPT },
 };
 
-static const char *const shortopts = "+hvl";
+static const char *const shortopts = "+hvlB:";
 
 static void print_help(void)
 {
@@ -32,6 +33,25 @@ static void print_help(void)
 	printf("  -h, --help:\t\tdisplay this message and exit\n");
 	printf("  -v, --version:\tdisplay the version and exit\n");
 	printf("  -l, --active-low:\tset the line active state to low\n");
+	printf("  -B, --bias=[as-is|disable|pull-down|pull-up] (defaults to 'as-is'):\n");
+	printf("		set the line bias\n");
+	printf("\n");
+	printf("Biases:\n");
+	printf("  as-is:\tleave bias unchanged\n");
+	printf("  disable:\tdisable bias\n");
+	printf("  pull-up:\tenable pull-up\n");
+	printf("  pull-down:\tenable pull-down\n");
+}
+
+static int bias_flags(const char *option)
+{
+	if (strcmp(option, "pull-down") == 0)
+		return GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN;
+	if (strcmp(option, "pull-up") == 0)
+		return GPIOD_CTXLESS_FLAG_BIAS_PULL_UP;
+	if (strcmp(option, "disable") == 0)
+		return GPIOD_CTXLESS_FLAG_BIAS_DISABLE;
+	return 0;
 }
 
 int main(int argc, char **argv)
@@ -39,6 +59,7 @@ int main(int argc, char **argv)
 	unsigned int *offsets, i, num_lines;
 	int *values, optc, opti, rv;
 	bool active_low = false;
+	int flags = 0;
 	char *device, *end;
 
 	for (;;) {
@@ -56,6 +77,9 @@ int main(int argc, char **argv)
 		case 'l':
 			active_low = true;
 			break;
+		case 'B':
+			flags = bias_flags(optarg);
+			break;
 		case '?':
 			die("try %s --help", get_progname());
 		default:
@@ -86,9 +110,9 @@ int main(int argc, char **argv)
 			die("invalid GPIO offset: %s", argv[i + 1]);
 	}
 
-	rv = gpiod_ctxless_get_value_multiple(device, offsets, values,
-					      num_lines, active_low,
-					      "gpioget");
+	rv = gpiod_ctxless_get_value_multiple_ext(device, offsets, values,
+						  num_lines, active_low,
+						  "gpioget", flags);
 	if (rv < 0)
 		die_perror("error reading GPIO values");
 
diff --git a/tools/gpiomon.c b/tools/gpiomon.c
index 9a1843b..687212d 100644
--- a/tools/gpiomon.c
+++ b/tools/gpiomon.c
@@ -22,6 +22,7 @@ static const struct option longopts[] = {
 	{ "help",		no_argument,		NULL,	'h' },
 	{ "version",		no_argument,		NULL,	'v' },
 	{ "active-low",		no_argument,		NULL,	'l' },
+	{ "bias",		required_argument,	NULL,	'B' },
 	{ "num-events",		required_argument,	NULL,	'n' },
 	{ "silent",		no_argument,		NULL,	's' },
 	{ "rising-edge",	no_argument,		NULL,	'r' },
@@ -31,7 +32,7 @@ static const struct option longopts[] = {
 	{ GETOPT_NULL_LONGOPT },
 };
 
-static const char *const shortopts = "+hvln:srfbF:";
+static const char *const shortopts = "+hvlB:n:srfbF:";
 
 static void print_help(void)
 {
@@ -43,6 +44,8 @@ static void print_help(void)
 	printf("  -h, --help:\t\tdisplay this message and exit\n");
 	printf("  -v, --version:\tdisplay the version and exit\n");
 	printf("  -l, --active-low:\tset the line active state to low\n");
+	printf("  -B, --bias=[as-is|disable|pull-down|pull-up] (defaults to 'as-is'):\n");
+	printf("		set the line bias\n");
 	printf("  -n, --num-events=NUM:\texit after processing NUM events\n");
 	printf("  -s, --silent:\t\tdon't print event info\n");
 	printf("  -r, --rising-edge:\tonly process rising edge events\n");
@@ -50,6 +53,12 @@ static void print_help(void)
 	printf("  -b, --line-buffered:\tset standard output as line buffered\n");
 	printf("  -F, --format=FMT\tspecify custom output format\n");
 	printf("\n");
+	printf("Biases:\n");
+	printf("  as-is:\tleave bias unchanged\n");
+	printf("  disable:\tdisable bias\n");
+	printf("  pull-up:\tenable pull-up\n");
+	printf("  pull-down:\tenable pull-down\n");
+	printf("\n");
 	printf("Format specifiers:\n");
 	printf("  %%o:  GPIO line offset\n");
 	printf("  %%e:  event type (0 - falling edge, 1 rising edge)\n");
@@ -240,10 +249,22 @@ static int make_signalfd(void)
 	return sigfd;
 }
 
+static int bias_flags(const char *option)
+{
+	if (strcmp(option, "pull-down") == 0)
+		return GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN;
+	if (strcmp(option, "pull-up") == 0)
+		return GPIOD_CTXLESS_FLAG_BIAS_PULL_UP;
+	if (strcmp(option, "disable") == 0)
+		return GPIOD_CTXLESS_FLAG_BIAS_DISABLE;
+	return 0;
+}
+
 int main(int argc, char **argv)
 {
 	unsigned int offsets[GPIOD_LINE_BULK_MAX_LINES], num_lines = 0, offset;
 	bool active_low = false, watch_rising = false, watch_falling = false;
+	int flags = 0;
 	struct timespec timeout = { 10, 0 };
 	int optc, opti, rv, i, event_type;
 	struct mon_ctx ctx;
@@ -266,6 +287,9 @@ int main(int argc, char **argv)
 		case 'l':
 			active_low = true;
 			break;
+		case 'B':
+			flags = bias_flags(optarg);
+			break;
 		case 'n':
 			ctx.events_wanted = strtoul(optarg, &end, 10);
 			if (*end != '\0')
@@ -320,11 +344,11 @@ int main(int argc, char **argv)
 
 	ctx.sigfd = make_signalfd();
 
-	rv = gpiod_ctxless_event_monitor_multiple(argv[0], event_type,
-						  offsets, num_lines,
-						  active_low, "gpiomon",
-						  &timeout, poll_callback,
-						  event_callback, &ctx);
+	rv = gpiod_ctxless_event_monitor_multiple_ext(
+				argv[0], event_type, offsets,
+				num_lines, active_low, "gpiomon",
+				&timeout, poll_callback,
+				event_callback, &ctx, flags);
 	if (rv)
 		die_perror("error waiting for events");
 
diff --git a/tools/gpioset.c b/tools/gpioset.c
index d9977a7..b91baea 100644
--- a/tools/gpioset.c
+++ b/tools/gpioset.c
@@ -23,6 +23,8 @@ static const struct option longopts[] = {
 	{ "help",		no_argument,		NULL,	'h' },
 	{ "version",		no_argument,		NULL,	'v' },
 	{ "active-low",		no_argument,		NULL,	'l' },
+	{ "bias",		required_argument,	NULL,	'B' },
+	{ "drive",		required_argument,	NULL,	'D' },
 	{ "mode",		required_argument,	NULL,	'm' },
 	{ "sec",		required_argument,	NULL,	's' },
 	{ "usec",		required_argument,	NULL,	'u' },
@@ -30,7 +32,7 @@ static const struct option longopts[] = {
 	{ GETOPT_NULL_LONGOPT },
 };
 
-static const char *const shortopts = "+hvlm:s:u:b";
+static const char *const shortopts = "+hvlB:D:m:s:u:b";
 
 static void print_help(void)
 {
@@ -42,12 +44,27 @@ static void print_help(void)
 	printf("  -h, --help:\t\tdisplay this message and exit\n");
 	printf("  -v, --version:\tdisplay the version and exit\n");
 	printf("  -l, --active-low:\tset the line active state to low\n");
+	printf("  -B, --bias=[as-is|disable|pull-down|pull-up] (defaults to 'as-is'):\n");
+	printf("		set the line bias\n");
+	printf("  -D, --drive=[push-pull|open-drain|open-source] (defaults to 'push-pull'):\n");
+	printf("		set the line drive mode\n");
 	printf("  -m, --mode=[exit|wait|time|signal] (defaults to 'exit'):\n");
 	printf("		tell the program what to do after setting values\n");
 	printf("  -s, --sec=SEC:\tspecify the number of seconds to wait (only valid for --mode=time)\n");
 	printf("  -u, --usec=USEC:\tspecify the number of microseconds to wait (only valid for --mode=time)\n");
 	printf("  -b, --background:\tafter setting values: detach from the controlling terminal\n");
 	printf("\n");
+	printf("Biases:\n");
+	printf("  as-is:\tleave bias unchanged\n");
+	printf("  disable:\tdisable bias\n");
+	printf("  pull-up:\tenable pull-up\n");
+	printf("  pull-down:\tenable pull-down\n");
+	printf("\n");
+	printf("Drives:\n");
+	printf("  push-pull:\tdrive the line both high and low\n");
+	printf("  open-drain:\tdrive the line low or go high impedance\n");
+	printf("  open-source:\tdrive the line high or go high impedance\n");
+	printf("\n");
 	printf("Modes:\n");
 	printf("  exit:\t\tset values and exit immediately\n");
 	printf("  wait:\t\tset values and wait for user to press ENTER\n");
@@ -178,11 +195,31 @@ static const struct mode_mapping *parse_mode(const char *mode)
 	return NULL;
 }
 
+static int bias_flags(const char *option)
+{
+	if (strcmp(option, "pull-down") == 0)
+		return GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN;
+	if (strcmp(option, "pull-up") == 0)
+		return GPIOD_CTXLESS_FLAG_BIAS_PULL_UP;
+	if (strcmp(option, "disable") == 0)
+		return GPIOD_CTXLESS_FLAG_BIAS_DISABLE;
+	return 0;
+}
+
+static int drive_flags(const char *option)
+{
+	if (strcmp(option, "open-drain") == 0)
+		return GPIOD_CTXLESS_FLAG_OPEN_DRAIN;
+	if (strcmp(option, "open-source") == 0)
+		return GPIOD_CTXLESS_FLAG_OPEN_SOURCE;
+	return 0;
+}
+
 int main(int argc, char **argv)
 {
 	const struct mode_mapping *mode = &modes[MODE_EXIT];
 	unsigned int *offsets, num_lines, i;
-	int *values, rv, optc, opti;
+	int *values, rv, optc, opti, flags = 0;
 	struct callback_data cbdata;
 	bool active_low = false;
 	char *device, *end;
@@ -204,6 +241,12 @@ int main(int argc, char **argv)
 		case 'l':
 			active_low = true;
 			break;
+		case 'B':
+			flags |= bias_flags(optarg);
+			break;
+		case 'D':
+			flags |= drive_flags(optarg);
+			break;
 		case 'm':
 			mode = parse_mode(optarg);
 			if (!mode)
@@ -268,9 +311,10 @@ int main(int argc, char **argv)
 			die("invalid offset: %s", argv[i + 1]);
 	}
 
-	rv = gpiod_ctxless_set_value_multiple(device, offsets, values,
-					      num_lines, active_low, "gpioset",
-					      mode->callback, &cbdata);
+	rv = gpiod_ctxless_set_value_multiple_ext(
+				device, offsets, values,
+				num_lines, active_low, "gpioset",
+				mode->callback, &cbdata, flags);
 	if (rv < 0)
 		die_perror("error setting the GPIO line values");
 
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [libgpiod][PATCH v3 14/14] tools: add tests for bias and drive flags
  2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
                   ` (12 preceding siblings ...)
  2019-11-25 14:31 ` [libgpiod][PATCH v3 13/14] tools: add support for bias flags Kent Gibson
@ 2019-11-25 14:31 ` Kent Gibson
  13 siblings, 0 replies; 21+ messages in thread
From: Kent Gibson @ 2019-11-25 14:31 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Add tests for bias flags to applicable tools - gpioget, gpioset, and
gpiomon, as well as drive flags for gpioset.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 tools/gpio-tools-test.bats | 139 +++++++++++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/tools/gpio-tools-test.bats b/tools/gpio-tools-test.bats
index aff54f7..8bced02 100755
--- a/tools/gpio-tools-test.bats
+++ b/tools/gpio-tools-test.bats
@@ -312,6 +312,34 @@ teardown() {
 	test "$output" = "1 1 0 0 1 0 1 0"
 }
 
+@test "gpioget: read all lines (pull-up)" {
+	gpio_mockup_probe 8 8 8
+
+	gpio_mockup_set_pull 1 2 1
+	gpio_mockup_set_pull 1 3 1
+	gpio_mockup_set_pull 1 5 1
+	gpio_mockup_set_pull 1 7 1
+
+	run_tool gpioget --bias=pull-up "$(gpio_mockup_chip_name 1)" 0 1 2 3 4 5 6 7
+
+	test "$status" -eq "0"
+	test "$output" = "1 1 1 1 1 1 1 1"
+}
+
+@test "gpioget: read all lines (pull-down)" {
+	gpio_mockup_probe 8 8 8
+
+	gpio_mockup_set_pull 1 2 1
+	gpio_mockup_set_pull 1 3 1
+	gpio_mockup_set_pull 1 5 1
+	gpio_mockup_set_pull 1 7 1
+
+	run_tool gpioget --bias=pull-down "$(gpio_mockup_chip_name 1)" 0 1 2 3 4 5 6 7
+
+	test "$status" -eq "0"
+	test "$output" = "0 0 0 0 0 0 0 0"
+}
+
 @test "gpioget: read some lines" {
 	gpio_mockup_probe 8 8 8
 
@@ -405,6 +433,79 @@ teardown() {
 	test "$status" -eq "0"
 }
 
+@test "gpioset: set lines and wait for SIGTERM (push-pull)" {
+	gpio_mockup_probe 8 8 8
+
+	coproc_run_tool gpioset --drive=push-pull --mode=signal "$(gpio_mockup_chip_name 2)" \
+					0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1
+
+	gpio_mockup_check_value 2 0 0
+	gpio_mockup_check_value 2 1 0
+	gpio_mockup_check_value 2 2 1
+	gpio_mockup_check_value 2 3 1
+	gpio_mockup_check_value 2 4 1
+	gpio_mockup_check_value 2 5 1
+	gpio_mockup_check_value 2 6 0
+	gpio_mockup_check_value 2 7 1
+
+	coproc_tool_kill
+	coproc_tool_wait
+
+	test "$status" -eq "0"
+}
+
+@test "gpioset: set lines and wait for SIGTERM (open-drain)" {
+	gpio_mockup_probe 8 8 8
+
+	gpio_mockup_set_pull 2 2 1
+	gpio_mockup_set_pull 2 3 1
+	gpio_mockup_set_pull 2 5 1
+	gpio_mockup_set_pull 2 7 1
+
+	coproc_run_tool gpioset --drive=open-drain --mode=signal "$(gpio_mockup_chip_name 2)" \
+					0=0 1=0 2=1 3=1 4=1 5=1 6=0 7=1
+
+	gpio_mockup_check_value 2 0 0
+	gpio_mockup_check_value 2 1 0
+	gpio_mockup_check_value 2 2 1
+	gpio_mockup_check_value 2 3 1
+	gpio_mockup_check_value 2 4 0
+	gpio_mockup_check_value 2 5 1
+	gpio_mockup_check_value 2 6 0
+	gpio_mockup_check_value 2 7 1
+
+	coproc_tool_kill
+	coproc_tool_wait
+
+	test "$status" -eq "0"
+}
+
+@test "gpioset: set lines and wait for SIGTERM (open-source)" {
+	gpio_mockup_probe 8 8 8
+
+	gpio_mockup_set_pull 2 2 1
+	gpio_mockup_set_pull 2 3 1
+	gpio_mockup_set_pull 2 5 1
+	gpio_mockup_set_pull 2 7 1
+
+	coproc_run_tool gpioset --drive=open-source --mode=signal "$(gpio_mockup_chip_name 2)" \
+					0=0 1=0 2=1 3=0 4=1 5=1 6=0 7=1
+
+	gpio_mockup_check_value 2 0 0
+	gpio_mockup_check_value 2 1 0
+	gpio_mockup_check_value 2 2 1
+	gpio_mockup_check_value 2 3 1
+	gpio_mockup_check_value 2 4 1
+	gpio_mockup_check_value 2 5 1
+	gpio_mockup_check_value 2 6 0
+	gpio_mockup_check_value 2 7 1
+
+	coproc_tool_kill
+	coproc_tool_wait
+
+	test "$status" -eq "0"
+}
+
 @test "gpioset: set some lines and wait for ENTER" {
 	gpio_mockup_probe 8 8 8
 
@@ -576,6 +677,44 @@ teardown() {
 "event:\\s+FALLING\\s+EDGE\\s+offset:\\s+4\\s+timestamp:\\s+\[[0-9]+\.[0-9]+\]"
 }
 
+@test "gpiomon: single falling edge event (pull-up)" {
+	gpio_mockup_probe 8 8
+
+	gpio_mockup_set_pull 1 4 0
+
+	coproc_run_tool gpiomon --bias=pull-up "$(gpio_mockup_chip_name 1)" 4
+
+	gpio_mockup_set_pull 1 4 0
+	sleep 0.2
+
+	coproc_tool_kill
+	coproc_tool_wait
+
+	test "$status" -eq "0"
+	output_regex_match \
+"event:\\s+FALLING\\s+EDGE\\s+offset:\\s+4\\s+timestamp:\\s+\[[0-9]+\.[0-9]+\]"
+
+}
+
+@test "gpiomon: single rising edge event (pull-down)" {
+	gpio_mockup_probe 8 8
+
+	gpio_mockup_set_pull 1 4 1
+
+	coproc_run_tool gpiomon --bias=pull-down "$(gpio_mockup_chip_name 1)" 4
+
+	gpio_mockup_set_pull 1 4 1
+	sleep 0.2
+
+	coproc_tool_kill
+	coproc_tool_wait
+
+	test "$status" -eq "0"
+	output_regex_match \
+"event:\\s+RISING\\s+EDGE\\s+offset:\\s+4\\s+timestamp:\\s+\[[0-9]+\.[0-9]+\]"
+
+}
+
 @test "gpiomon: single rising edge event (active-low)" {
 	gpio_mockup_probe 8 8
 
-- 
2.24.0


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [libgpiod][PATCH v3 02/14] tests: add tests for bias flags
  2019-11-25 14:31 ` [libgpiod][PATCH v3 02/14] tests: add tests " Kent Gibson
@ 2019-11-28 10:28   ` Bartosz Golaszewski
  0 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2019-11-28 10:28 UTC (permalink / raw)
  To: Kent Gibson; +Cc: linux-gpio

pon., 25 lis 2019 o 15:32 Kent Gibson <warthog618@gmail.com> napisał(a):
>
> Extend test coverage over the bias flags, gpiod_line_bias and the extended
> ctxless functions.
>
> Also update existing tests to check bias flags where line state is checked.
>

The kernel changes this relies on will first be available in v5.5, so
you need to change MIN_KERNEL_VERSION here and elsewhere (C++ and
Python tests).


> Signed-off-by: Kent Gibson <warthog618@gmail.com>
> ---
>  tests/tests-ctxless.c |  64 +++++++++++++++++++++-
>  tests/tests-event.c   | 120 ++++++++++++++++++++++++++++++++++++++++++
>  tests/tests-line.c    |  98 ++++++++++++++++++++++++++++++++++
>  3 files changed, 280 insertions(+), 2 deletions(-)
>
> diff --git a/tests/tests-ctxless.c b/tests/tests-ctxless.c
> index c1e1ca6..76b9a7c 100644
> --- a/tests/tests-ctxless.c
> +++ b/tests/tests-ctxless.c
> @@ -26,11 +26,41 @@ GPIOD_TEST_CASE(get_value, 0, { 8 })
>         g_assert_cmpint(ret, ==, 1);
>  }
>
> -static void set_value_check(gpointer data G_GNUC_UNUSED)
> +GPIOD_TEST_CASE(get_value_ext, 0, { 8 })
> +{
> +       gint ret;
> +
> +       ret = gpiod_ctxless_get_value_ext(gpiod_test_chip_name(0), 3,
> +                               false, GPIOD_TEST_CONSUMER,
> +                               GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       ret = gpiod_ctxless_get_value_ext(gpiod_test_chip_name(0), 3,
> +                               false, GPIOD_TEST_CONSUMER,
> +                               GPIOD_CTXLESS_FLAG_BIAS_PULL_UP);
> +       g_assert_cmpint(ret, ==, 1);
> +
> +       ret = gpiod_ctxless_get_value_ext(gpiod_test_chip_name(0), 3,
> +                               true, GPIOD_TEST_CONSUMER
> +                               , GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN);
> +       g_assert_cmpint(ret, ==, 1);
> +
> +       ret = gpiod_ctxless_get_value_ext(gpiod_test_chip_name(0), 3,
> +                               true, GPIOD_TEST_CONSUMER,
> +                               GPIOD_CTXLESS_FLAG_BIAS_PULL_UP);
> +       g_assert_cmpint(ret, ==, 0);
> +}
> +
> +static void set_value_check_hi(gpointer data G_GNUC_UNUSED)
>  {
>         g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 1);
>  }
>
> +static void set_value_check_lo(gpointer data G_GNUC_UNUSED)
> +{
> +       g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 0);
> +}
> +
>  GPIOD_TEST_CASE(set_value, 0, { 8 })
>  {
>         gint ret;
> @@ -39,13 +69,43 @@ GPIOD_TEST_CASE(set_value, 0, { 8 })
>
>         ret = gpiod_ctxless_set_value(gpiod_test_chip_name(0), 3, 1,
>                                       false, GPIOD_TEST_CONSUMER,
> -                                     set_value_check, NULL);
> +                                     set_value_check_hi, NULL);
>         gpiod_test_return_if_failed();
>         g_assert_cmpint(ret, ==, 0);
>
>         g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 0);
>  }
>
> +GPIOD_TEST_CASE(set_value_ext, 0, { 8 })
> +{
> +       gint ret;
> +
> +       gpiod_test_chip_set_pull(0, 3, 0);
> +
> +       ret = gpiod_ctxless_set_value_ext(gpiod_test_chip_name(0), 3, 1,
> +                       false, GPIOD_TEST_CONSUMER,
> +                       set_value_check_hi, NULL, 0);
> +       gpiod_test_return_if_failed();
> +       g_assert_cmpint(ret, ==, 0);
> +       g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 0);
> +
> +       /* test drive flags by checking that sets are caught by emulation */
> +       ret = gpiod_ctxless_set_value_ext(gpiod_test_chip_name(0), 3, 1,
> +                       false, GPIOD_TEST_CONSUMER, set_value_check_lo,
> +                       NULL, GPIOD_CTXLESS_FLAG_OPEN_DRAIN);
> +       gpiod_test_return_if_failed();
> +       g_assert_cmpint(ret, ==, 0);
> +       g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 0);
> +
> +       gpiod_test_chip_set_pull(0, 3, 1);
> +       ret = gpiod_ctxless_set_value_ext(gpiod_test_chip_name(0), 3, 0,
> +                       false, GPIOD_TEST_CONSUMER, set_value_check_hi,
> +                       NULL, GPIOD_CTXLESS_FLAG_OPEN_SOURCE);
> +       gpiod_test_return_if_failed();
> +       g_assert_cmpint(ret, ==, 0);
> +       g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 1);
> +}
> +
>  static const guint get_value_multiple_offsets[] = {
>         1, 3, 4, 5, 6, 7, 8, 9, 13, 14
>  };
> diff --git a/tests/tests-event.c b/tests/tests-event.c
> index 28b77ec..d425d1a 100644
> --- a/tests/tests-event.c
> +++ b/tests/tests-event.c
> @@ -196,6 +196,126 @@ GPIOD_TEST_CASE(both_edges_active_low, 0, { 8 })
>         g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE);
>  }
>
> +GPIOD_TEST_CASE(both_edges_bias_disable, 0, { 8 })
> +{
> +       g_autoptr(GpiodTestEventThread) ev_thread = NULL;
> +       g_autoptr(gpiod_chip_struct) chip = NULL;
> +       struct timespec ts = { 1, 0 };
> +       struct gpiod_line_event ev;
> +       struct gpiod_line *line;
> +       gint ret;
> +
> +       chip = gpiod_chip_open(gpiod_test_chip_path(0));
> +       g_assert_nonnull(chip);
> +       gpiod_test_return_if_failed();
> +
> +       line = gpiod_chip_get_line(chip, 7);
> +       g_assert_nonnull(line);
> +       gpiod_test_return_if_failed();
> +
> +       ret = gpiod_line_request_both_edges_events_flags(line,
> +               GPIOD_TEST_CONSUMER, GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       ev_thread = gpiod_test_start_event_thread(0, 7, 100);
> +
> +       ret = gpiod_line_event_wait(line, &ts);
> +       g_assert_cmpint(ret, ==, 1);
> +
> +       ret = gpiod_line_event_read(line, &ev);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE);
> +
> +       ret = gpiod_line_event_wait(line, &ts);
> +       g_assert_cmpint(ret, ==, 1);
> +
> +       ret = gpiod_line_event_read(line, &ev);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_FALLING_EDGE);
> +}
> +
> +GPIOD_TEST_CASE(both_edges_bias_pull_down, 0, { 8 })
> +{
> +       g_autoptr(GpiodTestEventThread) ev_thread = NULL;
> +       g_autoptr(gpiod_chip_struct) chip = NULL;
> +       struct timespec ts = { 1, 0 };
> +       struct gpiod_line_event ev;
> +       struct gpiod_line *line;
> +       gint ret;
> +
> +       chip = gpiod_chip_open(gpiod_test_chip_path(0));
> +       g_assert_nonnull(chip);
> +       gpiod_test_return_if_failed();
> +
> +       line = gpiod_chip_get_line(chip, 7);
> +       g_assert_nonnull(line);
> +       gpiod_test_return_if_failed();
> +
> +       ret = gpiod_line_request_both_edges_events_flags(line,
> +               GPIOD_TEST_CONSUMER, GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       ev_thread = gpiod_test_start_event_thread(0, 7, 100);
> +
> +       ret = gpiod_line_event_wait(line, &ts);
> +       g_assert_cmpint(ret, ==, 1);
> +
> +       ret = gpiod_line_event_read(line, &ev);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE);
> +
> +       ret = gpiod_line_event_wait(line, &ts);
> +       g_assert_cmpint(ret, ==, 1);
> +
> +       ret = gpiod_line_event_read(line, &ev);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_FALLING_EDGE);
> +}
> +
> +GPIOD_TEST_CASE(both_edges_bias_pull_up, 0, { 8 })
> +{
> +       g_autoptr(GpiodTestEventThread) ev_thread = NULL;
> +       g_autoptr(gpiod_chip_struct) chip = NULL;
> +       struct timespec ts = { 1, 0 };
> +       struct gpiod_line_event ev;
> +       struct gpiod_line *line;
> +       gint ret;
> +
> +       chip = gpiod_chip_open(gpiod_test_chip_path(0));
> +       g_assert_nonnull(chip);
> +       gpiod_test_return_if_failed();
> +
> +       line = gpiod_chip_get_line(chip, 7);
> +       g_assert_nonnull(line);
> +       gpiod_test_return_if_failed();
> +
> +       ret = gpiod_line_request_both_edges_events_flags(line,
> +               GPIOD_TEST_CONSUMER, GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       ev_thread = gpiod_test_start_event_thread(0, 7, 100);
> +
> +       ret = gpiod_line_event_wait(line, &ts);
> +       g_assert_cmpint(ret, ==, 1);
> +
> +       ret = gpiod_line_event_read(line, &ev);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_FALLING_EDGE);
> +
> +       ret = gpiod_line_event_wait(line, &ts);
> +       g_assert_cmpint(ret, ==, 1);
> +
> +       ret = gpiod_line_event_read(line, &ev);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE);
> +}
> +
>  GPIOD_TEST_CASE(falling_edge_active_low, 0, { 8 })
>  {
>         g_autoptr(GpiodTestEventThread) ev_thread = NULL;
> diff --git a/tests/tests-line.c b/tests/tests-line.c
> index 205c622..4bf7c02 100644
> --- a/tests/tests-line.c
> +++ b/tests/tests-line.c
> @@ -502,6 +502,7 @@ GPIOD_TEST_CASE(misc_flags, 0, { 8 })
>         g_assert_false(gpiod_line_is_used(line));
>         g_assert_false(gpiod_line_is_open_drain(line));
>         g_assert_false(gpiod_line_is_open_source(line));
> +       g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
>
>         config.request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT;
>         config.consumer = GPIOD_TEST_CONSUMER;
> @@ -513,6 +514,7 @@ GPIOD_TEST_CASE(misc_flags, 0, { 8 })
>         g_assert_true(gpiod_line_is_used(line));
>         g_assert_true(gpiod_line_is_open_drain(line));
>         g_assert_false(gpiod_line_is_open_source(line));
> +       g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
>         g_assert_cmpint(gpiod_line_direction(line), ==,
>                         GPIOD_LINE_DIRECTION_OUTPUT);
>
> @@ -526,8 +528,11 @@ GPIOD_TEST_CASE(misc_flags, 0, { 8 })
>         g_assert_true(gpiod_line_is_used(line));
>         g_assert_false(gpiod_line_is_open_drain(line));
>         g_assert_true(gpiod_line_is_open_source(line));
> +       g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
>         g_assert_cmpint(gpiod_line_direction(line), ==,
>                         GPIOD_LINE_DIRECTION_OUTPUT);
> +
> +       gpiod_line_release(line);
>  }
>
>  GPIOD_TEST_CASE(misc_flags_work_together, 0, { 8 })
> @@ -561,6 +566,7 @@ GPIOD_TEST_CASE(misc_flags_work_together, 0, { 8 })
>         g_assert_true(gpiod_line_is_used(line));
>         g_assert_true(gpiod_line_is_open_drain(line));
>         g_assert_false(gpiod_line_is_open_source(line));
> +       g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
>         g_assert_cmpint(gpiod_line_active_state(line), ==,
>                         GPIOD_LINE_ACTIVE_STATE_LOW);
>         g_assert_cmpint(gpiod_line_direction(line), ==,
> @@ -577,8 +583,59 @@ GPIOD_TEST_CASE(misc_flags_work_together, 0, { 8 })
>         g_assert_true(gpiod_line_is_used(line));
>         g_assert_false(gpiod_line_is_open_drain(line));
>         g_assert_true(gpiod_line_is_open_source(line));
> +       g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_AS_IS);
>         g_assert_cmpint(gpiod_line_active_state(line), ==,
>                         GPIOD_LINE_ACTIVE_STATE_LOW);
> +
> +       gpiod_line_release(line);
> +
> +       /*
> +        * Verify that pull-up/down flags work together
> +        * with active_low.
> +        */
> +
> +       config.request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT;
> +       config.flags = GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN |
> +                      GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
> +
> +       ret = gpiod_line_request(line, &config, 0);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       g_assert_true(gpiod_line_is_used(line));
> +       g_assert_false(gpiod_line_is_open_drain(line));
> +       g_assert_false(gpiod_line_is_open_source(line));
> +       g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_PULL_DOWN);
> +       g_assert_cmpint(gpiod_line_active_state(line), ==,
> +                       GPIOD_LINE_ACTIVE_STATE_LOW);
> +       g_assert_cmpint(gpiod_line_direction(line), ==,
> +                       GPIOD_LINE_DIRECTION_INPUT);
> +
> +       ret = gpiod_line_get_value(line);
> +       g_assert_cmpint(ret, ==, 1);
> +       g_assert_cmpint(errno, ==, 0);
> +
> +       gpiod_line_release(line);
> +
> +       config.flags = GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP |
> +                      GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
> +
> +       ret = gpiod_line_request(line, &config, 0);
> +       g_assert_cmpint(ret, ==, 0);
> +
> +       g_assert_true(gpiod_line_is_used(line));
> +       g_assert_false(gpiod_line_is_open_drain(line));
> +       g_assert_false(gpiod_line_is_open_source(line));
> +       g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_PULL_UP);
> +       g_assert_cmpint(gpiod_line_active_state(line), ==,
> +                       GPIOD_LINE_ACTIVE_STATE_LOW);
> +       g_assert_cmpint(gpiod_line_direction(line), ==,
> +                       GPIOD_LINE_DIRECTION_INPUT);
> +
> +       ret = gpiod_line_get_value(line);
> +       g_assert_cmpint(ret, ==, 0);
> +       g_assert_cmpint(errno, ==, 0);
> +
> +       gpiod_line_release(line);
>  }
>
>  GPIOD_TEST_CASE(open_source_open_drain_input_mode, 0, { 8 })
> @@ -627,6 +684,47 @@ GPIOD_TEST_CASE(open_source_open_drain_simultaneously, 0, { 8 })
>         g_assert_cmpint(errno, ==, EINVAL);
>  }
>
> +GPIOD_TEST_CASE(multiple_bias_flags, 0, { 8 })
> +{
> +       g_autoptr(gpiod_chip_struct) chip = NULL;
> +       struct gpiod_line *line;
> +       gint ret;
> +
> +       chip = gpiod_chip_open(gpiod_test_chip_path(0));
> +       g_assert_nonnull(chip);
> +       gpiod_test_return_if_failed();
> +
> +       line = gpiod_chip_get_line(chip, 2);
> +       g_assert_nonnull(line);
> +       gpiod_test_return_if_failed();
> +
> +       ret = gpiod_line_request_input_flags(line, GPIOD_TEST_CONSUMER,
> +                                       GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE |
> +                                       GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN);
> +       g_assert_cmpint(ret, ==, -1);
> +       g_assert_cmpint(errno, ==, EINVAL);
> +
> +       ret = gpiod_line_request_input_flags(line, GPIOD_TEST_CONSUMER,
> +                                       GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE |
> +                                       GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
> +       g_assert_cmpint(ret, ==, -1);
> +       g_assert_cmpint(errno, ==, EINVAL);
> +
> +       ret = gpiod_line_request_input_flags(line, GPIOD_TEST_CONSUMER,
> +                                       GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN |
> +                                       GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
> +       g_assert_cmpint(ret, ==, -1);
> +       g_assert_cmpint(errno, ==, EINVAL);
> +
> +       ret = gpiod_line_request_input_flags(line, GPIOD_TEST_CONSUMER,
> +                                       GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE |
> +                                       GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN |
> +                                       GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
> +       g_assert_cmpint(ret, ==, -1);
> +       g_assert_cmpint(errno, ==, EINVAL);
> +}
> +
> +
>  /* Verify that the reference counting of the line fd handle works correctly. */
>  GPIOD_TEST_CASE(release_one_use_another, 0, { 8 })
>  {
> --
> 2.24.0
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [libgpiod][PATCH v3 03/14] bindings: cxx: add support for bias flags
  2019-11-25 14:31 ` [libgpiod][PATCH v3 03/14] bindings: cxx: add support " Kent Gibson
@ 2019-11-28 10:29   ` Bartosz Golaszewski
  0 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2019-11-28 10:29 UTC (permalink / raw)
  To: Kent Gibson; +Cc: linux-gpio

pon., 25 lis 2019 o 15:32 Kent Gibson <warthog618@gmail.com> napisał(a):
>
> Add support for bias flags in line requests and returning the line bias
> setting via a bias accessor.
>
> Based on initial work by Drew Fustini <drew@pdp7.com>.
>
> Signed-off-by: Kent Gibson <warthog618@gmail.com>
> ---
>  bindings/cxx/gpiod.hpp     | 26 ++++++++++++++++++++++++++
>  bindings/cxx/line.cpp      | 19 +++++++++++++++++++
>  bindings/cxx/line_bulk.cpp |  6 ++++++
>  3 files changed, 51 insertions(+)
>
> diff --git a/bindings/cxx/gpiod.hpp b/bindings/cxx/gpiod.hpp
> index b5a9401..2b1a6ab 100644
> --- a/bindings/cxx/gpiod.hpp
> +++ b/bindings/cxx/gpiod.hpp
> @@ -233,6 +233,12 @@ struct line_request
>         /**< The line is an open-source port. */
>         GPIOD_API static const ::std::bitset<32> FLAG_OPEN_DRAIN;
>         /**< The line is an open-drain port. */
> +       GPIOD_API static const ::std::bitset<32> FLAG_BIAS_DISABLE;
> +       /**< The line has neither pull-up nor pull-down resistor enabled */
> +       GPIOD_API static const ::std::bitset<32> FLAG_BIAS_PULL_DOWN;
> +       /**< The line has a configurable pull-down resistor enabled */
> +       GPIOD_API static const ::std::bitset<32> FLAG_BIAS_PULL_UP;
> +       /**< The line has a configurable pull-up resistor enabled */
>
>         ::std::string consumer;
>         /**< Consumer name to pass to the request. */
> @@ -320,6 +326,12 @@ public:
>          */
>         GPIOD_API int active_state(void) const;
>
> +       /**
> +        * @brief Get current bias of this line.
> +        * @return Current bias setting.
> +        */
> +       GPIOD_API int bias(void) const;
> +
>         /**
>          * @brief Check if this line is used by the kernel or other user space
>          *        process.
> @@ -456,6 +468,20 @@ public:
>                 /**< Line's active state is high. */
>         };
>
> +       /**
> +        * @brief Possible bias settings.
> +        */
> +       enum : int {
> +               BIAS_AS_IS = 1,
> +               /**< Line's bias state is unknown. */
> +               BIAS_DISABLE,
> +               /**< Line's internal bias is disabled. */
> +               BIAS_PULL_UP,
> +               /**< Line's internal pull-up bias is enabled. */
> +               BIAS_PULL_DOWN,
> +               /**< Line's internal pull-down bias is enabled. */
> +       };
> +
>  private:
>
>         line(::gpiod_line* line, const chip& owner);
> diff --git a/bindings/cxx/line.cpp b/bindings/cxx/line.cpp
> index df6eada..dd6bb6a 100644
> --- a/bindings/cxx/line.cpp
> +++ b/bindings/cxx/line.cpp
> @@ -67,6 +67,25 @@ int line::active_state(void) const
>         return active == GPIOD_LINE_ACTIVE_STATE_HIGH ? ACTIVE_HIGH : ACTIVE_LOW;
>  }
>
> +int line::bias(void) const
> +{
> +       this->throw_if_null();
> +
> +       int bias = ::gpiod_line_bias(this->_m_line);
> +
> +       switch (bias) {
> +       case GPIOD_LINE_BIAS_PULL_UP:
> +               return BIAS_PULL_UP;
> +       case GPIOD_LINE_BIAS_PULL_DOWN:
> +               return BIAS_PULL_DOWN;
> +       case GPIOD_LINE_BIAS_DISABLE:
> +               return BIAS_DISABLE;
> +       case GPIOD_LINE_BIAS_AS_IS:
> +       default:
> +               return BIAS_AS_IS;
> +       }

I think that for consistency with other mappings, this can be made
shorter by using std::map - please see line_bulk.cpp.

Bart


> +}
> +
>  bool line::is_used(void) const
>  {
>         this->throw_if_null();
> diff --git a/bindings/cxx/line_bulk.cpp b/bindings/cxx/line_bulk.cpp
> index c708c8b..5f1cac4 100644
> --- a/bindings/cxx/line_bulk.cpp
> +++ b/bindings/cxx/line_bulk.cpp
> @@ -14,6 +14,9 @@ namespace gpiod {
>  const ::std::bitset<32> line_request::FLAG_ACTIVE_LOW(GPIOD_BIT(0));
>  const ::std::bitset<32> line_request::FLAG_OPEN_SOURCE(GPIOD_BIT(1));
>  const ::std::bitset<32> line_request::FLAG_OPEN_DRAIN(GPIOD_BIT(2));
> +const ::std::bitset<32> line_request::FLAG_BIAS_DISABLE(GPIOD_BIT(3));
> +const ::std::bitset<32> line_request::FLAG_BIAS_PULL_DOWN(GPIOD_BIT(4));
> +const ::std::bitset<32> line_request::FLAG_BIAS_PULL_UP(GPIOD_BIT(5));
>
>  namespace {
>
> @@ -38,6 +41,9 @@ const ::std::map<::std::bitset<32>, int, bitset_cmp> reqflag_mapping = {
>         { line_request::FLAG_ACTIVE_LOW,        GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW, },
>         { line_request::FLAG_OPEN_DRAIN,        GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN, },
>         { line_request::FLAG_OPEN_SOURCE,       GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE, },
> +       { line_request::FLAG_BIAS_DISABLE,      GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE, },
> +       { line_request::FLAG_BIAS_PULL_DOWN,    GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN, },
> +       { line_request::FLAG_BIAS_PULL_UP,      GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP, },
>  };
>
>  } /* namespace */
> --
> 2.24.0
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [libgpiod][PATCH v3 07/14] core: add support for SET_CONFIG
  2019-11-25 14:31 ` [libgpiod][PATCH v3 07/14] core: add support for SET_CONFIG Kent Gibson
@ 2019-11-28 10:29   ` Bartosz Golaszewski
  0 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2019-11-28 10:29 UTC (permalink / raw)
  To: Kent Gibson; +Cc: linux-gpio

pon., 25 lis 2019 o 15:32 Kent Gibson <warthog618@gmail.com> napisał(a):
>
> Extend the libgpiod API to support the setting line configuration using the
> GPIO GPIOHANDLE_SET_CONFIG_IOCTL uAPI ioctl.
>
> The core change is the addition of gpiod_line_set_config, which provides a
> low level wrapper around the ioctl.
>
> Additionally, higher level helper functions, gpiod_line_set_flags,
> gpiod_line_set_direction_input, and gpiod_line_set_direction_output provide
> slightly simplified APIs for common use cases.
>
> Bulk forms of all functions are also provided.
>
> Signed-off-by: Kent Gibson <warthog618@gmail.com>
> ---
>  include/gpiod.h | 123 +++++++++++++++++++++++++++++++
>  lib/core.c      | 187 +++++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 307 insertions(+), 3 deletions(-)
>
> diff --git a/include/gpiod.h b/include/gpiod.h
> index 86c3ea9..185e2f4 100644
> --- a/include/gpiod.h
> +++ b/include/gpiod.h
> @@ -1246,6 +1246,15 @@ void gpiod_line_release_bulk(struct gpiod_line_bulk *bulk) GPIOD_API;
>   */
>  bool gpiod_line_is_requested(struct gpiod_line *line) GPIOD_API;
>
> +/**
> + * @brief Check if the calling user has ownership of this line for values,
> + * not events.

For consistency: please align this with the beginning of the
description above...

> + * @param line GPIO line object.
> + * @return True if given line was requested for reading/setting values,
> + *         false otherwise.
> + */
> +bool gpiod_line_is_requested_values(struct gpiod_line *line) GPIOD_API;

... but also: why do we need to export this anyway?

> +
>  /**
>   * @brief Check if the calling user has neither requested ownership of this
>   *        line nor configured any event notifications.
> @@ -1296,6 +1305,7 @@ int gpiod_line_set_value(struct gpiod_line *line, int value) GPIOD_API;
>   * @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.

This looks like part of a different commit.

>   * @return 0 is the operation succeeds. In case of an error this routine
>   *         returns -1 and sets the last error number.
>   *
> @@ -1305,6 +1315,119 @@ int gpiod_line_set_value(struct gpiod_line *line, int value) GPIOD_API;
>  int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk,
>                               const int *values) GPIOD_API;
>
> +/**
> + * @}
> + *
> + * @defgroup __line_config__ Setting line configuration
> + * @{
> + */
> +
> +/**
> + * @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) GPIOD_API;
> +
> +/**
> + * @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.
> + */
> +int gpiod_line_set_config_bulk(struct gpiod_line_bulk *bulk,
> +                              int direction, int flags,
> +                              const int *values) GPIOD_API;
> +
> +
> +/**
> + * @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.
> + */
> +int gpiod_line_set_flags(struct gpiod_line *line, int flags) GPIOD_API;
> +
> +/**
> + * @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.
> + */
> +int gpiod_line_set_flags_bulk(struct gpiod_line_bulk *bulk,
> +                             int flags) GPIOD_API;
> +
> +/**
> + * @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.
> + */
> +int gpiod_line_set_direction_input(struct gpiod_line *line) GPIOD_API;
> +
> +/**
> + * @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.
> + */
> +int gpiod_line_set_direction_input_bulk(struct gpiod_line_bulk *bulk
> +                                       ) GPIOD_API;
> +
> +/**
> + * @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.
> + */
> +int gpiod_line_set_direction_output(struct gpiod_line *line,
> +                                   int value) GPIOD_API;
> +
> +/**
> + * @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.
> + */
> +int gpiod_line_set_direction_output_bulk(struct gpiod_line_bulk *bulk,
> +                                        const int *values) GPIOD_API;
> +
>  /**
>   * @}
>   *
> diff --git a/lib/core.c b/lib/core.c
> index 0465de9..71bb4fb 100644
> --- a/lib/core.c
> +++ b/lib/core.c
> @@ -34,10 +34,26 @@ struct line_fd_handle {
>
>  struct gpiod_line {
>         unsigned int offset;
> +
> +       /* The GPIOD_LINE_DIRECTION */
>         int direction;
> +
> +       /* The GPIOD_LINE_ACTIVE_STATE */
>         int active_state;
> +
> +       /* 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;

Please include the mention on documenting this in the commit message.
Otherwise it looks like it should be part of a different commit.

>
>         struct gpiod_chip *chip;
> @@ -445,6 +461,20 @@ static bool line_bulk_all_requested(struct gpiod_line_bulk *bulk)
>         return true;
>  }
>
> +static bool line_bulk_all_requested_values(struct gpiod_line_bulk *bulk)
> +{
> +       struct gpiod_line *line, **lineptr;
> +
> +       gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
> +               if (!gpiod_line_is_requested_values(line)) {
> +                       errno = EPERM;
> +                       return false;
> +               }
> +       }
> +
> +       return true;
> +}
> +
>  static bool line_bulk_all_free(struct gpiod_line_bulk *bulk)
>  {
>         struct gpiod_line *line, **lineptr;
> @@ -459,6 +489,27 @@ static bool line_bulk_all_free(struct gpiod_line_bulk *bulk)
>         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;
> +}

Same here: please be a bit more elaborate in the commit message. Say
something like: "while at it: restructure the code by doing this and
that".

> +
> +static __u32 line_request_direction_to_gpio_handleflag(int direction)
> +{
> +       if (direction == GPIOD_LINE_REQUEST_DIRECTION_INPUT)
> +               return GPIOHANDLE_REQUEST_INPUT;
> +       if (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
> +               return GPIOHANDLE_REQUEST_OUTPUT;
> +
> +       return 0;
> +}
> +
>  static __u32 line_request_flag_to_gpio_handleflag(int flags)
>  {
>         int hflags = 0;
> @@ -483,7 +534,7 @@ static int line_request_values(struct gpiod_line_bulk *bulk,
>                                const struct gpiod_line_request_config *config,
>                                const int *default_vals)
>  {
> -       struct gpiod_line *line, **lineptr;
> +       struct gpiod_line *line;
>         struct line_fd_handle *line_fd;
>         struct gpiohandle_request req;
>         unsigned int i;
> @@ -512,7 +563,6 @@ static int line_request_values(struct gpiod_line_bulk *bulk,
>         else if (config->request_type == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
>                 req.flags |= GPIOHANDLE_REQUEST_OUTPUT;
>
> -

Fixing stray newlines etc should be in separate commits too IMO but
let's let is slip here, this is already a big patch anyway.

>         gpiod_line_bulk_foreach_line_off(bulk, line, i) {
>                 req.lineoffsets[i] = gpiod_line_offset(line);
>                 if (config->request_type ==
> @@ -536,8 +586,12 @@ static int line_request_values(struct gpiod_line_bulk *bulk,
>         if (!line_fd)
>                 return -1;
>
> -       gpiod_line_bulk_foreach_line(bulk, line, lineptr) {
> +       gpiod_line_bulk_foreach_line_off(bulk, line, i) {
>                 line->state = LINE_REQUESTED_VALUES;
> +               line->req_flags = config->flags;
> +               if (config->request_type ==
> +                       GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)

Add another tab here because otherwise there's no indentation
difference between this line and the line below.

> +                       line->output_value = req.default_values[i];
>                 line_set_fd(line, line_fd);
>
>                 rv = gpiod_line_update(line);
> @@ -583,6 +637,7 @@ static int line_request_event_single(struct gpiod_line *line,
>                 return -1;
>
>         line->state = LINE_REQUESTED_EVENTS;
> +       line->req_flags = config->flags;
>         line_set_fd(line, line_fd);
>
>         rv = gpiod_line_update(line);
> @@ -686,6 +741,11 @@ bool gpiod_line_is_requested(struct gpiod_line *line)
>                 line->state == LINE_REQUESTED_EVENTS);
>  }
>
> +bool gpiod_line_is_requested_values(struct gpiod_line *line)
> +{
> +       return (line->state == LINE_REQUESTED_VALUES);
> +}
> +
>  bool gpiod_line_is_free(struct gpiod_line *line)
>  {
>         return line->state == LINE_FREE;
> @@ -766,9 +826,130 @@ int gpiod_line_set_value_bulk(struct gpiod_line_bulk *bulk, const int *values)
>         if (rv < 0)
>                 return -1;
>
> +       gpiod_line_bulk_foreach_line_off(bulk, line, i)
> +               line->output_value = data.values[i];
> +
>         return 0;
>  }
>
> +int gpiod_line_set_config(struct gpiod_line *line, int direction,
> +                         int flags, int value)
> +{
> +       struct gpiod_line_bulk bulk;
> +
> +       gpiod_line_bulk_init(&bulk);
> +       gpiod_line_bulk_add(&bulk, line);
> +
> +       return gpiod_line_set_config_bulk(&bulk, direction, flags, &value);
> +}
> +
> +int gpiod_line_set_config_bulk(struct gpiod_line_bulk *bulk,
> +                              int direction, int flags,
> +                              const int *values)
> +{
> +       struct gpiohandle_config hcfg;
> +       struct gpiod_line *line;
> +       unsigned int i;
> +       int rv, fd;
> +
> +       if (!line_bulk_same_chip(bulk) ||
> +           !line_bulk_all_requested_values(bulk))
> +               return -1;
> +
> +       if (!line_request_direction_is_valid(direction))
> +               return -1;
> +
> +       memset(&hcfg, 0, sizeof(hcfg));
> +
> +       hcfg.flags = line_request_flag_to_gpio_handleflag(flags);
> +       hcfg.flags |= line_request_direction_to_gpio_handleflag(direction);
> +       if (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT && values) {
> +               for (i = 0; i < gpiod_line_bulk_num_lines(bulk); i++)
> +                       hcfg.default_values[i] = (uint8_t)!!values[i];
> +       }
> +
> +       line = gpiod_line_bulk_get_line(bulk, 0);
> +       fd = line_get_fd(line);
> +
> +       rv = ioctl(fd, GPIOHANDLE_SET_CONFIG_IOCTL, &hcfg);
> +       if (rv < 0)
> +               return -1;
> +
> +       gpiod_line_bulk_foreach_line_off(bulk, line, i) {
> +               line->req_flags = flags;
> +               if (direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT)
> +                       line->output_value = hcfg.default_values[i];

Please add a newline here.

> +               rv = gpiod_line_update(line);
> +               if (rv < 0)
> +                       return rv;
> +       }
> +       return 0;
> +}
> +
> +int gpiod_line_set_flags(struct gpiod_line *line, int flags)
> +{
> +       struct gpiod_line_bulk bulk;
> +
> +       gpiod_line_bulk_init(&bulk);
> +       gpiod_line_bulk_add(&bulk, line);
> +
> +       return gpiod_line_set_flags_bulk(&bulk, flags);
> +}
> +
> +int gpiod_line_set_flags_bulk(struct gpiod_line_bulk *bulk, int flags)
> +{
> +       struct gpiod_line *line;
> +       int values[GPIOD_LINE_BULK_MAX_LINES];
> +       unsigned int i;
> +       int direction;
> +
> +       line = gpiod_line_bulk_get_line(bulk, 0);
> +       if (line->direction == GPIOD_LINE_DIRECTION_OUTPUT) {
> +               gpiod_line_bulk_foreach_line_off(bulk, line, i) {
> +                       values[i] = line->output_value;
> +               }

No need for brackets here.



> +               direction = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT;
> +       } else {
> +               direction = GPIOD_LINE_REQUEST_DIRECTION_INPUT;
> +       }
> +
> +       return gpiod_line_set_config_bulk(bulk, direction,
> +                                         flags, values);
> +}
> +
> +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);
> +}
> +
> +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);
> +}
> +
> +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);
> +}
> +
> +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);
> +}
> +
>  int gpiod_line_event_wait(struct gpiod_line *line,
>                           const struct timespec *timeout)
>  {
> --
> 2.24.0
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [libgpiod][PATCH v3 09/14] bindings: cxx: add support for SET_CONFIG
  2019-11-25 14:31 ` [libgpiod][PATCH v3 09/14] bindings: cxx: add support " Kent Gibson
@ 2019-11-28 10:29   ` Bartosz Golaszewski
  0 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2019-11-28 10:29 UTC (permalink / raw)
  To: Kent Gibson; +Cc: linux-gpio

pon., 25 lis 2019 o 15:33 Kent Gibson <warthog618@gmail.com> napisał(a):
>
> Add methods to support setting line configuration.
>
> Signed-off-by: Kent Gibson <warthog618@gmail.com>
> ---
>  bindings/cxx/gpiod.hpp     | 55 +++++++++++++++++++++++++
>  bindings/cxx/line.cpp      | 37 +++++++++++++++++
>  bindings/cxx/line_bulk.cpp | 83 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 175 insertions(+)
>
> diff --git a/bindings/cxx/gpiod.hpp b/bindings/cxx/gpiod.hpp
> index 2b1a6ab..dcae431 100644
> --- a/bindings/cxx/gpiod.hpp
> +++ b/bindings/cxx/gpiod.hpp
> @@ -381,6 +381,32 @@ public:
>          */
>         GPIOD_API void set_value(int val) const;
>
> +       /**
> +        * @brief Set configuration of this line.
> +        * @param direction New direction.
> +        * @param flags Replacement flags.
> +        * @param value New value (0 or 1) - only matters for OUTPUT direction.
> +        */
> +       GPIOD_API void set_config(int direction, ::std::bitset<32> flags,
> +                       int value = 0) const;

Please align this with the opening bracket. Same elsewhere.



> +
> +       /**
> +        * @brief Set configuration flags of this line.
> +        * @param flags Replacement flags.
> +        */
> +       GPIOD_API void set_flags(::std::bitset<32> flags) const;
> +
> +       /**
> +        * @brief Change the direction this line to input.
> +        */
> +       GPIOD_API void set_direction_input() const;
> +
> +       /**
> +        * @brief Change the direction this lines to output.
> +        * @param value New value (0 or 1).
> +        */
> +       GPIOD_API void set_direction_output(int value = 0) const;
> +
>         /**
>          * @brief Wait for an event on this line.
>          * @param timeout Time to wait before returning if no event occurred.
> @@ -648,6 +674,35 @@ public:
>          */
>         GPIOD_API void set_values(const ::std::vector<int>& values) const;
>
> +       /**
> +        * @brief Set configuration of all lines held by this object.
> +        * @param direction New direction.
> +        * @param flags Replacement flags.
> +        * @param values Vector of values to set. Must be the same size as the
> +        *        number of lines held by this line_bulk.
> +        *        Only relevant for output direction requests.
> +        */
> +       GPIOD_API void set_config(int direction, ::std::bitset<32> flags,
> +                       const ::std::vector<int> values = std::vector<int>()) const;
> +
> +       /**
> +        * @brief Set configuration flags of all lines held by this object.
> +        * @param flags Replacement flags.
> +        */
> +       GPIOD_API void set_flags(::std::bitset<32> flags) const;
> +
> +       /**
> +        * @brief Change the direction all lines held by this object to input.
> +        */
> +       GPIOD_API void set_direction_input() const;
> +
> +       /**
> +        * @brief Change the direction all lines held by this object to output.
> +        * @param values Vector of values to set. Must be the same size as the
> +        *        number of lines held by this line_bulk.
> +        */
> +       GPIOD_API void set_direction_output(const ::std::vector<int>& values) const;
> +
>         /**
>          * @brief Poll the set of lines for line events.
>          * @param timeout Number of nanoseconds to wait before returning an
> diff --git a/bindings/cxx/line.cpp b/bindings/cxx/line.cpp
> index dd6bb6a..a688b5d 100644
> --- a/bindings/cxx/line.cpp
> +++ b/bindings/cxx/line.cpp
> @@ -158,6 +158,43 @@ void line::set_value(int val) const
>         bulk.set_values({ val });
>  }
>
> +void line::set_config(int direction, ::std::bitset<32> flags,
> +                       int value) const
> +{
> +       this->throw_if_null();
> +
> +       line_bulk bulk({ *this });
> +
> +       bulk.set_config(direction, flags, { value });
> +}
> +
> +void line::set_flags(::std::bitset<32> flags) const
> +{
> +       this->throw_if_null();
> +
> +       line_bulk bulk({ *this });
> +
> +       bulk.set_flags(flags);
> +}
> +
> +void line::set_direction_input() const
> +{
> +       this->throw_if_null();
> +
> +       line_bulk bulk({ *this });
> +
> +       bulk.set_direction_input();
> +}
> +
> +void line::set_direction_output(int value) const
> +{
> +       this->throw_if_null();
> +
> +       line_bulk bulk({ *this });
> +
> +       bulk.set_direction_output({ value });
> +}
> +
>  bool line::event_wait(const ::std::chrono::nanoseconds& timeout) const
>  {
>         this->throw_if_null();
> diff --git a/bindings/cxx/line_bulk.cpp b/bindings/cxx/line_bulk.cpp
> index 5f1cac4..b8f5eb7 100644
> --- a/bindings/cxx/line_bulk.cpp
> +++ b/bindings/cxx/line_bulk.cpp
> @@ -176,6 +176,89 @@ void line_bulk::set_values(const ::std::vector<int>& values) const
>                                           "error setting GPIO line values");
>  }
>
> +void line_bulk::set_config(int direction, ::std::bitset<32> flags,
> +                          const ::std::vector<int> values) const
> +{
> +       this->throw_if_empty();
> +
> +       if (!values.empty() && this->_m_bulk.size() != values.size())
> +               throw ::std::invalid_argument("the number of default values must correspond with the number of lines");
> +
> +       ::gpiod_line_bulk bulk;
> +       int rv, gflags;
> +
> +       gflags = 0;
> +
> +       for (auto& it: reqflag_mapping) {
> +               if ((it.first & flags).to_ulong())
> +                       gflags |= it.second;
> +       }
> +
> +       this->to_line_bulk(::std::addressof(bulk));
> +
> +       rv = ::gpiod_line_set_config_bulk(::std::addressof(bulk), direction,
> +                                         gflags, values.data());
> +       if (rv)
> +               throw ::std::system_error(errno, ::std::system_category(),
> +                                         "error setting GPIO line config");
> +}
> +
> +void line_bulk::set_flags(::std::bitset<32> flags) const
> +{
> +       this->throw_if_empty();
> +
> +       ::gpiod_line_bulk bulk;
> +       int rv, gflags;
> +
> +       this->to_line_bulk(::std::addressof(bulk));
> +
> +       gflags = 0;
> +
> +       for (auto& it: reqflag_mapping) {
> +               if ((it.first & flags).to_ulong())
> +                       gflags |= it.second;
> +       }
> +
> +       rv = ::gpiod_line_set_flags_bulk(::std::addressof(bulk), gflags);
> +       if (rv)
> +               throw ::std::system_error(errno, ::std::system_category(),
> +                                         "error setting GPIO line flags");
> +}
> +
> +void line_bulk::set_direction_input() const
> +{
> +       this->throw_if_empty();
> +
> +       ::gpiod_line_bulk bulk;
> +       int rv;
> +
> +       this->to_line_bulk(::std::addressof(bulk));
> +
> +       rv = ::gpiod_line_set_direction_input_bulk(::std::addressof(bulk));
> +       if (rv)
> +               throw ::std::system_error(errno, ::std::system_category(),
> +                       "error setting GPIO line direction to input");
> +}
> +
> +void line_bulk::set_direction_output(const ::std::vector<int>& values) const
> +{
> +       this->throw_if_empty();
> +
> +       if (values.size() != this->_m_bulk.size())
> +               throw ::std::invalid_argument("the size of values array must correspond with the number of lines");
> +
> +       ::gpiod_line_bulk bulk;
> +       int rv;
> +
> +       this->to_line_bulk(::std::addressof(bulk));
> +
> +       rv = ::gpiod_line_set_direction_output_bulk(::std::addressof(bulk),
> +                                                   values.data());
> +       if (rv)
> +               throw ::std::system_error(errno, ::std::system_category(),
> +                       "error setting GPIO line direction to output");
> +}
> +
>  line_bulk line_bulk::event_wait(const ::std::chrono::nanoseconds& timeout) const
>  {
>         this->throw_if_empty();
> --
> 2.24.0
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [libgpiod][PATCH v3 11/14] bindings: python: add support for SET_CONFIG
  2019-11-25 14:31 ` [libgpiod][PATCH v3 11/14] bindings: python: add support for SET_CONFIG Kent Gibson
@ 2019-11-28 10:29   ` Bartosz Golaszewski
  0 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2019-11-28 10:29 UTC (permalink / raw)
  To: Kent Gibson; +Cc: linux-gpio

pon., 25 lis 2019 o 15:33 Kent Gibson <warthog618@gmail.com> napisał(a):
>
> Add methods to support setting line configuration.
>
> Signed-off-by: Kent Gibson <warthog618@gmail.com>
> ---
>  bindings/python/gpiodmodule.c | 381 +++++++++++++++++++++++++++++++++-
>  1 file changed, 379 insertions(+), 2 deletions(-)
>
> diff --git a/bindings/python/gpiodmodule.c b/bindings/python/gpiodmodule.c
> index 4723771..4f5e117 100644
> --- a/bindings/python/gpiodmodule.c
> +++ b/bindings/python/gpiodmodule.c
> @@ -585,14 +585,149 @@ static PyObject *gpiod_Line_set_value(gpiod_LineObject *self, PyObject *args)
>         if (!bulk_obj)
>                 return NULL;
>
> -       vals = Py_BuildValue("((O))", val);
> +       vals = Py_BuildValue("(O)", val);

Please don't try to sneak in changes like this. If you think this
should be changed, make it a separate commit with proper explanation.
I vaguely remember using this because previously I didn't use
PyObject_CallMethod() but called the set_values() function directly
and it was easier to package it right away. Anyway - is it broken? Do
we gain something from changing it? If so, let's have a separate patch
for this.

>         if (!vals) {
>                 Py_DECREF(bulk_obj);
>                 return NULL;
>         }
>
>         ret = PyObject_CallMethod((PyObject *)bulk_obj,
> -                                 "set_values", "O", vals);
> +                                 "set_values", "(O)", vals);
> +       Py_DECREF(bulk_obj);
> +       Py_DECREF(vals);
> +
> +       return ret;
> +}
> +
> +PyDoc_STRVAR(gpiod_Line_set_config_doc,
> +"set_config(direction,flags,value) -> None\n"
> +"\n"
> +"Set the configuration of this GPIO line.\n"
> +"\n"
> +"  direction\n"
> +"    New direction (integer)\n"
> +"  flags\n"
> +"    New flags (integer)\n"
> +"  value\n"
> +"    New value (integer)");
> +
> +static PyObject *gpiod_Line_set_config(gpiod_LineObject *self, PyObject *args)
> +{
> +       gpiod_LineBulkObject *bulk_obj;
> +       PyObject *dirn, *flags, *val, *vals, *ret;
> +       int rv;
> +
> +       val = NULL;
> +       rv = PyArg_ParseTuple(args, "OO|O", &dirn, &flags, &val);
> +       if (!rv)
> +               return NULL;
> +
> +       bulk_obj = gpiod_LineToLineBulk(self);
> +       if (!bulk_obj)
> +               return NULL;
> +
> +       if (val) {
> +               vals = Py_BuildValue("(O)", val);
> +               if (!vals) {
> +                       Py_DECREF(bulk_obj);
> +                       return NULL;
> +               }
> +               ret = PyObject_CallMethod((PyObject *)bulk_obj,
> +                               "set_config", "OO(O)", dirn, flags, vals);
> +               Py_DECREF(vals);
> +       } else
> +               ret = PyObject_CallMethod((PyObject *)bulk_obj,
> +                               "set_config", "OO", dirn, flags);

Please use brackets here even if it's a single line after you used it
in the first branch. Same elsewhere if needed.

> +
> +       Py_DECREF(bulk_obj);
> +
> +       return ret;
> +}
> +
> +PyDoc_STRVAR(gpiod_Line_set_flags_doc,
> +"set_flags(flags) -> None\n"
> +"\n"
> +"Set the flags of this GPIO line.\n"
> +"\n"
> +"  flags\n"
> +"    New flags (integer)");
> +
> +static PyObject *gpiod_Line_set_flags(gpiod_LineObject *self, PyObject *args)
> +{
> +       gpiod_LineBulkObject *bulk_obj;
> +       PyObject *ret;
> +
> +       bulk_obj = gpiod_LineToLineBulk(self);
> +       if (!bulk_obj)
> +               return NULL;
> +
> +       ret = PyObject_CallMethod((PyObject *)bulk_obj,
> +                                 "set_flags", "O", args);
> +       Py_DECREF(bulk_obj);
> +
> +       return ret;
> +}
> +
> +PyDoc_STRVAR(gpiod_Line_set_direction_input_doc,
> +"set_direction_input() -> None\n"
> +"\n"
> +"Set the direction of this GPIO line to input.\n");
> +
> +static PyObject *gpiod_Line_set_direction_input(gpiod_LineObject *self,
> +                                               PyObject *Py_UNUSED(ignored))
> +{
> +       gpiod_LineBulkObject *bulk_obj;
> +       PyObject *ret;
> +
> +       bulk_obj = gpiod_LineToLineBulk(self);
> +       if (!bulk_obj)
> +               return NULL;
> +
> +       ret = PyObject_CallMethod((PyObject *)bulk_obj,
> +                                 "set_direction_input", "");
> +       Py_DECREF(bulk_obj);
> +
> +       return ret;
> +}
> +
> +PyDoc_STRVAR(gpiod_Line_set_direction_output_doc,
> +"set_direction_output(value) -> None\n"
> +"\n"
> +"Set the direction of this GPIO line to output.\n"
> +"\n"
> +"  value\n"
> +"    New value (integer)");
> +
> +static PyObject *gpiod_Line_set_direction_output(gpiod_LineObject *self,
> +                                                PyObject *args)
> +{
> +       gpiod_LineBulkObject *bulk_obj;
> +       PyObject *val, *vals, *ret;
> +       int rv;
> +       const char *fmt;
> +
> +       val = NULL;
> +       rv = PyArg_ParseTuple(args, "|O", &val);
> +       if (!rv)
> +               return NULL;
> +
> +       if (val) {
> +               fmt = "(O)";
> +               vals = Py_BuildValue(fmt, val);
> +       } else {
> +               vals = Py_BuildValue("()");
> +               fmt = "O"; /* pass empty args to bulk */
> +       }
> +       if (!vals)
> +               return NULL;
> +
> +       bulk_obj = gpiod_LineToLineBulk(self);
> +       if (!bulk_obj)
> +               return NULL;
> +
> +       ret = PyObject_CallMethod((PyObject *)bulk_obj,
> +                                 "set_direction_output", fmt, vals);
> +
>         Py_DECREF(bulk_obj);
>         Py_DECREF(vals);
>
> @@ -838,6 +973,30 @@ static PyMethodDef gpiod_Line_methods[] = {
>                 .ml_flags = METH_VARARGS,
>                 .ml_doc = gpiod_Line_set_value_doc,
>         },
> +       {
> +               .ml_name = "set_config",
> +               .ml_meth = (PyCFunction)gpiod_Line_set_config,
> +               .ml_flags = METH_VARARGS,
> +               .ml_doc = gpiod_Line_set_config_doc,
> +       },
> +       {
> +               .ml_name = "set_flags",
> +               .ml_meth = (PyCFunction)gpiod_Line_set_flags,
> +               .ml_flags = METH_VARARGS,
> +               .ml_doc = gpiod_Line_set_flags_doc,
> +       },
> +       {
> +               .ml_name = "set_direction_input",
> +               .ml_meth = (PyCFunction)gpiod_Line_set_direction_input,
> +               .ml_flags = METH_NOARGS,
> +               .ml_doc = gpiod_Line_set_direction_input_doc,
> +       },
> +       {
> +               .ml_name = "set_direction_output",
> +               .ml_meth = (PyCFunction)gpiod_Line_set_direction_output,
> +               .ml_flags = METH_VARARGS,
> +               .ml_doc = gpiod_Line_set_direction_output_doc,
> +       },
>         {
>                 .ml_name = "release",
>                 .ml_meth = (PyCFunction)gpiod_Line_release,
> @@ -1283,6 +1442,200 @@ static PyObject *gpiod_LineBulk_set_values(gpiod_LineBulkObject *self,
>         Py_RETURN_NONE;
>  }
>
> +static int convert_values(PyObject *src, int *dst, Py_ssize_t n)

Coding convention and readability: this module is a bit different as I
tried to stay consistent with Python C code when naming symbols.
Please use the common 'gpiod_' prefix even for non-exported functions
and maybe name this routine something else as "convert_values" doesn't
really indicate concrete functionality. I'm still not sure what it
does.


> +{
> +       int val;
> +       Py_ssize_t num_vals, i;
> +       PyObject *iter, *next;
> +
> +       num_vals = PyObject_Size(src);
> +       if (num_vals != n) {
> +               PyErr_SetString(PyExc_TypeError,
> +                       "Number of values must correspond to the number of lines");
> +               return -1;
> +       }
> +       iter = PyObject_GetIter(src);
> +       if (!iter)
> +               return -1;
> +       for (i = 0;; i++) {
> +               next = PyIter_Next(iter);
> +               if (!next) {
> +                       Py_DECREF(iter);
> +                       break;
> +               }
> +               val = PyLong_AsLong(next);
> +               Py_DECREF(next);
> +               if (PyErr_Occurred()) {
> +                       Py_DECREF(iter);
> +                       return -1;
> +               }
> +               dst[i] = (int)val;
> +       }
> +       return 0;
> +}
> +
> +PyDoc_STRVAR(gpiod_LineBulk_set_config_doc,
> +"set_config(direction,flags,values) -> None\n"
> +"\n"
> +"Set the configuration of all the lines held by this LineBulk object.\n"
> +"\n"
> +"  direction\n"
> +"    New direction (integer)\n"
> +"  flags\n"
> +"    New flags (integer)\n"
> +"  values\n"
> +"    List of values (integers) to set when direction is output.\n"
> +"\n"
> +"The number of values in the list passed as argument must be the same as\n"
> +"the number of lines held by this gpiod.LineBulk object. The index of each\n"
> +"value corresponds to the index of each line in the object.\n");
> +
> +static PyObject *gpiod_LineBulk_set_config(gpiod_LineBulkObject *self,
> +                                          PyObject *args)
> +{
> +       int rv, vals[GPIOD_LINE_BULK_MAX_LINES];
> +       PyObject *val_list;
> +       struct gpiod_line_bulk bulk;
> +       const int *valp;
> +       int dirn, flags;
> +
> +       if (gpiod_LineBulkOwnerIsClosed(self))
> +               return NULL;
> +
> +       gpiod_LineBulkObjToCLineBulk(self, &bulk);
> +
> +       val_list = NULL;
> +       rv = PyArg_ParseTuple(args, "ii|(O)", &dirn, &flags, &val_list);
> +       if (!rv)
> +               return NULL;
> +
> +       if (val_list == NULL)
> +               valp = NULL;
> +       else {
> +               memset(vals, 0, sizeof(vals));
> +               rv = convert_values(val_list, vals, self->num_lines);
> +               if (rv)
> +                       return NULL;
> +               valp = vals;
> +       }
> +
> +       Py_BEGIN_ALLOW_THREADS;
> +       rv = gpiod_line_set_config_bulk(&bulk, dirn, flags, valp);
> +       Py_END_ALLOW_THREADS;
> +       if (rv)
> +               return PyErr_SetFromErrno(PyExc_OSError);
> +
> +       Py_RETURN_NONE;
> +}
> +
> +PyDoc_STRVAR(gpiod_LineBulk_set_flags_doc,
> +"set_flags(flags) -> None\n"
> +"\n"
> +"Set the flags of all the lines held by this LineBulk object.\n"
> +"\n"
> +"  flags\n"
> +"    New flags (integer)");
> +
> +static PyObject *gpiod_LineBulk_set_flags(gpiod_LineBulkObject *self,
> +                                         PyObject *args)
> +{
> +       int rv;
> +       struct gpiod_line_bulk bulk;
> +       int flags;
> +
> +       if (gpiod_LineBulkOwnerIsClosed(self))
> +               return NULL;
> +
> +       gpiod_LineBulkObjToCLineBulk(self, &bulk);
> +
> +       rv = PyArg_ParseTuple(args, "i", &flags);
> +       if (!rv)
> +               return NULL;
> +
> +       Py_BEGIN_ALLOW_THREADS;
> +       rv = gpiod_line_set_flags_bulk(&bulk, flags);
> +       Py_END_ALLOW_THREADS;
> +       if (rv)
> +               return PyErr_SetFromErrno(PyExc_OSError);
> +
> +       Py_RETURN_NONE;
> +}
> +
> +PyDoc_STRVAR(gpiod_LineBulk_set_direction_input_doc,
> +"set_direction_input() -> None\n"
> +"\n"
> +"Set the direction of all the lines held by this LineBulk object to input.\n");
> +
> +static PyObject *gpiod_LineBulk_set_direction_input(gpiod_LineBulkObject *self,
> +                                               PyObject *Py_UNUSED(ignored))
> +{
> +       struct gpiod_line_bulk bulk;
> +       int rv;
> +
> +       if (gpiod_LineBulkOwnerIsClosed(self))
> +               return NULL;
> +
> +       gpiod_LineBulkObjToCLineBulk(self, &bulk);
> +
> +       Py_BEGIN_ALLOW_THREADS;
> +       rv = gpiod_line_set_direction_input_bulk(&bulk);
> +       Py_END_ALLOW_THREADS;
> +       if (rv)
> +               return PyErr_SetFromErrno(PyExc_OSError);
> +
> +       Py_RETURN_NONE;
> +}
> +
> +PyDoc_STRVAR(gpiod_LineBulk_set_direction_output_doc,
> +"set_direction_output(value) -> None\n"
> +"\n"
> +"Set the direction of all the lines held by this LineBulk object to output.\n"
> +"\n"
> +"  values\n"
> +"    List of values (integers) to set when direction is output.\n"
> +"\n"
> +"The number of values in the list passed as argument must be the same as\n"
> +"the number of lines held by this gpiod.LineBulk object. The index of each\n"
> +"value corresponds to the index of each line in the object.\n");
> +
> +static PyObject *gpiod_LineBulk_set_direction_output(
> +                               gpiod_LineBulkObject *self,
> +                               PyObject *args)
> +{
> +       int rv, vals[GPIOD_LINE_BULK_MAX_LINES];
> +       PyObject *val_list;
> +       struct gpiod_line_bulk bulk;
> +       const int *valp;
> +
> +       if (gpiod_LineBulkOwnerIsClosed(self))
> +               return NULL;
> +
> +       gpiod_LineBulkObjToCLineBulk(self, &bulk);
> +
> +       val_list = NULL;
> +       rv = PyArg_ParseTuple(args, "|O", &val_list);
> +       if (!rv)
> +               return NULL;
> +
> +       if (val_list == NULL)
> +               valp = NULL;
> +       else {
> +               memset(vals, 0, sizeof(vals));
> +               rv = convert_values(val_list, vals, self->num_lines);
> +               if (rv)
> +                       return NULL;
> +               valp = vals;
> +       }
> +
> +       Py_BEGIN_ALLOW_THREADS;
> +       rv = gpiod_line_set_direction_output_bulk(&bulk, valp);
> +       Py_END_ALLOW_THREADS;
> +       if (rv)
> +               return PyErr_SetFromErrno(PyExc_OSError);
> +
> +       Py_RETURN_NONE;
> +}
> +
>  PyDoc_STRVAR(gpiod_LineBulk_release_doc,
>  "release() -> None\n"
>  "\n"
> @@ -1431,6 +1784,30 @@ static PyMethodDef gpiod_LineBulk_methods[] = {
>                 .ml_doc = gpiod_LineBulk_set_values_doc,
>                 .ml_flags = METH_VARARGS,
>         },
> +       {
> +               .ml_name = "set_config",
> +               .ml_meth = (PyCFunction)gpiod_LineBulk_set_config,
> +               .ml_flags = METH_VARARGS,
> +               .ml_doc = gpiod_LineBulk_set_config_doc,
> +       },
> +       {
> +               .ml_name = "set_flags",
> +               .ml_meth = (PyCFunction)gpiod_LineBulk_set_flags,
> +               .ml_flags = METH_VARARGS,
> +               .ml_doc = gpiod_LineBulk_set_flags_doc,
> +       },
> +       {
> +               .ml_name = "set_direction_input",
> +               .ml_meth = (PyCFunction)gpiod_LineBulk_set_direction_input,
> +               .ml_flags = METH_NOARGS,
> +               .ml_doc = gpiod_LineBulk_set_direction_input_doc,
> +       },
> +       {
> +               .ml_name = "set_direction_output",
> +               .ml_meth = (PyCFunction)gpiod_LineBulk_set_direction_output,
> +               .ml_flags = METH_VARARGS,
> +               .ml_doc = gpiod_LineBulk_set_direction_output_doc,
> +       },
>         {
>                 .ml_name = "release",
>                 .ml_meth = (PyCFunction)gpiod_LineBulk_release,
> --
> 2.24.0
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [libgpiod][PATCH v3 13/14] tools: add support for bias flags
  2019-11-25 14:31 ` [libgpiod][PATCH v3 13/14] tools: add support for bias flags Kent Gibson
@ 2019-11-28 10:29   ` Bartosz Golaszewski
  0 siblings, 0 replies; 21+ messages in thread
From: Bartosz Golaszewski @ 2019-11-28 10:29 UTC (permalink / raw)
  To: Kent Gibson; +Cc: linux-gpio

pon., 25 lis 2019 o 15:33 Kent Gibson <warthog618@gmail.com> napisał(a):
>
> Add support for bias flags to applicable tools - gpioget, gpioset, and
> gpiomon.
>
> Signed-off-by: Kent Gibson <warthog618@gmail.com>
> ---
>  tools/gpioget.c | 32 +++++++++++++++++++++++++----
>  tools/gpiomon.c | 36 +++++++++++++++++++++++++++------
>  tools/gpioset.c | 54 ++++++++++++++++++++++++++++++++++++++++++++-----
>  3 files changed, 107 insertions(+), 15 deletions(-)
>
> diff --git a/tools/gpioget.c b/tools/gpioget.c
> index 196ebeb..17614cb 100644
> --- a/tools/gpioget.c
> +++ b/tools/gpioget.c
> @@ -17,10 +17,11 @@ static const struct option longopts[] = {
>         { "help",       no_argument,    NULL,   'h' },
>         { "version",    no_argument,    NULL,   'v' },
>         { "active-low", no_argument,    NULL,   'l' },
> +       { "bias", required_argument,    NULL,   'B' },
>         { GETOPT_NULL_LONGOPT },
>  };
>
> -static const char *const shortopts = "+hvl";
> +static const char *const shortopts = "+hvlB:";
>
>  static void print_help(void)
>  {
> @@ -32,6 +33,25 @@ static void print_help(void)
>         printf("  -h, --help:\t\tdisplay this message and exit\n");
>         printf("  -v, --version:\tdisplay the version and exit\n");
>         printf("  -l, --active-low:\tset the line active state to low\n");
> +       printf("  -B, --bias=[as-is|disable|pull-down|pull-up] (defaults to 'as-is'):\n");
> +       printf("                set the line bias\n");
> +       printf("\n");
> +       printf("Biases:\n");
> +       printf("  as-is:\tleave bias unchanged\n");
> +       printf("  disable:\tdisable bias\n");
> +       printf("  pull-up:\tenable pull-up\n");
> +       printf("  pull-down:\tenable pull-down\n");
> +}
> +
> +static int bias_flags(const char *option)
> +{
> +       if (strcmp(option, "pull-down") == 0)
> +               return GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN;
> +       if (strcmp(option, "pull-up") == 0)
> +               return GPIOD_CTXLESS_FLAG_BIAS_PULL_UP;
> +       if (strcmp(option, "disable") == 0)
> +               return GPIOD_CTXLESS_FLAG_BIAS_DISABLE;
> +       return 0;
>  }
>
>  int main(int argc, char **argv)
> @@ -39,6 +59,7 @@ int main(int argc, char **argv)
>         unsigned int *offsets, i, num_lines;
>         int *values, optc, opti, rv;
>         bool active_low = false;
> +       int flags = 0;
>         char *device, *end;
>
>         for (;;) {
> @@ -56,6 +77,9 @@ int main(int argc, char **argv)
>                 case 'l':
>                         active_low = true;
>                         break;
> +               case 'B':
> +                       flags = bias_flags(optarg);
> +                       break;
>                 case '?':
>                         die("try %s --help", get_progname());
>                 default:
> @@ -86,9 +110,9 @@ int main(int argc, char **argv)
>                         die("invalid GPIO offset: %s", argv[i + 1]);
>         }
>
> -       rv = gpiod_ctxless_get_value_multiple(device, offsets, values,
> -                                             num_lines, active_low,
> -                                             "gpioget");
> +       rv = gpiod_ctxless_get_value_multiple_ext(device, offsets, values,
> +                                                 num_lines, active_low,
> +                                                 "gpioget", flags);
>         if (rv < 0)
>                 die_perror("error reading GPIO values");
>
> diff --git a/tools/gpiomon.c b/tools/gpiomon.c
> index 9a1843b..687212d 100644
> --- a/tools/gpiomon.c
> +++ b/tools/gpiomon.c
> @@ -22,6 +22,7 @@ static const struct option longopts[] = {
>         { "help",               no_argument,            NULL,   'h' },
>         { "version",            no_argument,            NULL,   'v' },
>         { "active-low",         no_argument,            NULL,   'l' },
> +       { "bias",               required_argument,      NULL,   'B' },
>         { "num-events",         required_argument,      NULL,   'n' },
>         { "silent",             no_argument,            NULL,   's' },
>         { "rising-edge",        no_argument,            NULL,   'r' },
> @@ -31,7 +32,7 @@ static const struct option longopts[] = {
>         { GETOPT_NULL_LONGOPT },
>  };
>
> -static const char *const shortopts = "+hvln:srfbF:";
> +static const char *const shortopts = "+hvlB:n:srfbF:";
>
>  static void print_help(void)
>  {
> @@ -43,6 +44,8 @@ static void print_help(void)
>         printf("  -h, --help:\t\tdisplay this message and exit\n");
>         printf("  -v, --version:\tdisplay the version and exit\n");
>         printf("  -l, --active-low:\tset the line active state to low\n");
> +       printf("  -B, --bias=[as-is|disable|pull-down|pull-up] (defaults to 'as-is'):\n");
> +       printf("                set the line bias\n");
>         printf("  -n, --num-events=NUM:\texit after processing NUM events\n");
>         printf("  -s, --silent:\t\tdon't print event info\n");
>         printf("  -r, --rising-edge:\tonly process rising edge events\n");
> @@ -50,6 +53,12 @@ static void print_help(void)
>         printf("  -b, --line-buffered:\tset standard output as line buffered\n");
>         printf("  -F, --format=FMT\tspecify custom output format\n");
>         printf("\n");
> +       printf("Biases:\n");
> +       printf("  as-is:\tleave bias unchanged\n");
> +       printf("  disable:\tdisable bias\n");
> +       printf("  pull-up:\tenable pull-up\n");
> +       printf("  pull-down:\tenable pull-down\n");
> +       printf("\n");
>         printf("Format specifiers:\n");
>         printf("  %%o:  GPIO line offset\n");
>         printf("  %%e:  event type (0 - falling edge, 1 rising edge)\n");
> @@ -240,10 +249,22 @@ static int make_signalfd(void)
>         return sigfd;
>  }
>
> +static int bias_flags(const char *option)
> +{
> +       if (strcmp(option, "pull-down") == 0)
> +               return GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN;
> +       if (strcmp(option, "pull-up") == 0)
> +               return GPIOD_CTXLESS_FLAG_BIAS_PULL_UP;
> +       if (strcmp(option, "disable") == 0)
> +               return GPIOD_CTXLESS_FLAG_BIAS_DISABLE;
> +       return 0;

Does it mean that any other string would be interpreted as 'as-is'?
I'd prefer it to bail out on invalid value.

> +}
> +
>  int main(int argc, char **argv)
>  {
>         unsigned int offsets[GPIOD_LINE_BULK_MAX_LINES], num_lines = 0, offset;
>         bool active_low = false, watch_rising = false, watch_falling = false;
> +       int flags = 0;
>         struct timespec timeout = { 10, 0 };
>         int optc, opti, rv, i, event_type;
>         struct mon_ctx ctx;
> @@ -266,6 +287,9 @@ int main(int argc, char **argv)
>                 case 'l':
>                         active_low = true;
>                         break;
> +               case 'B':
> +                       flags = bias_flags(optarg);
> +                       break;
>                 case 'n':
>                         ctx.events_wanted = strtoul(optarg, &end, 10);
>                         if (*end != '\0')
> @@ -320,11 +344,11 @@ int main(int argc, char **argv)
>
>         ctx.sigfd = make_signalfd();
>
> -       rv = gpiod_ctxless_event_monitor_multiple(argv[0], event_type,
> -                                                 offsets, num_lines,
> -                                                 active_low, "gpiomon",
> -                                                 &timeout, poll_callback,
> -                                                 event_callback, &ctx);
> +       rv = gpiod_ctxless_event_monitor_multiple_ext(
> +                               argv[0], event_type, offsets,
> +                               num_lines, active_low, "gpiomon",
> +                               &timeout, poll_callback,
> +                               event_callback, &ctx, flags);
>         if (rv)
>                 die_perror("error waiting for events");
>
> diff --git a/tools/gpioset.c b/tools/gpioset.c
> index d9977a7..b91baea 100644
> --- a/tools/gpioset.c
> +++ b/tools/gpioset.c
> @@ -23,6 +23,8 @@ static const struct option longopts[] = {
>         { "help",               no_argument,            NULL,   'h' },
>         { "version",            no_argument,            NULL,   'v' },
>         { "active-low",         no_argument,            NULL,   'l' },
> +       { "bias",               required_argument,      NULL,   'B' },
> +       { "drive",              required_argument,      NULL,   'D' },
>         { "mode",               required_argument,      NULL,   'm' },
>         { "sec",                required_argument,      NULL,   's' },
>         { "usec",               required_argument,      NULL,   'u' },
> @@ -30,7 +32,7 @@ static const struct option longopts[] = {
>         { GETOPT_NULL_LONGOPT },
>  };
>
> -static const char *const shortopts = "+hvlm:s:u:b";
> +static const char *const shortopts = "+hvlB:D:m:s:u:b";
>
>  static void print_help(void)
>  {
> @@ -42,12 +44,27 @@ static void print_help(void)
>         printf("  -h, --help:\t\tdisplay this message and exit\n");
>         printf("  -v, --version:\tdisplay the version and exit\n");
>         printf("  -l, --active-low:\tset the line active state to low\n");
> +       printf("  -B, --bias=[as-is|disable|pull-down|pull-up] (defaults to 'as-is'):\n");
> +       printf("                set the line bias\n");
> +       printf("  -D, --drive=[push-pull|open-drain|open-source] (defaults to 'push-pull'):\n");
> +       printf("                set the line drive mode\n");
>         printf("  -m, --mode=[exit|wait|time|signal] (defaults to 'exit'):\n");
>         printf("                tell the program what to do after setting values\n");
>         printf("  -s, --sec=SEC:\tspecify the number of seconds to wait (only valid for --mode=time)\n");
>         printf("  -u, --usec=USEC:\tspecify the number of microseconds to wait (only valid for --mode=time)\n");
>         printf("  -b, --background:\tafter setting values: detach from the controlling terminal\n");
>         printf("\n");
> +       printf("Biases:\n");
> +       printf("  as-is:\tleave bias unchanged\n");
> +       printf("  disable:\tdisable bias\n");
> +       printf("  pull-up:\tenable pull-up\n");
> +       printf("  pull-down:\tenable pull-down\n");
> +       printf("\n");
> +       printf("Drives:\n");
> +       printf("  push-pull:\tdrive the line both high and low\n");
> +       printf("  open-drain:\tdrive the line low or go high impedance\n");
> +       printf("  open-source:\tdrive the line high or go high impedance\n");
> +       printf("\n");
>         printf("Modes:\n");
>         printf("  exit:\t\tset values and exit immediately\n");
>         printf("  wait:\t\tset values and wait for user to press ENTER\n");
> @@ -178,11 +195,31 @@ static const struct mode_mapping *parse_mode(const char *mode)
>         return NULL;
>  }
>
> +static int bias_flags(const char *option)
> +{
> +       if (strcmp(option, "pull-down") == 0)
> +               return GPIOD_CTXLESS_FLAG_BIAS_PULL_DOWN;
> +       if (strcmp(option, "pull-up") == 0)
> +               return GPIOD_CTXLESS_FLAG_BIAS_PULL_UP;
> +       if (strcmp(option, "disable") == 0)
> +               return GPIOD_CTXLESS_FLAG_BIAS_DISABLE;
> +       return 0;
> +}
> +
> +static int drive_flags(const char *option)
> +{
> +       if (strcmp(option, "open-drain") == 0)
> +               return GPIOD_CTXLESS_FLAG_OPEN_DRAIN;
> +       if (strcmp(option, "open-source") == 0)
> +               return GPIOD_CTXLESS_FLAG_OPEN_SOURCE;
> +       return 0;

Thanks for doing this, but please put it into a separate commit that
adds support for drive flags.


> +}
> +
>  int main(int argc, char **argv)
>  {
>         const struct mode_mapping *mode = &modes[MODE_EXIT];
>         unsigned int *offsets, num_lines, i;
> -       int *values, rv, optc, opti;
> +       int *values, rv, optc, opti, flags = 0;
>         struct callback_data cbdata;
>         bool active_low = false;
>         char *device, *end;
> @@ -204,6 +241,12 @@ int main(int argc, char **argv)
>                 case 'l':
>                         active_low = true;
>                         break;
> +               case 'B':
> +                       flags |= bias_flags(optarg);
> +                       break;
> +               case 'D':
> +                       flags |= drive_flags(optarg);
> +                       break;
>                 case 'm':
>                         mode = parse_mode(optarg);
>                         if (!mode)
> @@ -268,9 +311,10 @@ int main(int argc, char **argv)
>                         die("invalid offset: %s", argv[i + 1]);
>         }
>
> -       rv = gpiod_ctxless_set_value_multiple(device, offsets, values,
> -                                             num_lines, active_low, "gpioset",
> -                                             mode->callback, &cbdata);
> +       rv = gpiod_ctxless_set_value_multiple_ext(
> +                               device, offsets, values,
> +                               num_lines, active_low, "gpioset",
> +                               mode->callback, &cbdata, flags);
>         if (rv < 0)
>                 die_perror("error setting the GPIO line values");
>
> --
> 2.24.0
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, back to index

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-25 14:31 [libgpiod][PATCH v3 00/14] Add support for bias flags and SET_CONFIG Kent Gibson
2019-11-25 14:31 ` [libgpiod][PATCH v3 01/14] core: add support for bias flags Kent Gibson
2019-11-25 14:31 ` [libgpiod][PATCH v3 02/14] tests: add tests " Kent Gibson
2019-11-28 10:28   ` Bartosz Golaszewski
2019-11-25 14:31 ` [libgpiod][PATCH v3 03/14] bindings: cxx: add support " Kent Gibson
2019-11-28 10:29   ` Bartosz Golaszewski
2019-11-25 14:31 ` [libgpiod][PATCH v3 04/14] bindings: cxx: tests: add tests " Kent Gibson
2019-11-25 14:31 ` [libgpiod][PATCH v3 05/14] bindings: python: add support " Kent Gibson
2019-11-25 14:31 ` [libgpiod][PATCH v3 06/14] bindings: python: tests: add tests " Kent Gibson
2019-11-25 14:31 ` [libgpiod][PATCH v3 07/14] core: add support for SET_CONFIG Kent Gibson
2019-11-28 10:29   ` Bartosz Golaszewski
2019-11-25 14:31 ` [libgpiod][PATCH v3 08/14] tests: add tests " Kent Gibson
2019-11-25 14:31 ` [libgpiod][PATCH v3 09/14] bindings: cxx: add support " Kent Gibson
2019-11-28 10:29   ` Bartosz Golaszewski
2019-11-25 14:31 ` [libgpiod][PATCH v3 10/14] bindings: cxx: tests: add tests for SET_CONFIG methods Kent Gibson
2019-11-25 14:31 ` [libgpiod][PATCH v3 11/14] bindings: python: add support for SET_CONFIG Kent Gibson
2019-11-28 10:29   ` Bartosz Golaszewski
2019-11-25 14:31 ` [libgpiod][PATCH v3 12/14] bindings: python: tests: add tests for SET_CONFIG methods Kent Gibson
2019-11-25 14:31 ` [libgpiod][PATCH v3 13/14] tools: add support for bias flags Kent Gibson
2019-11-28 10:29   ` Bartosz Golaszewski
2019-11-25 14:31 ` [libgpiod][PATCH v3 14/14] tools: add tests for bias and drive flags Kent Gibson

Linux-GPIO Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-gpio/0 linux-gpio/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-gpio linux-gpio/ https://lore.kernel.org/linux-gpio \
		linux-gpio@vger.kernel.org
	public-inbox-index linux-gpio

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-gpio


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git