This replaces the old tests for the C API v1 based on gpio-mockup with a test suite based on gpio-sim that covers around 95% of the libgpiod v2 codebase. The test harness has been rebuilt and shrank significantly as well. The libgpiosim API has been wrapped in a gobject interface. Signed-off-by: Bartosz Golaszewski <brgl@xxxxxxxx> --- configure.ac | 8 +- tests/Makefile.am | 20 +- tests/gpiod-test-helpers.c | 49 ++ tests/gpiod-test-helpers.h | 131 ++++ tests/gpiod-test-sim.c | 303 ++++++++++ tests/gpiod-test-sim.h | 42 ++ tests/gpiod-test.c | 233 +------- tests/gpiod-test.h | 81 +-- tests/gpiosim/gpiosim.c | 1 + tests/mockup/Makefile.am | 11 - tests/mockup/gpio-mockup.c | 496 ---------------- tests/mockup/gpio-mockup.h | 36 -- tests/tests-chip.c | 282 ++++----- tests/tests-edge-event.c | 420 +++++++++++++ tests/tests-event.c | 908 ---------------------------- tests/tests-info-event.c | 301 ++++++++++ tests/tests-line-config.c | 408 +++++++++++++ tests/tests-line-info.c | 316 ++++++++++ tests/tests-line-request.c | 421 +++++++++++++ tests/tests-line.c | 1091 ---------------------------------- tests/tests-misc.c | 86 ++- tests/tests-request-config.c | 90 +++ 22 files changed, 2714 insertions(+), 3020 deletions(-) create mode 100644 tests/gpiod-test-helpers.c create mode 100644 tests/gpiod-test-helpers.h create mode 100644 tests/gpiod-test-sim.c create mode 100644 tests/gpiod-test-sim.h delete mode 100644 tests/mockup/Makefile.am delete mode 100644 tests/mockup/gpio-mockup.c delete mode 100644 tests/mockup/gpio-mockup.h create mode 100644 tests/tests-edge-event.c delete mode 100644 tests/tests-event.c create mode 100644 tests/tests-info-event.c create mode 100644 tests/tests-line-config.c create mode 100644 tests/tests-line-info.c create mode 100644 tests/tests-line-request.c delete mode 100644 tests/tests-line.c create mode 100644 tests/tests-request-config.c diff --git a/configure.ac b/configure.ac index dc09d70..b4f5db0 100644 --- a/configure.ac +++ b/configure.ac @@ -28,9 +28,8 @@ AC_SUBST(VERSION_STR, [$PACKAGE_VERSION$EXTRA_VERSION]) AC_SUBST(ABI_VERSION, [4.1.2]) # Have a separate ABI version for C++ bindings: AC_SUBST(ABI_CXX_VERSION, [2.1.1]) -# ABI version for libgpiomockup (we need this since it can be installed if we +# ABI version for libgpiosim (we need this since it can be installed if we # enable install-tests). -AC_SUBST(ABI_MOCKUP_VERSION, [0.1.0]) AC_SUBST(ABI_GPIOSIM_VERSION, [0.1.0]) AC_CONFIG_AUX_DIR([autostuff]) @@ -137,14 +136,14 @@ AC_DEFUN([FUNC_NOT_FOUND_TESTS], if test "x$with_tests" = xtrue then - # For libgpiomockup & libgpiosim + # For libgpiosim AC_CHECK_FUNC([qsort], [], [FUNC_NOT_FOUND_TESTS([qsort])]) PKG_CHECK_MODULES([KMOD], [libkmod >= 18]) - PKG_CHECK_MODULES([UDEV], [libudev >= 215]) PKG_CHECK_MODULES([MOUNT], [mount >= 2.33.1]) # For core library tests PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.50]) + PKG_CHECK_MODULES([GOBJECT], [gobject-2.0 >= 2.50]) if test "x$with_tools" = xtrue then @@ -235,7 +234,6 @@ AC_CONFIG_FILES([Makefile lib/libgpiod.pc tools/Makefile tests/Makefile - tests/mockup/Makefile tests/gpiosim/Makefile bindings/cxx/libgpiodcxx.pc bindings/Makefile diff --git a/tests/Makefile.am b/tests/Makefile.am index 8ec51e9..df8c87b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,24 +1,32 @@ # SPDX-License-Identifier: GPL-2.0-or-later # SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski <brgl@xxxxxxxx> -SUBDIRS = mockup gpiosim +SUBDIRS = gpiosim AM_CFLAGS = -I$(top_srcdir)/include/ -I$(top_srcdir)/tests/gpiosim/ AM_CFLAGS += -include $(top_builddir)/config.h -AM_CFLAGS += -Wall -Wextra -g -std=gnu89 $(GLIB_CFLAGS) +AM_CFLAGS += -Wall -Wextra -g -std=gnu89 $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiod-test\" AM_CFLAGS += $(PROFILING_FLAGS) AM_LDFLAGS = -pthread LDADD = $(top_builddir)/lib/libgpiod.la LDADD += $(top_builddir)/tests/gpiosim/libgpiosim.la -LDADD += $(GLIB_LIBS) +LDADD += $(GLIB_LIBS) $(GOBJECT_LIBS) bin_PROGRAMS = gpiod-test gpiod_test_SOURCES = \ gpiod-test.c \ gpiod-test.h \ + gpiod-test-helpers.c \ + gpiod-test-helpers.h \ + gpiod-test-sim.c \ + gpiod-test-sim.h \ tests-chip.c \ - tests-event.c \ - tests-line.c \ - tests-misc.c + tests-edge-event.c \ + tests-info-event.c \ + tests-line-config.c \ + tests-line-info.c \ + tests-line-request.c \ + tests-misc.c \ + tests-request-config.c diff --git a/tests/gpiod-test-helpers.c b/tests/gpiod-test-helpers.c new file mode 100644 index 0000000..24a6ee4 --- /dev/null +++ b/tests/gpiod-test-helpers.c @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski <brgl@xxxxxxxx> */ + +/* + * Testing framework for the core library. + * + * This file contains functions and definitions extending the GLib unit testing + * framework with functionalities necessary to test the libgpiod core C API as + * well as the kernel-to-user-space interface. + */ + +#include "gpiod-test-helpers.h" + +GVariant * +gpiod_test_package_line_names(const struct gpiod_test_line_name *names) +{ + const struct gpiod_test_line_name *name; + GVariantBuilder *builder; + GVariant *ret; + + builder = g_variant_builder_new(G_VARIANT_TYPE("a(us)")); + + for (name = &names[0]; name->name; name++) + g_variant_builder_add(builder, "(us)", + name->offset, name->name); + + ret = g_variant_new("a(us)", builder); + g_variant_builder_unref(builder); + + return ret; +} + +GVariant *gpiod_test_package_hogs(const struct gpiod_test_hog *hogs) +{ + const struct gpiod_test_hog *hog; + GVariantBuilder *builder; + GVariant *ret; + + builder = g_variant_builder_new(G_VARIANT_TYPE("a(usi)")); + + for (hog = &hogs[0]; hog->name; hog++) + g_variant_builder_add(builder, "(usi)", + hog->offset, hog->name, hog->direction); + + ret = g_variant_new("a(usi)", builder); + g_variant_builder_unref(builder); + + return ret; +} diff --git a/tests/gpiod-test-helpers.h b/tests/gpiod-test-helpers.h new file mode 100644 index 0000000..1ca84f4 --- /dev/null +++ b/tests/gpiod-test-helpers.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@xxxxxxxx> */ + +#ifndef __GPIOD_TEST_HELPERS_H__ +#define __GPIOD_TEST_HELPERS_H__ + +#include <errno.h> +#include <glib.h> +#include <gpiod.h> + +#include "gpiod-test-sim.h" + +/* + * These typedefs are needed to make g_autoptr work - it doesn't accept + * regular 'struct typename' syntax. + */ + +typedef struct gpiod_chip struct_gpiod_chip; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_chip, gpiod_chip_close); + +typedef struct gpiod_line_info struct_gpiod_line_info; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_info, gpiod_line_info_free); + +typedef struct gpiod_info_event struct_gpiod_info_event; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_info_event, gpiod_info_event_free); + +typedef struct gpiod_line_config struct_gpiod_line_config; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_config, gpiod_line_config_free); + +typedef struct gpiod_request_config struct_gpiod_request_config; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_request_config, + gpiod_request_config_free); + +typedef struct gpiod_line_request struct_gpiod_line_request; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_line_request, + gpiod_line_request_release); + +typedef struct gpiod_edge_event struct_gpiod_edge_event; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_edge_event, gpiod_edge_event_free); + +typedef struct gpiod_edge_event_buffer struct_gpiod_edge_event_buffer; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_edge_event_buffer, + gpiod_edge_event_buffer_free); + +#define gpiod_test_return_if_failed() \ + do { \ + if (g_test_failed()) \ + return; \ + } while (0) + +#define gpiod_test_join_thread_and_return_if_failed(_thread) \ + do { \ + if (g_test_failed()) { \ + g_thread_join(_thread); \ + return; \ + } \ + } while (0) + +#define gpiod_test_open_chip_or_fail(_path) \ + ({ \ + struct gpiod_chip *_chip = gpiod_chip_open((_path)); \ + g_assert_nonnull(_chip); \ + gpiod_test_return_if_failed(); \ + _chip; \ + }) + +#define gpiod_test_get_line_info_or_fail(_chip, _offset) \ + ({ \ + struct gpiod_line_info *_info = \ + gpiod_chip_get_line_info((_chip), (_offset)); \ + g_assert_nonnull(_info); \ + gpiod_test_return_if_failed(); \ + _info; \ + }) + +#define gpiod_test_create_line_config_or_fail() \ + ({ \ + struct gpiod_line_config *_config = \ + gpiod_line_config_new(); \ + g_assert_nonnull(_config); \ + gpiod_test_return_if_failed(); \ + _config; \ + }) + +#define gpiod_test_create_edge_event_buffer_or_fail(_capacity) \ + ({ \ + struct gpiod_edge_event_buffer *_buffer = \ + gpiod_edge_event_buffer_new(_capacity); \ + g_assert_nonnull(_buffer); \ + gpiod_test_return_if_failed(); \ + _buffer; \ + }) + +#define gpiod_test_create_request_config_or_fail() \ + ({ \ + struct gpiod_request_config *_config = \ + gpiod_request_config_new(); \ + g_assert_nonnull(_config); \ + gpiod_test_return_if_failed(); \ + _config; \ + }) + +#define gpiod_test_request_lines_or_fail(_chip, _req_cfg, _line_cfg) \ + ({ \ + struct gpiod_line_request *_request = \ + gpiod_chip_request_lines((_chip), \ + (_req_cfg), (_line_cfg)); \ + g_assert_nonnull(_request); \ + gpiod_test_return_if_failed(); \ + _request; \ + }) + +#define gpiod_test_expect_errno(_expected) \ + g_assert_cmpint((_expected), ==, errno) + +struct gpiod_test_line_name { + guint offset; + const gchar *name; +}; + +struct gpiod_test_hog { + guint offset; + const gchar *name; + GPIOSimHogDir direction; +}; + +GVariant * +gpiod_test_package_line_names(const struct gpiod_test_line_name *names); +GVariant *gpiod_test_package_hogs(const struct gpiod_test_hog *hogs); + +#endif /* __GPIOD_TEST_HELPERS_H__ */ diff --git a/tests/gpiod-test-sim.c b/tests/gpiod-test-sim.c new file mode 100644 index 0000000..9dd6f69 --- /dev/null +++ b/tests/gpiod-test-sim.c @@ -0,0 +1,303 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@xxxxxxxx> */ + +#include <errno.h> +#include <gpiosim.h> +#include <stdlib.h> + +#include "gpiod-test-sim.h" + +struct _GPIOSimChip { + GObject parent_instance; + struct gpiosim_bank *bank; +}; + +enum { + G_GPIOSIM_CHIP_PROP_DEV_PATH = 1, + G_GPIOSIM_CHIP_PROP_NAME, + G_GPIOSIM_CHIP_PROP_NUM_LINES, + G_GPIOSIM_CHIP_PROP_LABEL, + G_GPIOSIM_CHIP_PROP_LINE_NAMES, + G_GPIOSIM_CHIP_PROP_HOGS, +}; + +static GOnce gpiosim_ctx_once = G_ONCE_INIT; +static struct gpiosim_ctx *sim_ctx; + +G_DEFINE_TYPE(GPIOSimChip, g_gpiosim_chip, G_TYPE_OBJECT); + +static void g_gpiosim_ctx_unref(void) +{ + gpiosim_ctx_unref(sim_ctx); +} + +static void *g_gpiosim_ctx_init(void *data G_GNUC_UNUSED) +{ + sim_ctx = gpiosim_ctx_new(); + if (!sim_ctx) + g_error("Unable to initialize libgpiosim: %s", + g_strerror(errno)); + + atexit(g_gpiosim_ctx_unref); + + return NULL; +} + +static void g_gpiosim_chip_constructed(GObject *obj) +{ + GPIOSimChip *self = G_GPIOSIM_CHIP(obj); + struct gpiosim_dev *dev; + gint ret; + + dev = gpiosim_bank_get_dev(self->bank); + ret = gpiosim_dev_enable(dev); + gpiosim_dev_unref(dev); + if (ret) + g_error("Error while trying to enable the simulated GPIO device: %s", + g_strerror(errno)); +} + +static void g_gpiosim_chip_get_property(GObject *obj, guint prop_id, + GValue *val, GParamSpec *pspec) +{ + GPIOSimChip *self = G_GPIOSIM_CHIP(obj); + + switch (prop_id) { + case G_GPIOSIM_CHIP_PROP_DEV_PATH: + g_value_set_static_string(val, + gpiosim_bank_get_dev_path(self->bank)); + break; + case G_GPIOSIM_CHIP_PROP_NAME: + g_value_set_static_string(val, + gpiosim_bank_get_chip_name(self->bank)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + break; + } +} + +static void g_gpiosim_chip_set_property(GObject *obj, guint prop_id, + const GValue *val, GParamSpec *pspec) +{ + GPIOSimChip *self = G_GPIOSIM_CHIP(obj); + gint ret, vdir, dir; + GVariantIter *iter; + GVariant *variant; + guint offset; + gchar *name; + + switch (prop_id) { + case G_GPIOSIM_CHIP_PROP_NUM_LINES: + ret = gpiosim_bank_set_num_lines(self->bank, + g_value_get_uint(val)); + if (ret) + g_error("Unable to set the number of lines exposed by the simulated chip: %s", + g_strerror(errno)); + break; + case G_GPIOSIM_CHIP_PROP_LABEL: + ret = gpiosim_bank_set_label(self->bank, + g_value_get_string(val)); + if (ret) + g_error("Unable to set the label of the simulated chip: %s", + g_strerror(errno)); + break; + case G_GPIOSIM_CHIP_PROP_LINE_NAMES: + variant = g_value_get_variant(val); + if (!variant) + break; + + iter = g_variant_iter_new(variant); + + while (g_variant_iter_loop(iter, "(us)", &offset, &name)) { + ret = gpiosim_bank_set_line_name(self->bank, + offset, name); + if (ret) + g_error("Unable to set the name of the simulated GPIO line: %s", + g_strerror(errno)); + } + + g_variant_iter_free(iter); + break; + case G_GPIOSIM_CHIP_PROP_HOGS: + variant = g_value_get_variant(val); + if (!variant) + break; + + iter = g_variant_iter_new(variant); + + while (g_variant_iter_loop(iter, "(usi)", + &offset, &name, &vdir)) { + switch (vdir) { + case G_GPIOSIM_HOG_DIR_INPUT: + dir = GPIOSIM_HOG_DIR_INPUT; + break; + case G_GPIOSIM_HOG_DIR_OUTPUT_HIGH: + dir = GPIOSIM_HOG_DIR_OUTPUT_HIGH; + break; + case G_GPIOSIM_HOG_DIR_OUTPUT_LOW: + dir = GPIOSIM_HOG_DIR_OUTPUT_LOW; + break; + default: + g_error("Invalid hog direction value: %d", + vdir); + } + + ret = gpiosim_bank_hog_line(self->bank, + offset, name, dir); + if (ret) + g_error("Unable to hog the simulated GPIO line: %s", + g_strerror(errno)); + } + + g_variant_iter_free(iter); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + break; + } +} + +static void g_gpiosim_chip_dispose(GObject *obj) +{ + GPIOSimChip *self = G_GPIOSIM_CHIP(obj); + struct gpiosim_dev *dev; + gint ret; + + dev = gpiosim_bank_get_dev(self->bank); + + if (gpiosim_dev_is_live(dev)) { + ret = gpiosim_dev_disable(dev); + if (ret) + g_error("Error while trying to disable the simulated GPIO device: %s", + g_strerror(errno)); + } + + gpiosim_dev_unref(dev); +} + +static void g_gpiosim_chip_finalize(GObject *obj) +{ + GPIOSimChip *self = G_GPIOSIM_CHIP(obj); + + gpiosim_bank_unref(self->bank); + + G_OBJECT_CLASS(g_gpiosim_chip_parent_class)->finalize(obj); +} + +static void g_gpiosim_chip_class_init(GPIOSimChipClass *chip_class) +{ + GObjectClass *class = G_OBJECT_CLASS(chip_class); + + class->constructed = g_gpiosim_chip_constructed; + class->get_property = g_gpiosim_chip_get_property; + class->set_property = g_gpiosim_chip_set_property; + class->dispose = g_gpiosim_chip_dispose; + class->finalize = g_gpiosim_chip_finalize; + + g_object_class_install_property(class, G_GPIOSIM_CHIP_PROP_DEV_PATH, + g_param_spec_string("dev-path", "Device path", + "Character device filesystem path.", NULL, + G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOSIM_CHIP_PROP_NAME, + g_param_spec_string("name", "Chip name", + "Name of this chip device as set by the kernel.", NULL, + G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOSIM_CHIP_PROP_NUM_LINES, + g_param_spec_uint("num-lines", "Number of lines", + "Number of lines this simulated chip exposes.", + 1, G_MAXUINT, 1, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property(class, G_GPIOSIM_CHIP_PROP_LABEL, + g_param_spec_string("label", "Chip label", + "Label of this simulated chip.", NULL, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property(class, G_GPIOSIM_CHIP_PROP_LINE_NAMES, + g_param_spec_variant("line-names", "Line names", + "List of names of the lines exposed by this chip", + (GVariantType *)"a(us)", NULL, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property(class, G_GPIOSIM_CHIP_PROP_HOGS, + g_param_spec_variant("hogs", "Line hogs", + "List of hogged lines and their directions.", + (GVariantType *)"a(usi)", NULL, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void g_gpiosim_chip_init(GPIOSimChip *self) +{ + struct gpiosim_dev *dev; + + g_once(&gpiosim_ctx_once, g_gpiosim_ctx_init, NULL); + + dev = gpiosim_dev_new(sim_ctx, NULL); + if (!dev) + g_error("Unable to instantiate new GPIO device: %s", + g_strerror(errno)); + + self->bank = gpiosim_bank_new(dev, NULL); + gpiosim_dev_unref(dev); + if (!self->bank) + g_error("Unable to instantiate new GPIO bank: %s", + g_strerror(errno)); +} + +static const gchar * +g_gpiosim_chip_get_string_prop(GPIOSimChip *self, const gchar *prop) +{ + GValue val = G_VALUE_INIT; + const gchar *str; + + g_object_get_property(G_OBJECT(self), prop, &val); + str = g_value_get_string(&val); + g_value_unset(&val); + + return str; +} + +const gchar *g_gpiosim_chip_get_dev_path(GPIOSimChip *self) +{ + return g_gpiosim_chip_get_string_prop(self, "dev-path"); +} + +const gchar *g_gpiosim_chip_get_name(GPIOSimChip *self) +{ + return g_gpiosim_chip_get_string_prop(self, "name"); +} + +gint g_gpiosim_chip_get_value(GPIOSimChip *chip, guint offset) +{ + gint val; + + val = gpiosim_bank_get_value(chip->bank, offset); + if (val < 0) + g_error("Unable to read the line value: %s", g_strerror(errno)); + + return val; +} + +void g_gpiosim_chip_set_pull(GPIOSimChip *chip, guint offset, GPIOSimPull pull) +{ + gint ret, sim_pull; + + switch (pull) { + case G_GPIOSIM_PULL_DOWN: + sim_pull = GPIOSIM_PULL_DOWN; + break; + case G_GPIOSIM_PULL_UP: + sim_pull = GPIOSIM_PULL_UP; + break; + default: + g_error("invalid pull value"); + } + + ret = gpiosim_bank_set_pull(chip->bank, offset, sim_pull); + if (ret) + g_error("Unable to set the pull setting for simulated line: %s", + g_strerror(errno)); +} diff --git a/tests/gpiod-test-sim.h b/tests/gpiod-test-sim.h new file mode 100644 index 0000000..0cc2a0b --- /dev/null +++ b/tests/gpiod-test-sim.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@xxxxxxxx> */ + +#ifndef __GPIOD_TEST_SIM_H__ +#define __GPIOD_TEST_SIM_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef enum { + G_GPIOSIM_PULL_UP = 1, + G_GPIOSIM_PULL_DOWN, +} GPIOSimPull; + +typedef enum { + G_GPIOSIM_HOG_DIR_INPUT = 1, + G_GPIOSIM_HOG_DIR_OUTPUT_HIGH, + G_GPIOSIM_HOG_DIR_OUTPUT_LOW, +} GPIOSimHogDir; + +typedef struct _GPIOSimChip GPIOSimChip; + +G_DECLARE_FINAL_TYPE(GPIOSimChip, g_gpiosim_chip, G_GPIOSIM, CHIP, GObject); + +#define G_GPIOSIM_TYPE_CHIP (g_gpiosim_chip_get_type()) +#define G_GPIOSIM_CHIP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOSIM_TYPE_CHIP, GPIOSimChip)) + +#define g_gpiosim_chip_new(...) \ + G_GPIOSIM_CHIP(g_object_new(G_GPIOSIM_TYPE_CHIP, __VA_ARGS__)) + +const gchar *g_gpiosim_chip_get_dev_path(GPIOSimChip *self); +const gchar *g_gpiosim_chip_get_name(GPIOSimChip *self); + +gint g_gpiosim_chip_get_value(GPIOSimChip *self, guint offset); +void g_gpiosim_chip_set_pull(GPIOSimChip *self, guint offset, GPIOSimPull pull); + +G_END_DECLS + +#endif /* __GPIOD_TEST_SIM_H__ */ diff --git a/tests/gpiod-test.c b/tests/gpiod-test.c index aa9eaa4..df546a1 100644 --- a/tests/gpiod-test.c +++ b/tests/gpiod-test.c @@ -2,8 +2,6 @@ // SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski <brgl@xxxxxxxx> #include <errno.h> -#include <glib/gstdio.h> -#include <gpiosim.h> #include <linux/version.h> #include <stdio.h> #include <sys/utsname.h> @@ -12,28 +10,13 @@ #include "gpiod-test.h" #define MIN_KERNEL_MAJOR 5 -#define MIN_KERNEL_MINOR 10 +#define MIN_KERNEL_MINOR 16 #define MIN_KERNEL_RELEASE 0 #define MIN_KERNEL_VERSION KERNEL_VERSION(MIN_KERNEL_MAJOR, \ MIN_KERNEL_MINOR, \ MIN_KERNEL_RELEASE) -struct gpiod_test_event_thread { - GThread *id; - GMutex lock; - GCond cond; - gboolean should_stop; - guint chip_index; - guint line_offset; - guint period_ms; -}; - -static struct { - GList *tests; - struct gpiosim_ctx *gpiosim; - GPtrArray *sim_chips; - GPtrArray *sim_banks; -} globals; +static GList *tests; static void check_kernel(void) { @@ -63,103 +46,16 @@ static void check_kernel(void) return; } -static void remove_gpiosim_chip(gpointer data) -{ - struct gpiosim_dev *dev = data; - gint ret; - - ret = gpiosim_dev_disable(dev); - if (ret) - g_error("unable to uncommit a simulated GPIO device: %s", - g_strerror(errno)); - - gpiosim_dev_unref(dev); -} - -static void remove_gpiosim_bank(gpointer data) -{ - struct gpiosim_bank *bank = data; - - gpiosim_bank_unref(bank); -} - static void test_func_wrapper(gconstpointer data) { - const _GpiodTestCase *test = data; - struct gpiosim_bank *sim_bank; - struct gpiosim_dev *sim_dev; - gchar *line_name, *label; - gchar chip_idx; - guint i, j; - gint ret; - - globals.sim_chips = g_ptr_array_new_full(test->num_chips, - remove_gpiosim_chip); - globals.sim_banks = g_ptr_array_new_full(test->num_chips, - remove_gpiosim_bank); - - for (i = 0; i < test->num_chips; i++) { - chip_idx = i + 65; - - sim_dev = gpiosim_dev_new(globals.gpiosim, NULL); - if (!sim_dev) - g_error("unable to create a simulated GPIO chip: %s", - g_strerror(errno)); - - sim_bank = gpiosim_bank_new(sim_dev, NULL); - if (!sim_bank) - g_error("unable to create a simulated GPIO bank: %s", - g_strerror(errno)); - - label = g_strdup_printf("gpio-mockup-%c", chip_idx); - ret = gpiosim_bank_set_label(sim_bank, label); - g_free(label); - if (ret) - g_error("unable to set simulated chip label: %s", - g_strerror(errno)); - - ret = gpiosim_bank_set_num_lines(sim_bank, test->chip_sizes[i]); - if (ret) - g_error("unable to set the number of lines for a simulated chip: %s", - g_strerror(errno)); - - if (test->flags & GPIOD_TEST_FLAG_NAMED_LINES) { - for (j = 0; j < test->chip_sizes[i]; j++) { - line_name = g_strdup_printf("gpio-mockup-%c-%u", - chip_idx, j); - - ret = gpiosim_bank_set_line_name(sim_bank, j, - line_name); - g_free(line_name); - if (ret) - g_error("unable to set the line names for a simulated bank: %s", - g_strerror(errno)); - } - } - - ret = gpiosim_dev_enable(sim_dev); - if (ret) - g_error("unable to commit the simulated GPIO device: %s", - g_strerror(errno)); - - g_ptr_array_add(globals.sim_chips, sim_dev); - g_ptr_array_add(globals.sim_banks, sim_bank); - } + const struct _gpiod_test_case *test = data; test->func(); - - g_ptr_array_unref(globals.sim_banks); - g_ptr_array_unref(globals.sim_chips); -} - -static void unref_gpiosim(void) -{ - gpiosim_ctx_unref(globals.gpiosim); } static void add_test_from_list(gpointer element, gpointer data G_GNUC_UNUSED) { - _GpiodTestCase *test = element; + struct _gpiod_test_case *test = element; g_test_add_data_func(test->path, test, test_func_wrapper); } @@ -170,128 +66,17 @@ int main(gint argc, gchar **argv) g_test_set_nonfatal_assertions(); g_debug("running libgpiod test suite"); - g_debug("%u tests registered", g_list_length(globals.tests)); - - /* - * Setup libpiosim first so that it runs its own kernel version - * check before we tell the user our local requirements are met as - * well. - */ - globals.gpiosim = gpiosim_ctx_new(); - if (!globals.gpiosim) - g_error("unable to initialize gpiosim library: %s", - g_strerror(errno)); - atexit(unref_gpiosim); + g_debug("%u tests registered", g_list_length(tests)); check_kernel(); - g_list_foreach(globals.tests, add_test_from_list, NULL); - g_list_free(globals.tests); + g_list_foreach(tests, add_test_from_list, NULL); + g_list_free(tests); return g_test_run(); } -void _gpiod_test_register(_GpiodTestCase *test) -{ - globals.tests = g_list_append(globals.tests, test); -} - -const gchar *gpiod_test_chip_path(guint idx) -{ - struct gpiosim_bank *bank = g_ptr_array_index(globals.sim_banks, idx); - - return gpiosim_bank_get_dev_path(bank); -} - -const gchar *gpiod_test_chip_name(guint idx) -{ - struct gpiosim_bank *bank = g_ptr_array_index(globals.sim_banks, idx); - - return gpiosim_bank_get_chip_name(bank); -} - -gint gpiod_test_chip_get_value(guint chip_index, guint line_offset) -{ - struct gpiosim_bank *bank = g_ptr_array_index(globals.sim_banks, - chip_index); - gint ret; - - ret = gpiosim_bank_get_value(bank, line_offset); - if (ret < 0) - g_error("unable to read line value from gpiosim: %s", - g_strerror(errno)); - - return ret; -} - -void gpiod_test_chip_set_pull(guint chip_index, guint line_offset, gint pull) -{ - struct gpiosim_bank *bank = g_ptr_array_index(globals.sim_banks, - chip_index); - gint ret; - - ret = gpiosim_bank_set_pull(bank, line_offset, - pull ? GPIOSIM_PULL_UP : GPIOSIM_PULL_DOWN); - if (ret) - g_error("unable to set line pull in gpiosim: %s", - g_strerror(errno)); -} - -static gpointer event_worker_func(gpointer data) -{ - GpiodTestEventThread *thread = data; - gboolean signalled; - gint64 end_time; - gint i; - - for (i = 0;; i++) { - g_mutex_lock(&thread->lock); - if (thread->should_stop) { - g_mutex_unlock(&thread->lock); - break; - } - - end_time = g_get_monotonic_time() + thread->period_ms * 1000; - - signalled = g_cond_wait_until(&thread->cond, - &thread->lock, end_time); - if (!signalled) - gpiod_test_chip_set_pull(thread->chip_index, - thread->line_offset, i % 2); - - g_mutex_unlock(&thread->lock); - } - - return NULL; -} - -GpiodTestEventThread * -gpiod_test_start_event_thread(guint chip_index, guint line_offset, guint period_ms) +void _gpiod_test_register(struct _gpiod_test_case *test) { - GpiodTestEventThread *thread = g_malloc0(sizeof(*thread)); - - g_mutex_init(&thread->lock); - g_cond_init(&thread->cond); - - thread->chip_index = chip_index; - thread->line_offset = line_offset; - thread->period_ms = period_ms; - - thread->id = g_thread_new("event-worker", event_worker_func, thread); - - return thread; -} - -void gpiod_test_stop_event_thread(GpiodTestEventThread *thread) -{ - g_mutex_lock(&thread->lock); - thread->should_stop = TRUE; - g_cond_broadcast(&thread->cond); - g_mutex_unlock(&thread->lock); - - (void)g_thread_join(thread->id); - - g_mutex_clear(&thread->lock); - g_cond_clear(&thread->cond); - g_free(thread); + tests = g_list_append(tests, test); } diff --git a/tests/gpiod-test.h b/tests/gpiod-test.h index 1f2a677..16c331a 100644 --- a/tests/gpiod-test.h +++ b/tests/gpiod-test.h @@ -13,86 +13,35 @@ #define __GPIOD_TEST_H__ #include <glib.h> -#include <gpiod.h> - -/* - * These typedefs are needed to make g_autoptr work - it doesn't accept - * regular 'struct typename' syntax. - */ -typedef struct gpiod_chip gpiod_chip_struct; -typedef struct gpiod_line_bulk gpiod_line_bulk_struct; - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(gpiod_chip_struct, gpiod_chip_unref); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(gpiod_line_bulk_struct, gpiod_line_bulk_free); /* These are private definitions and should not be used directly. */ + typedef void (*_gpiod_test_func)(void); -typedef struct _gpiod_test_case _GpiodTestCase; struct _gpiod_test_case { const gchar *path; _gpiod_test_func func; - - guint num_chips; - guint *chip_sizes; - gint flags; }; -void _gpiod_test_register(_GpiodTestCase *test); +void _gpiod_test_register(struct _gpiod_test_case *test); #define _GPIOD_TEST_PATH(_name) \ "/gpiod/" GPIOD_TEST_GROUP "/" G_STRINGIFY(_name) -enum { - /* Dummy lines for this test case should have names assigned. */ - GPIOD_TEST_FLAG_NAMED_LINES = (1 << 0), -}; - /* - * Register a test case function. The last argument is the array of numbers - * of lines per simulated chip. + * Register a test case function. */ -#define GPIOD_TEST_CASE(_name, _flags, ...) \ - static void _name(void); \ - static guint _##_name##_chip_sizes[] = __VA_ARGS__; \ - static _GpiodTestCase _##_name##_test_case = { \ - .path = _GPIOD_TEST_PATH(_name), \ - .func = _name, \ - .num_chips = G_N_ELEMENTS(_##_name##_chip_sizes), \ - .chip_sizes = _##_name##_chip_sizes, \ - .flags = _flags, \ - }; \ - static __attribute__((constructor)) void \ - _##_name##_test_register(void) \ - { \ - _gpiod_test_register(&_##_name##_test_case); \ - } \ - static void _name(void) - -#define GPIOD_TEST_CONSUMER "gpiod-test" - -#define gpiod_test_return_if_failed() \ - do { \ - if (g_test_failed()) \ - return; \ - } while (0) - -/* Wrappers around libgpiomockup helpers. */ -const gchar *gpiod_test_chip_path(guint idx); -const gchar *gpiod_test_chip_name(guint idx); -gint gpiod_test_chip_get_value(guint chip_index, guint line_offset); -void gpiod_test_chip_set_pull(guint chip_index, guint line_offset, gint pull); - -/* Helpers for triggering line events in a separate thread. */ -struct gpiod_test_event_thread; -typedef struct gpiod_test_event_thread GpiodTestEventThread; - -GpiodTestEventThread * -gpiod_test_start_event_thread(guint chip_index, - guint line_offset, guint period_ms); -void gpiod_test_stop_event_thread(GpiodTestEventThread *thread); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GpiodTestEventThread, - gpiod_test_stop_event_thread); +#define GPIOD_TEST_CASE(_name) \ + static void _gpiod_test_func_##_name(void); \ + static struct _gpiod_test_case _##_name##_test_case = { \ + .path = _GPIOD_TEST_PATH(_name), \ + .func = _gpiod_test_func_##_name, \ + }; \ + static __attribute__((constructor)) void \ + _##_name##_test_register(void) \ + { \ + _gpiod_test_register(&_##_name##_test_case); \ + } \ + static void _gpiod_test_func_##_name(void) #endif /* __GPIOD_TEST_H__ */ diff --git a/tests/gpiosim/gpiosim.c b/tests/gpiosim/gpiosim.c index 1429b7e..9a8aefd 100644 --- a/tests/gpiosim/gpiosim.c +++ b/tests/gpiosim/gpiosim.c @@ -736,6 +736,7 @@ static void bank_release(struct refcount *ref) unsigned int i; char buf[64]; + /* FIXME should be based on dirent because num_lines can change. */ for (i = 0; i < bank->num_lines; i++) { snprintf(buf, sizeof(buf), "line%u/hog", i); unlinkat(bank->cfs_dir_fd, buf, AT_REMOVEDIR); diff --git a/tests/mockup/Makefile.am b/tests/mockup/Makefile.am deleted file mode 100644 index 36cd397..0000000 --- a/tests/mockup/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> - -lib_LTLIBRARIES = libgpiomockup.la - -libgpiomockup_la_SOURCES = gpio-mockup.c gpio-mockup.h -libgpiomockup_la_CFLAGS = -Wall -Wextra -g -fvisibility=hidden -std=gnu89 -libgpiomockup_la_CFLAGS += -include $(top_builddir)/config.h -libgpiomockup_la_CFLAGS += $(KMOD_CFLAGS) $(UDEV_CFLAGS) -libgpiomockup_la_LDFLAGS = -version-info $(subst .,:,$(ABI_MOCKUP_VERSION)) -libgpiomockup_la_LDFLAGS += $(KMOD_LIBS) $(UDEV_LIBS) diff --git a/tests/mockup/gpio-mockup.c b/tests/mockup/gpio-mockup.c deleted file mode 100644 index eba26d3..0000000 --- a/tests/mockup/gpio-mockup.c +++ /dev/null @@ -1,496 +0,0 @@ -// SPDX-License-Identifier: LGPL-2.1-or-later -// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> - -#include <errno.h> -#include <libkmod.h> -#include <libudev.h> -#include <linux/version.h> -#include <poll.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/utsname.h> -#include <unistd.h> - -#include "gpio-mockup.h" - -#define EXPORT __attribute__((visibility("default"))) -/* - * The gpio-mockup features (including the debugfs interface) we're using - * in this library have first been released in the linux kernel version below. - */ -#define MIN_KERNEL_VERSION KERNEL_VERSION(5, 10, 0) - -struct gpio_mockup_chip { - char *name; - char *path; - unsigned int num; -}; - -struct gpio_mockup { - struct gpio_mockup_chip **chips; - unsigned int num_chips; - struct kmod_ctx *kmod; - struct kmod_module *module; - int refcount; -}; - -static void free_chip(struct gpio_mockup_chip *chip) -{ - free(chip->name); - free(chip->path); - free(chip); -} - -static bool check_kernel_version(void) -{ - unsigned int major, minor, release; - struct utsname un; - int rv; - - rv = uname(&un); - if (rv) - return false; - - rv = sscanf(un.release, "%u.%u.%u", &major, &minor, &release); - if (rv != 3) { - errno = EFAULT; - return false; - } - - if (KERNEL_VERSION(major, minor, release) < MIN_KERNEL_VERSION) { - errno = EOPNOTSUPP; - return false; - } - - return true; -} - -EXPORT struct gpio_mockup *gpio_mockup_new(void) -{ - struct gpio_mockup *ctx; - const char *modpath; - int rv; - - if (!check_kernel_version()) - goto err_out; - - ctx = malloc(sizeof(*ctx)); - if (!ctx) - goto err_out; - - memset(ctx, 0, sizeof(*ctx)); - ctx->refcount = 1; - - ctx->kmod = kmod_new(NULL, NULL); - if (!ctx->kmod) - goto err_free_kmod; - - rv = kmod_module_new_from_name(ctx->kmod, "gpio-mockup", &ctx->module); - if (rv) - goto err_unref_module; - - /* First see if we can find the module. */ - modpath = kmod_module_get_path(ctx->module); - if (!modpath) { - errno = ENOENT; - goto err_unref_module; - } - - /* - * Then see if we can freely load and unload it. If it's already - * loaded - no problem, we'll remove it next anyway. - */ - rv = kmod_module_probe_insert_module(ctx->module, - KMOD_PROBE_IGNORE_LOADED, - "gpio_mockup_ranges=-1,4", - NULL, NULL, NULL); - if (rv) - goto err_unref_module; - - /* We need to check that the gpio-mockup debugfs directory exists. */ - rv = access("/sys/kernel/debug/gpio-mockup", R_OK | W_OK); - if (rv) - goto err_unref_module; - - rv = kmod_module_remove_module(ctx->module, 0); - if (rv) - goto err_unref_module; - - return ctx; - -err_unref_module: - kmod_unref(ctx->kmod); -err_free_kmod: - free(ctx); -err_out: - return NULL; -} - -EXPORT void gpio_mockup_ref(struct gpio_mockup *ctx) -{ - ctx->refcount++; -} - -EXPORT void gpio_mockup_unref(struct gpio_mockup *ctx) -{ - ctx->refcount--; - - if (ctx->refcount == 0) { - if (ctx->chips) - gpio_mockup_remove(ctx); - - kmod_module_unref(ctx->module); - kmod_unref(ctx->kmod); - free(ctx); - } -} - -static char *make_module_param_string(unsigned int num_chips, - const unsigned int *num_lines, int flags) -{ - char *params, *new; - unsigned int i; - int rv; - - params = strdup("gpio_mockup_ranges="); - if (!params) - return NULL; - - for (i = 0; i < num_chips; i++) { - rv = asprintf(&new, "%s-1,%u,", params, num_lines[i]); - free(params); - if (rv < 0) - return NULL; - - params = new; - } - params[strlen(params) - 1] = '\0'; /* Remove the last comma. */ - - if (flags & GPIO_MOCKUP_FLAG_NAMED_LINES) { - rv = asprintf(&new, "%s gpio_mockup_named_lines", params); - free(params); - if (rv < 0) - return NULL; - - params = new; - } - - return params; -} - -static bool devpath_is_mockup(const char *devpath) -{ - static const char mockup_devpath[] = "/devices/platform/gpio-mockup"; - - return !strncmp(devpath, mockup_devpath, sizeof(mockup_devpath) - 1); -} - -static int chipcmp(const void *c1, const void *c2) -{ - const struct gpio_mockup_chip *chip1, *chip2; - - chip1 = *(const struct gpio_mockup_chip **)c1; - chip2 = *(const struct gpio_mockup_chip **)c2; - - return chip1->num > chip2->num; -} - -static struct gpio_mockup_chip *make_chip(const char *sysname, - const char *devnode) -{ - struct gpio_mockup_chip *chip; - int rv; - - chip = malloc(sizeof(*chip)); - if (!chip) - return NULL; - - chip->name = strdup(sysname); - if (!chip->name) { - free(chip); - return NULL; - } - - chip->path = strdup(devnode); - if (!chip->path) { - free(chip->name); - free(chip); - return NULL; - } - - rv = sscanf(sysname, "gpiochip%u", &chip->num); - if (rv != 1) { - errno = EINVAL; - free(chip->path); - free(chip->name); - free(chip); - return NULL; - } - - return chip; -} - -EXPORT int gpio_mockup_probe(struct gpio_mockup *ctx, unsigned int num_chips, - const unsigned int *chip_sizes, int flags) -{ - const char *devpath, *devnode, *sysname, *action; - struct gpio_mockup_chip *chip; - struct udev_monitor *monitor; - unsigned int i, detected = 0; - struct udev_device *dev; - struct udev *udev_ctx; - struct pollfd pfd; - char *params; - int rv; - - if (ctx->chips) { - errno = EBUSY; - goto err_out; - } - - if (num_chips < 1) { - errno = EINVAL; - goto err_out; - } - - udev_ctx = udev_new(); - if (!udev_ctx) - goto err_out; - - monitor = udev_monitor_new_from_netlink(udev_ctx, "udev"); - if (!monitor) - goto err_unref_udev; - - rv = udev_monitor_filter_add_match_subsystem_devtype(monitor, - "gpio", NULL); - if (rv < 0) - goto err_unref_monitor; - - rv = udev_monitor_enable_receiving(monitor); - if (rv < 0) - goto err_unref_monitor; - - params = make_module_param_string(num_chips, chip_sizes, flags); - if (!params) - goto err_unref_monitor; - - rv = kmod_module_probe_insert_module(ctx->module, - KMOD_PROBE_FAIL_ON_LOADED, - params, NULL, NULL, NULL); - free(params); - if (rv) - goto err_unref_monitor; - - ctx->chips = calloc(num_chips, sizeof(struct gpio_mockup_chip *)); - if (!ctx->chips) - goto err_remove_module; - - ctx->num_chips = num_chips; - - pfd.fd = udev_monitor_get_fd(monitor); - pfd.events = POLLIN | POLLPRI; - - while (num_chips > detected) { - rv = poll(&pfd, 1, 5000); - if (rv < 0) { - goto err_free_chips; - } if (rv == 0) { - errno = EAGAIN; - goto err_free_chips; - } - - dev = udev_monitor_receive_device(monitor); - if (!dev) - goto err_free_chips; - - devpath = udev_device_get_devpath(dev); - devnode = udev_device_get_devnode(dev); - sysname = udev_device_get_sysname(dev); - action = udev_device_get_action(dev); - - if (!devpath || !devnode || !sysname || - !devpath_is_mockup(devpath) || strcmp(action, "add") != 0) { - udev_device_unref(dev); - continue; - } - - chip = make_chip(sysname, devnode); - if (!chip) - goto err_free_chips; - - ctx->chips[detected++] = chip; - udev_device_unref(dev); - } - - udev_monitor_unref(monitor); - udev_unref(udev_ctx); - - /* - * We can't assume that the order in which the mockup gpiochip - * devices are created will be deterministic, yet we want the - * index passed to the test_chip_*() functions to correspond to the - * order in which the chips were defined in the TEST_DEFINE() - * macro. - * - * Once all gpiochips are there, sort them by chip number. - */ - qsort(ctx->chips, ctx->num_chips, sizeof(*ctx->chips), chipcmp); - - return 0; - -err_free_chips: - for (i = 0; i < detected; i++) - free_chip(ctx->chips[i]); - free(ctx->chips); -err_remove_module: - kmod_module_remove_module(ctx->module, 0); -err_unref_monitor: - udev_monitor_unref(monitor); -err_unref_udev: - udev_unref(udev_ctx); -err_out: - return -1; -} - -EXPORT int gpio_mockup_remove(struct gpio_mockup *ctx) -{ - unsigned int i; - int rv; - - if (!ctx->chips) { - errno = ENODEV; - return -1; - } - - rv = kmod_module_remove_module(ctx->module, 0); - if (rv) - return -1; - - for (i = 0; i < ctx->num_chips; i++) - free_chip(ctx->chips[i]); - free(ctx->chips); - ctx->chips = NULL; - ctx->num_chips = 0; - - return 0; -} - -static bool index_valid(struct gpio_mockup *ctx, unsigned int idx) -{ - if (!ctx->chips) { - errno = ENODEV; - return false; - } - - if (idx >= ctx->num_chips) { - errno = EINVAL; - return false; - } - - return true; -} - -EXPORT const char * -gpio_mockup_chip_name(struct gpio_mockup *ctx, unsigned int idx) -{ - if (!index_valid(ctx, idx)) - return NULL; - - return ctx->chips[idx]->name; -} - -EXPORT const char * -gpio_mockup_chip_path(struct gpio_mockup *ctx, unsigned int idx) -{ - if (!index_valid(ctx, idx)) - return NULL; - - return ctx->chips[idx]->path; -} - -EXPORT int gpio_mockup_chip_num(struct gpio_mockup *ctx, unsigned int idx) -{ - if (!index_valid(ctx, idx)) - return -1; - - return ctx->chips[idx]->num; -} - -static int debugfs_open(unsigned int chip_num, - unsigned int line_offset, int flags) -{ - char *path; - int fd, rv; - - rv = asprintf(&path, "/sys/kernel/debug/gpio-mockup/gpiochip%u/%u", - chip_num, line_offset); - if (rv < 0) - return -1; - - fd = open(path, flags); - free(path); - - return fd; -} - -EXPORT int gpio_mockup_get_value(struct gpio_mockup *ctx, - unsigned int chip_idx, - unsigned int line_offset) -{ - ssize_t rd; - char buf; - int fd; - - if (!index_valid(ctx, chip_idx)) - return -1; - - fd = debugfs_open(ctx->chips[chip_idx]->num, line_offset, O_RDONLY); - if (fd < 0) - return fd; - - rd = read(fd, &buf, 1); - close(fd); - if (rd < 0) - return rd; - if (rd != 1) { - errno = ENOTTY; - return -1; - } - if (buf != '0' && buf != '1') { - errno = EIO; - return -1; - } - - return buf == '0' ? 0 : 1; -} - -EXPORT int gpio_mockup_set_pull(struct gpio_mockup *ctx, - unsigned int chip_idx, - unsigned int line_offset, int pull) -{ - char buf[2]; - ssize_t wr; - int fd; - - if (!index_valid(ctx, chip_idx)) - return -1; - - fd = debugfs_open(ctx->chips[chip_idx]->num, line_offset, O_WRONLY); - if (fd < 0) - return fd; - - buf[0] = pull ? '1' : '0'; - buf[1] = '\n'; - - wr = write(fd, &buf, sizeof(buf)); - close(fd); - if (wr < 0) - return wr; - if (wr != sizeof(buf)) { - errno = EAGAIN; - return -1; - } - - return 0; -} diff --git a/tests/mockup/gpio-mockup.h b/tests/mockup/gpio-mockup.h deleted file mode 100644 index 4a55032..0000000 --- a/tests/mockup/gpio-mockup.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> */ - -#ifndef __GPIO_MOCKUP_H__ -#define __GPIO_MOCKUP_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -struct gpio_mockup; - -#define GPIO_MOCKUP_FLAG_NAMED_LINES (1 << 0) - -struct gpio_mockup *gpio_mockup_new(void); -void gpio_mockup_ref(struct gpio_mockup *ctx); -void gpio_mockup_unref(struct gpio_mockup *ctx); - -int gpio_mockup_probe(struct gpio_mockup *ctx, unsigned int num_chips, - const unsigned int *chip_sizes, int flags); -int gpio_mockup_remove(struct gpio_mockup *ctx); - -const char *gpio_mockup_chip_name(struct gpio_mockup *ctx, unsigned int idx); -const char *gpio_mockup_chip_path(struct gpio_mockup *ctx, unsigned int idx); -int gpio_mockup_chip_num(struct gpio_mockup *ctx, unsigned int idx); - -int gpio_mockup_get_value(struct gpio_mockup *ctx, - unsigned int chip_idx, unsigned int line_offset); -int gpio_mockup_set_pull(struct gpio_mockup *ctx, unsigned int chip_idx, - unsigned int line_offset, int pull); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* __GPIO_MOCKUP_H__ */ diff --git a/tests/tests-chip.c b/tests/tests-chip.c index 46fb8d2..09906e3 100644 --- a/tests/tests-chip.c +++ b/tests/tests-chip.c @@ -2,229 +2,171 @@ // SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> #include <errno.h> +#include <glib.h> +#include <gpiod.h> #include "gpiod-test.h" +#include "gpiod-test-helpers.h" +#include "gpiod-test-sim.h" #define GPIOD_TEST_GROUP "chip" -GPIOD_TEST_CASE(is_gpiochip_good, 0, { 8 }) +GPIOD_TEST_CASE(open_chip_good) { - g_assert_true(gpiod_is_gpiochip_device(gpiod_test_chip_path(0))); -} - -GPIOD_TEST_CASE(is_gpiochip_bad, 0, { 8 }) -{ - g_assert_false(gpiod_is_gpiochip_device("/dev/null")); -} + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; -GPIOD_TEST_CASE(is_gpiochip_nonexistent, 0, { 8 }) -{ - g_assert_false(gpiod_is_gpiochip_device("/dev/nonexistent_gpiochip")); + chip = gpiod_chip_open(g_gpiosim_chip_get_dev_path(sim)); + g_assert_nonnull(chip); } -GPIOD_TEST_CASE(open_good, 0, { 8 }) +GPIOD_TEST_CASE(open_chip_nonexistent) { - g_autoptr(gpiod_chip_struct) chip = NULL; + g_autoptr(struct_gpiod_chip) chip = NULL; - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); + chip = gpiod_chip_open("/dev/nonexistent"); + g_assert_null(chip); + gpiod_test_expect_errno(ENOENT); } -GPIOD_TEST_CASE(open_nonexistent, 0, { 8 }) +GPIOD_TEST_CASE(open_chip_not_a_character_device) { - struct gpiod_chip *chip; + g_autoptr(struct_gpiod_chip) chip = NULL; - chip = gpiod_chip_open("/dev/nonexistent_gpiochip"); + chip = gpiod_chip_open("/tmp"); g_assert_null(chip); - g_assert_cmpint(errno, ==, ENOENT); + gpiod_test_expect_errno(ENOTTY); } -GPIOD_TEST_CASE(open_notty, 0, { 8 }) +GPIOD_TEST_CASE(open_chip_not_a_gpio_device) { - struct gpiod_chip *chip; + g_autoptr(struct_gpiod_chip) chip = NULL; chip = gpiod_chip_open("/dev/null"); g_assert_null(chip); - g_assert_cmpint(errno, ==, ENOTTY); + gpiod_test_expect_errno(ENODEV); } -GPIOD_TEST_CASE(get_name, 0, { 8, 8, 8}) +GPIOD_TEST_CASE(get_chip_name) { - g_autoptr(gpiod_chip_struct) chip0 = NULL; - g_autoptr(gpiod_chip_struct) chip1 = NULL; - g_autoptr(gpiod_chip_struct) chip2 = NULL; - - chip0 = gpiod_chip_open(gpiod_test_chip_path(0)); - chip1 = gpiod_chip_open(gpiod_test_chip_path(1)); - chip2 = gpiod_chip_open(gpiod_test_chip_path(2)); - - g_assert_nonnull(chip0); - g_assert_nonnull(chip1); - g_assert_nonnull(chip2); - gpiod_test_return_if_failed(); - - g_assert_cmpstr(gpiod_chip_get_name(chip0), ==, - gpiod_test_chip_name(0)); - g_assert_cmpstr(gpiod_chip_get_name(chip1), ==, - gpiod_test_chip_name(1)); - g_assert_cmpstr(gpiod_chip_get_name(chip2), ==, - gpiod_test_chip_name(2)); + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + + g_assert_cmpstr(gpiod_chip_get_name(chip), ==, + g_gpiosim_chip_get_name(sim)); } -GPIOD_TEST_CASE(get_label, 0, { 8, 8, 8}) +GPIOD_TEST_CASE(get_chip_label) { - g_autoptr(gpiod_chip_struct) chip0 = NULL; - g_autoptr(gpiod_chip_struct) chip1 = NULL; - g_autoptr(gpiod_chip_struct) chip2 = NULL; - - chip0 = gpiod_chip_open(gpiod_test_chip_path(0)); - chip1 = gpiod_chip_open(gpiod_test_chip_path(1)); - chip2 = gpiod_chip_open(gpiod_test_chip_path(2)); - - g_assert_nonnull(chip0); - g_assert_nonnull(chip1); - g_assert_nonnull(chip2); - gpiod_test_return_if_failed(); - - g_assert_cmpstr(gpiod_chip_get_label(chip0), ==, "gpio-mockup-A"); - g_assert_cmpstr(gpiod_chip_get_label(chip1), ==, "gpio-mockup-B"); - g_assert_cmpstr(gpiod_chip_get_label(chip2), ==, "gpio-mockup-C"); + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("label", "foobar", + NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + + g_assert_cmpstr(gpiod_chip_get_label(chip), ==, "foobar"); } -GPIOD_TEST_CASE(num_lines, 0, { 1, 4, 8, 16, 32 }) +GPIOD_TEST_CASE(get_chip_path) { - g_autoptr(gpiod_chip_struct) chip0 = NULL; - g_autoptr(gpiod_chip_struct) chip1 = NULL; - g_autoptr(gpiod_chip_struct) chip2 = NULL; - g_autoptr(gpiod_chip_struct) chip3 = NULL; - g_autoptr(gpiod_chip_struct) chip4 = NULL; - - chip0 = gpiod_chip_open(gpiod_test_chip_path(0)); - chip1 = gpiod_chip_open(gpiod_test_chip_path(1)); - chip2 = gpiod_chip_open(gpiod_test_chip_path(2)); - chip3 = gpiod_chip_open(gpiod_test_chip_path(3)); - chip4 = gpiod_chip_open(gpiod_test_chip_path(4)); - - g_assert_nonnull(chip0); - g_assert_nonnull(chip1); - g_assert_nonnull(chip2); - g_assert_nonnull(chip3); - g_assert_nonnull(chip4); - gpiod_test_return_if_failed(); - - g_assert_cmpuint(gpiod_chip_get_num_lines(chip0), ==, 1); - g_assert_cmpuint(gpiod_chip_get_num_lines(chip1), ==, 4); - g_assert_cmpuint(gpiod_chip_get_num_lines(chip2), ==, 8); - g_assert_cmpuint(gpiod_chip_get_num_lines(chip3), ==, 16); - g_assert_cmpuint(gpiod_chip_get_num_lines(chip4), ==, 32); + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + const gchar *path = g_gpiosim_chip_get_dev_path(sim); + + chip = gpiod_test_open_chip_or_fail(path); + + g_assert_cmpstr(gpiod_chip_get_path(chip), ==, path); } -GPIOD_TEST_CASE(get_line, 0, { 16 }) +GPIOD_TEST_CASE(get_num_lines) { - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line *line; + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 16, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); - line = gpiod_chip_get_line(chip, 3); - g_assert_nonnull(line); - g_assert_cmpuint(gpiod_line_offset(line), ==, 3); + g_assert_cmpuint(gpiod_chip_get_num_lines(chip), ==, 16); } -GPIOD_TEST_CASE(get_lines, 0, { 16 }) +GPIOD_TEST_CASE(get_fd) { - struct gpiod_line *line0, *line1, *line2, *line3; - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - guint offsets[4]; + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); - - offsets[0] = 1; - offsets[1] = 3; - offsets[2] = 4; - offsets[3] = 7; - - bulk = gpiod_chip_get_lines(chip, offsets, 4); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - g_assert_cmpuint(gpiod_line_bulk_num_lines(bulk), ==, 4); - gpiod_test_return_if_failed(); - - line0 = gpiod_line_bulk_get_line(bulk, 0); - line1 = gpiod_line_bulk_get_line(bulk, 1); - line2 = gpiod_line_bulk_get_line(bulk, 2); - line3 = gpiod_line_bulk_get_line(bulk, 3); - - g_assert_cmpuint(gpiod_line_offset(line0), ==, 1); - g_assert_cmpuint(gpiod_line_offset(line1), ==, 3); - g_assert_cmpuint(gpiod_line_offset(line2), ==, 4); - g_assert_cmpuint(gpiod_line_offset(line3), ==, 7); + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + + g_assert_cmpint(gpiod_chip_get_fd(chip), >=, 0); } -GPIOD_TEST_CASE(get_all_lines, 0, { 4 }) +GPIOD_TEST_CASE(find_line_bad) { - struct gpiod_line *line0, *line1, *line2, *line3; - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); - - bulk = gpiod_chip_get_all_lines(chip); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - g_assert_cmpuint(gpiod_line_bulk_num_lines(bulk), ==, 4); - gpiod_test_return_if_failed(); - - line0 = gpiod_line_bulk_get_line(bulk, 0); - line1 = gpiod_line_bulk_get_line(bulk, 1); - line2 = gpiod_line_bulk_get_line(bulk, 2); - line3 = gpiod_line_bulk_get_line(bulk, 3); - - g_assert_cmpuint(gpiod_line_offset(line0), ==, 0); - g_assert_cmpuint(gpiod_line_offset(line1), ==, 1); - g_assert_cmpuint(gpiod_line_offset(line2), ==, 2); - g_assert_cmpuint(gpiod_line_offset(line3), ==, 3); + static const struct gpiod_test_line_name names[] = { + { .offset = 1, .name = "foo", }, + { .offset = 2, .name = "bar", }, + { .offset = 4, .name = "baz", }, + { .offset = 5, .name = "xyz", }, + { } + }; + + g_autoptr(GPIOSimChip) sim = NULL; + g_autoptr(struct_gpiod_chip) chip = NULL; + + sim = g_gpiosim_chip_new( + "num-lines", 8, + "line-names", gpiod_test_package_line_names(names), + NULL); + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + + g_assert_cmpint(gpiod_chip_find_line(chip, "nonexistent"), ==, -1); + gpiod_test_expect_errno(ENOENT); } -GPIOD_TEST_CASE(find_line_good, GPIOD_TEST_FLAG_NAMED_LINES, { 8, 8, 8 }) +GPIOD_TEST_CASE(find_line_good) { - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line *line; - int offset; + static const struct gpiod_test_line_name names[] = { + { .offset = 1, .name = "foo", }, + { .offset = 2, .name = "bar", }, + { .offset = 4, .name = "baz", }, + { .offset = 5, .name = "xyz", }, + { } + }; - chip = gpiod_chip_open(gpiod_test_chip_path(1)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); + g_autoptr(GPIOSimChip) sim = NULL; + g_autoptr(struct_gpiod_chip) chip = NULL; - offset = gpiod_chip_find_line(chip, "gpio-mockup-B-4"); - g_assert_cmpint(offset, ==, 4); - gpiod_test_return_if_failed(); + sim = g_gpiosim_chip_new( + "num-lines", 8, + "line-names", gpiod_test_package_line_names(names), + NULL); - line = gpiod_chip_get_line(chip, 4); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); - g_assert_cmpstr(gpiod_line_name(line), ==, "gpio-mockup-B-4"); + g_assert_cmpint(gpiod_chip_find_line(chip, "baz"), ==, 4); } -GPIOD_TEST_CASE(find_line_unique_not_found, - GPIOD_TEST_FLAG_NAMED_LINES, { 8, 8, 8 }) +/* Verify that for duplicated line names, the first one is returned. */ +GPIOD_TEST_CASE(find_line_duplicate) { - g_autoptr(gpiod_chip_struct) chip = NULL; - int offset; + static const struct gpiod_test_line_name names[] = { + { .offset = 1, .name = "foo", }, + { .offset = 2, .name = "baz", }, + { .offset = 4, .name = "baz", }, + { .offset = 5, .name = "xyz", }, + { } + }; - chip = gpiod_chip_open(gpiod_test_chip_path(1)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); + g_autoptr(GPIOSimChip) sim = NULL; + g_autoptr(struct_gpiod_chip) chip = NULL; + + sim = g_gpiosim_chip_new( + "num-lines", 8, + "line-names", gpiod_test_package_line_names(names), + NULL); + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); - offset = gpiod_chip_find_line(chip, "nonexistent"); - g_assert_cmpint(offset, ==, -1); - g_assert_cmpint(errno, ==, ENOENT); + g_assert_cmpint(gpiod_chip_find_line(chip, "baz"), ==, 2); } diff --git a/tests/tests-edge-event.c b/tests/tests-edge-event.c new file mode 100644 index 0000000..8e3fb62 --- /dev/null +++ b/tests/tests-edge-event.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> + +#include <glib.h> +#include <gpiod.h> + +#include "gpiod-test.h" +#include "gpiod-test-helpers.h" +#include "gpiod-test-sim.h" + +#define GPIOD_TEST_GROUP "edge-event" + +GPIOD_TEST_CASE(edge_event_buffer_capacity) +{ + g_autoptr(struct_gpiod_edge_event_buffer) buffer = NULL; + + buffer = gpiod_test_create_edge_event_buffer_or_fail(32); + + g_assert_cmpuint(gpiod_edge_event_buffer_get_capacity(buffer), ==, 32); +} + +GPIOD_TEST_CASE(edge_event_buffer_max_capacity) +{ + g_autoptr(struct_gpiod_edge_event_buffer) buffer = NULL; + + buffer = gpiod_test_create_edge_event_buffer_or_fail(16 * 64 * 2); + + g_assert_cmpuint(gpiod_edge_event_buffer_get_capacity(buffer), + ==, 16 * 64); +} + +GPIOD_TEST_CASE(edge_event_wait_timeout) +{ + static const guint offset = 4; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + gint ret; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + gpiod_line_config_set_edge_detection_default(line_cfg, + GPIOD_LINE_EDGE_BOTH); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + ret = gpiod_line_request_edge_event_wait(request, 1000000); + g_assert_cmpint(ret, ==, 0); +} + +GPIOD_TEST_CASE(cannot_request_lines_in_output_mode_with_edge_detection) +{ + static const guint offset = 4; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + gpiod_line_config_set_edge_detection_default(line_cfg, + GPIOD_LINE_EDGE_BOTH); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT); + + request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); + g_assert_null(request); + gpiod_test_expect_errno(EINVAL); +} + +static gpointer falling_and_rising_edge_events(gpointer data) +{ + GPIOSimChip *sim = data; + + g_usleep(50); + + g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_UP); + + g_usleep(50); + + g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_DOWN); + + return NULL; +} + +GPIOD_TEST_CASE(read_both_events) +{ + static const guint offset = 2; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(GThread) thread = NULL; + g_autoptr(struct_gpiod_edge_event_buffer) buffer = NULL; + struct gpiod_edge_event *event; + guint64 ts_rising, ts_falling; + gint ret; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + buffer = gpiod_test_create_edge_event_buffer_or_fail(64); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_INPUT); + gpiod_line_config_set_edge_detection_default(line_cfg, + GPIOD_LINE_EDGE_BOTH); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + thread = g_thread_new("request-release", + falling_and_rising_edge_events, sim); + g_thread_ref(thread); + + /* First event. */ + + ret = gpiod_line_request_edge_event_wait(request, 1000000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_join_thread_and_return_if_failed(thread); + + ret = gpiod_line_request_edge_event_read(request, buffer, 1); + g_assert_cmpint(ret, ==, 1); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpuint(gpiod_edge_event_buffer_get_num_events(buffer), ==, 1); + event = gpiod_edge_event_buffer_get_event(buffer, 0); + g_assert_nonnull(event); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpint(gpiod_edge_event_get_event_type(event), + ==, GPIOD_EDGE_EVENT_RISING_EDGE); + g_assert_cmpuint(gpiod_edge_event_get_line_offset(event), ==, 2); + ts_rising = gpiod_edge_event_get_timestamp(event); + + /* Second event. */ + + ret = gpiod_line_request_edge_event_wait(request, 1000000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_join_thread_and_return_if_failed(thread); + + ret = gpiod_line_request_edge_event_read(request, buffer, 1); + g_assert_cmpint(ret, ==, 1); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpuint(gpiod_edge_event_buffer_get_num_events(buffer), ==, 1); + event = gpiod_edge_event_buffer_get_event(buffer, 0); + g_assert_nonnull(event); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpint(gpiod_edge_event_get_event_type(event), + ==, GPIOD_EDGE_EVENT_FALLING_EDGE); + g_assert_cmpuint(gpiod_edge_event_get_line_offset(event), ==, 2); + ts_falling = gpiod_edge_event_get_timestamp(event); + + g_thread_join(thread); + + g_assert_cmpuint(ts_falling, >, ts_rising); +} + +GPIOD_TEST_CASE(read_rising_edge_event) +{ + static const guint offset = 2; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(GThread) thread = NULL; + g_autoptr(struct_gpiod_edge_event_buffer) buffer = NULL; + struct gpiod_edge_event *event; + gint ret; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + buffer = gpiod_test_create_edge_event_buffer_or_fail(64); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_INPUT); + gpiod_line_config_set_edge_detection_default(line_cfg, + GPIOD_LINE_EDGE_RISING); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + thread = g_thread_new("request-release", + falling_and_rising_edge_events, sim); + g_thread_ref(thread); + + /* First event. */ + + ret = gpiod_line_request_edge_event_wait(request, 1000000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_join_thread_and_return_if_failed(thread); + + ret = gpiod_line_request_edge_event_read(request, buffer, 1); + g_assert_cmpint(ret, ==, 1); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpuint(gpiod_edge_event_buffer_get_num_events(buffer), ==, 1); + event = gpiod_edge_event_buffer_get_event(buffer, 0); + g_assert_nonnull(event); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpint(gpiod_edge_event_get_event_type(event), + ==, GPIOD_EDGE_EVENT_RISING_EDGE); + g_assert_cmpuint(gpiod_edge_event_get_line_offset(event), ==, 2); + + /* Second event. */ + + ret = gpiod_line_request_edge_event_wait(request, 1000000); + g_assert_cmpint(ret, ==, 0); /* Time-out. */ + + g_thread_join(thread); +} + +GPIOD_TEST_CASE(read_falling_edge_event) +{ + static const guint offset = 2; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(GThread) thread = NULL; + g_autoptr(struct_gpiod_edge_event_buffer) buffer = NULL; + struct gpiod_edge_event *event; + gint ret; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + buffer = gpiod_test_create_edge_event_buffer_or_fail(64); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_INPUT); + gpiod_line_config_set_edge_detection_default(line_cfg, + GPIOD_LINE_EDGE_FALLING); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + thread = g_thread_new("request-release", + falling_and_rising_edge_events, sim); + g_thread_ref(thread); + + /* First event is the second generated. */ + + ret = gpiod_line_request_edge_event_wait(request, 1000000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_join_thread_and_return_if_failed(thread); + + ret = gpiod_line_request_edge_event_read(request, buffer, 1); + g_assert_cmpint(ret, ==, 1); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpuint(gpiod_edge_event_buffer_get_num_events(buffer), ==, 1); + event = gpiod_edge_event_buffer_get_event(buffer, 0); + g_assert_nonnull(event); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpint(gpiod_edge_event_get_event_type(event), + ==, GPIOD_EDGE_EVENT_FALLING_EDGE); + g_assert_cmpuint(gpiod_edge_event_get_line_offset(event), ==, 2); + + /* No more events. */ + + ret = gpiod_line_request_edge_event_wait(request, 1000000); + g_assert_cmpint(ret, ==, 0); /* Time-out. */ + + g_thread_join(thread); +} + +static gpointer rising_edge_events_on_two_offsets(gpointer data) +{ + GPIOSimChip *sim = data; + + g_usleep(50); + + g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_UP); + + g_usleep(50); + + g_gpiosim_chip_set_pull(sim, 3, G_GPIOSIM_PULL_UP); + + return NULL; +} + +GPIOD_TEST_CASE(seqno) +{ + static const guint offsets[] = { 2, 3 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(GThread) thread = NULL; + g_autoptr(struct_gpiod_edge_event_buffer) buffer = NULL; + struct gpiod_edge_event *event; + gint ret; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + buffer = gpiod_test_create_edge_event_buffer_or_fail(64); + + gpiod_request_config_set_offsets(req_cfg, 2, offsets); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_INPUT); + gpiod_line_config_set_edge_detection_default(line_cfg, + GPIOD_LINE_EDGE_BOTH); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + thread = g_thread_new("request-release", + rising_edge_events_on_two_offsets, sim); + g_thread_ref(thread); + + /* First event. */ + + ret = gpiod_line_request_edge_event_wait(request, 1000000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_join_thread_and_return_if_failed(thread); + + ret = gpiod_line_request_edge_event_read(request, buffer, 1); + g_assert_cmpint(ret, ==, 1); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpuint(gpiod_edge_event_buffer_get_num_events(buffer), ==, 1); + event = gpiod_edge_event_buffer_get_event(buffer, 0); + g_assert_nonnull(event); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpuint(gpiod_edge_event_get_line_offset(event), ==, 2); + g_assert_cmpuint(gpiod_edge_event_get_global_seqno(event), ==, 1); + g_assert_cmpuint(gpiod_edge_event_get_line_seqno(event), ==, 1); + + /* Second event. */ + + ret = gpiod_line_request_edge_event_wait(request, 1000000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_join_thread_and_return_if_failed(thread); + + ret = gpiod_line_request_edge_event_read(request, buffer, 1); + g_assert_cmpint(ret, ==, 1); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpuint(gpiod_edge_event_buffer_get_num_events(buffer), ==, 1); + event = gpiod_edge_event_buffer_get_event(buffer, 0); + g_assert_nonnull(event); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpuint(gpiod_edge_event_get_line_offset(event), ==, 3); + g_assert_cmpuint(gpiod_edge_event_get_global_seqno(event), ==, 2); + g_assert_cmpuint(gpiod_edge_event_get_line_seqno(event), ==, 1); +} + +GPIOD_TEST_CASE(event_copy) +{ + static const guint offset = 2; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(GThread) thread = NULL; + g_autoptr(struct_gpiod_edge_event_buffer) buffer = NULL; + g_autoptr(struct_gpiod_edge_event) copy = NULL; + struct gpiod_edge_event *event; + gint ret; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + buffer = gpiod_test_create_edge_event_buffer_or_fail(64); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_INPUT); + gpiod_line_config_set_edge_detection_default(line_cfg, + GPIOD_LINE_EDGE_BOTH); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_UP); + + ret = gpiod_line_request_edge_event_wait(request, 1000000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_return_if_failed(); + + ret = gpiod_line_request_edge_event_read(request, buffer, 1); + g_assert_cmpint(ret, ==, 1); + gpiod_test_return_if_failed(); + + event = gpiod_edge_event_buffer_get_event(buffer, 0); + g_assert_nonnull(event); + gpiod_test_return_if_failed(); + + copy = gpiod_edge_event_copy(event); + g_assert_nonnull(copy); + g_assert_true(copy != event); +} diff --git a/tests/tests-event.c b/tests/tests-event.c deleted file mode 100644 index 53d3e8c..0000000 --- a/tests/tests-event.c +++ /dev/null @@ -1,908 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> - -#include <errno.h> -#include <unistd.h> - -#include "gpiod-test.h" - -#define GPIOD_TEST_GROUP "event" - -GPIOD_TEST_CASE(rising_edge_good, 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_rising_edge_events(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - 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); -} - -GPIOD_TEST_CASE(falling_edge_good, 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_falling_edge_events(line, - GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - 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); -} - -GPIOD_TEST_CASE(rising_edge_ignore_falling, 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[3]; - 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_rising_edge_events(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - 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[0]); - g_assert_cmpint(ret, ==, 0); - - ret = gpiod_line_event_wait(line, &ts); - g_assert_cmpint(ret, ==, 1); - ret = gpiod_line_event_read(line, &ev[1]); - g_assert_cmpint(ret, ==, 0); - - ret = gpiod_line_event_wait(line, &ts); - g_assert_cmpint(ret, ==, 1); - ret = gpiod_line_event_read(line, &ev[2]); - g_assert_cmpint(ret, ==, 0); - - g_assert_cmpint(ev[0].event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE); - g_assert_cmpint(ev[1].event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE); - g_assert_cmpint(ev[2].event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE); -} - -GPIOD_TEST_CASE(both_edges, 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(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - 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_active_low, 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_ACTIVE_LOW); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - 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(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_DISABLED); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - 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); - gpiod_test_return_if_failed(); - - 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); - gpiod_test_return_if_failed(); - - 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; - 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_falling_edge_events_flags(line, - GPIOD_TEST_CONSUMER, GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - 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); -} - -GPIOD_TEST_CASE(get_value, 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(); - - gpiod_test_chip_set_pull(0, 7, 1); - - ret = gpiod_line_request_falling_edge_events(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - ret = gpiod_line_get_value(line); - g_assert_cmpint(ret, ==, 1); - - 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); -} - -GPIOD_TEST_CASE(get_value_active_low, 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(); - - gpiod_test_chip_set_pull(0, 7, 1); - - ret = gpiod_line_request_falling_edge_events_flags(line, - GPIOD_TEST_CONSUMER, GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - ret = gpiod_line_get_value(line); - 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); -} - -GPIOD_TEST_CASE(get_values, 0, { 8 }) -{ - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line *line; - gint i, ret, vals[8]; - - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); - - bulk = gpiod_line_bulk_new(8); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - - for (i = 0; i < 8; i++) { - line = gpiod_chip_get_line(chip, i); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - gpiod_line_bulk_add_line(bulk, line); - gpiod_test_chip_set_pull(0, i, 1); - } - - ret = gpiod_line_request_bulk_rising_edge_events(bulk, - GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - memset(vals, 0, sizeof(vals)); - ret = gpiod_line_get_value_bulk(bulk, vals); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpint(vals[0], ==, 1); - g_assert_cmpint(vals[1], ==, 1); - g_assert_cmpint(vals[2], ==, 1); - g_assert_cmpint(vals[3], ==, 1); - g_assert_cmpint(vals[4], ==, 1); - g_assert_cmpint(vals[5], ==, 1); - g_assert_cmpint(vals[6], ==, 1); - g_assert_cmpint(vals[7], ==, 1); - - gpiod_test_chip_set_pull(0, 1, 0); - gpiod_test_chip_set_pull(0, 3, 0); - gpiod_test_chip_set_pull(0, 4, 0); - gpiod_test_chip_set_pull(0, 7, 0); - - memset(vals, 0, sizeof(vals)); - ret = gpiod_line_get_value_bulk(bulk, vals); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpint(vals[0], ==, 1); - g_assert_cmpint(vals[1], ==, 0); - g_assert_cmpint(vals[2], ==, 1); - g_assert_cmpint(vals[3], ==, 0); - g_assert_cmpint(vals[4], ==, 0); - g_assert_cmpint(vals[5], ==, 1); - g_assert_cmpint(vals[6], ==, 1); - g_assert_cmpint(vals[7], ==, 0); -} - -GPIOD_TEST_CASE(get_values_active_low, 0, { 8 }) -{ - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line *line; - gint i, ret, vals[8]; - - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); - - bulk = gpiod_line_bulk_new(8); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - - for (i = 0; i < 8; i++) { - line = gpiod_chip_get_line(chip, i); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - gpiod_line_bulk_add_line(bulk, line); - gpiod_test_chip_set_pull(0, i, 0); - } - - ret = gpiod_line_request_bulk_rising_edge_events_flags(bulk, - GPIOD_TEST_CONSUMER, GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - memset(vals, 0, sizeof(vals)); - ret = gpiod_line_get_value_bulk(bulk, vals); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpint(vals[0], ==, 1); - g_assert_cmpint(vals[1], ==, 1); - g_assert_cmpint(vals[2], ==, 1); - g_assert_cmpint(vals[3], ==, 1); - g_assert_cmpint(vals[4], ==, 1); - g_assert_cmpint(vals[5], ==, 1); - g_assert_cmpint(vals[6], ==, 1); - g_assert_cmpint(vals[7], ==, 1); - - gpiod_test_chip_set_pull(0, 1, 1); - gpiod_test_chip_set_pull(0, 3, 1); - gpiod_test_chip_set_pull(0, 4, 1); - gpiod_test_chip_set_pull(0, 7, 1); - - memset(vals, 0, sizeof(vals)); - ret = gpiod_line_get_value_bulk(bulk, vals); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpint(vals[0], ==, 1); - g_assert_cmpint(vals[1], ==, 0); - g_assert_cmpint(vals[2], ==, 1); - g_assert_cmpint(vals[3], ==, 0); - g_assert_cmpint(vals[4], ==, 0); - g_assert_cmpint(vals[5], ==, 1); - g_assert_cmpint(vals[6], ==, 1); - g_assert_cmpint(vals[7], ==, 0); -} - -GPIOD_TEST_CASE(wait_multiple, 0, { 8 }) -{ - g_autoptr(GpiodTestEventThread) ev_thread = NULL; - g_autoptr(gpiod_line_bulk_struct) ev_bulk = NULL; - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - struct timespec ts = { 1, 0 }; - struct gpiod_line_event ev; - struct gpiod_line *line; - gint ret, i; - - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); - - bulk = gpiod_line_bulk_new(8); - ev_bulk = gpiod_line_bulk_new(8); - g_assert_nonnull(bulk); - g_assert_nonnull(ev_bulk); - gpiod_test_return_if_failed(); - - for (i = 1; i < 8; i++) { - line = gpiod_chip_get_line(chip, i); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - gpiod_line_bulk_add_line(bulk, line); - } - - ret = gpiod_line_request_bulk_rising_edge_events(bulk, - GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - ev_thread = gpiod_test_start_event_thread(0, 4, 100); - - ret = gpiod_line_event_wait_bulk(bulk, &ts, ev_bulk); - g_assert_cmpint(ret, ==, 1); - - g_assert_cmpuint(gpiod_line_bulk_num_lines(ev_bulk), ==, 1); - line = gpiod_line_bulk_get_line(ev_bulk, 0); - g_assert_cmpuint(gpiod_line_offset(line), ==, 4); - - ret = gpiod_line_event_read(line, &ev); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE); - g_assert_cmpint(ev.offset, ==, 4); -} - -GPIOD_TEST_CASE(get_fd_when_values_requested, 0, { 8 }) -{ - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line *line; - gint ret, fd; - - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); - - line = gpiod_chip_get_line(chip, 3); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - ret = gpiod_line_request_input(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - fd = gpiod_line_event_get_fd(line); - g_assert_cmpint(fd, ==, -1); - g_assert_cmpint(errno, ==, EPERM); -} - -GPIOD_TEST_CASE(request_bulk_fail, 0, { 8 }) -{ - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line *line; - gint ret, i; - - 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_input(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - bulk = gpiod_line_bulk_new(8); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - - for (i = 0; i < 8; i++) { - line = gpiod_chip_get_line(chip, i); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - gpiod_line_bulk_add_line(bulk, line); - } - - ret = gpiod_line_request_bulk_both_edges_events(bulk, - GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, -1); - g_assert_cmpint(errno, ==, EBUSY); -} - -GPIOD_TEST_CASE(invalid_fd, 0, { 8 }) -{ - g_autoptr(gpiod_line_bulk_struct) ev_bulk = NULL; - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - struct timespec ts = { 1, 0 }; - struct gpiod_line *line; - gint ret, fd; - - 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(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - fd = gpiod_line_event_get_fd(line); - close(fd); - - ret = gpiod_line_event_wait(line, &ts); - g_assert_cmpint(ret, ==, -1); - g_assert_cmpint(errno, ==, EINVAL); - - bulk = gpiod_line_bulk_new(1); - ev_bulk = gpiod_line_bulk_new(1); - g_assert_nonnull(bulk); - g_assert_nonnull(ev_bulk); - - /* - * The single line variant calls gpiod_line_event_wait_bulk() with the - * event_bulk argument set to NULL, so test this use case explicitly - * as well. - */ - gpiod_line_bulk_add_line(bulk, line); - ret = gpiod_line_event_wait_bulk(bulk, &ts, ev_bulk); - g_assert_cmpint(ret, ==, -1); - g_assert_cmpint(errno, ==, EINVAL); -} - -GPIOD_TEST_CASE(read_events_individually, 0, { 8 }) -{ - g_autoptr(gpiod_chip_struct) chip = NULL; - struct timespec ts = { 1, 0 }; - struct gpiod_line_event ev; - struct gpiod_line *line; - gint ret; - guint i; - - 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); - gpiod_test_return_if_failed(); - - /* generate multiple events */ - for (i = 0; i < 3; i++) { - gpiod_test_chip_set_pull(0, 7, i & 1); - usleep(10000); - } - - /* read them individually... */ - ret = gpiod_line_event_wait(line, &ts); - g_assert_cmpint(ret, ==, 1); - if (!ret) - return; - - 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); - if (!ret) - return; - - 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); - if (!ret) - return; - - 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, ==, 0); -} - -GPIOD_TEST_CASE(read_multiple_events, 0, { 8 }) -{ - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line_event events[5]; - struct timespec ts = { 1, 0 }; - struct gpiod_line *line; - gint ret; - guint i; - - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); - - line = gpiod_chip_get_line(chip, 4); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - ret = gpiod_line_request_both_edges_events(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - /* generate multiple events */ - for (i = 0; i < 7; i++) { - gpiod_test_chip_set_pull(0, 4, !(i & 1)); - /* - * We sleep for a short period of time here and in other - * test cases for multiple events to let the kernel service - * each simulated interrupt. Otherwise we'd risk triggering - * an interrupt while the previous one is still being - * handled. - */ - usleep(10000); - } - - ret = gpiod_line_event_wait(line, &ts); - g_assert_cmpint(ret, ==, 1); - if (!ret) - return; - - /* read a chunk */ - ret = gpiod_line_event_read_multiple(line, events, 3); - g_assert_cmpint(ret, ==, 3); - - g_assert_cmpint(events[0].event_type, ==, - GPIOD_LINE_EVENT_RISING_EDGE); - g_assert_cmpint(events[1].event_type, ==, - GPIOD_LINE_EVENT_FALLING_EDGE); - g_assert_cmpint(events[2].event_type, ==, - GPIOD_LINE_EVENT_RISING_EDGE); - - ret = gpiod_line_event_wait(line, &ts); - g_assert_cmpint(ret, ==, 1); - if (!ret) - return; - - /* - * read the remainder - * - note the attempt to read more than are available - */ - ret = gpiod_line_event_read_multiple(line, events, 5); - g_assert_cmpint(ret, ==, 4); - - g_assert_cmpint(events[0].event_type, ==, - GPIOD_LINE_EVENT_FALLING_EDGE); - g_assert_cmpint(events[1].event_type, ==, - GPIOD_LINE_EVENT_RISING_EDGE); - g_assert_cmpint(events[2].event_type, ==, - GPIOD_LINE_EVENT_FALLING_EDGE); - g_assert_cmpint(events[3].event_type, ==, - GPIOD_LINE_EVENT_RISING_EDGE); - - ret = gpiod_line_event_wait(line, &ts); - g_assert_cmpint(ret, ==, 0); -} - -GPIOD_TEST_CASE(read_multiple_events_fd, 0, { 8 }) -{ - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line_event events[5]; - struct timespec ts = { 1, 0 }; - struct gpiod_line *line; - gint ret, fd; - guint i; - - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); - - line = gpiod_chip_get_line(chip, 4); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - ret = gpiod_line_request_both_edges_events(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - /* generate multiple events */ - for (i = 0; i < 7; i++) { - gpiod_test_chip_set_pull(0, 4, !(i & 1)); - usleep(10000); - } - - ret = gpiod_line_event_wait(line, &ts); - g_assert_cmpint(ret, ==, 1); - if (!ret) - return; - - fd = gpiod_line_event_get_fd(line); - g_assert_cmpint(fd, >=, 0); - - /* read a chunk */ - ret = gpiod_line_event_read_fd_multiple(fd, events, 3); - g_assert_cmpint(ret, ==, 3); - - g_assert_cmpint(events[0].event_type, ==, - GPIOD_LINE_EVENT_RISING_EDGE); - g_assert_cmpint(events[1].event_type, ==, - GPIOD_LINE_EVENT_FALLING_EDGE); - g_assert_cmpint(events[2].event_type, ==, - GPIOD_LINE_EVENT_RISING_EDGE); - - ret = gpiod_line_event_wait(line, &ts); - g_assert_cmpint(ret, ==, 1); - if (!ret) - return; - - /* - * read the remainder - * - note the attempt to read more than are available - */ - ret = gpiod_line_event_read_fd_multiple(fd, events, 5); - g_assert_cmpint(ret, ==, 4); - - g_assert_cmpint(events[0].event_type, ==, - GPIOD_LINE_EVENT_FALLING_EDGE); - g_assert_cmpint(events[1].event_type, ==, - GPIOD_LINE_EVENT_RISING_EDGE); - g_assert_cmpint(events[2].event_type, ==, - GPIOD_LINE_EVENT_FALLING_EDGE); - g_assert_cmpint(events[3].event_type, ==, - GPIOD_LINE_EVENT_RISING_EDGE); - - ret = gpiod_line_event_wait(line, &ts); - g_assert_cmpint(ret, ==, 0); -} diff --git a/tests/tests-info-event.c b/tests/tests-info-event.c new file mode 100644 index 0000000..8129f16 --- /dev/null +++ b/tests/tests-info-event.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> + +#include <glib.h> +#include <gpiod.h> +#include <poll.h> + +#include "gpiod-test.h" +#include "gpiod-test-helpers.h" +#include "gpiod-test-sim.h" + +#define GPIOD_TEST_GROUP "info-event" + +GPIOD_TEST_CASE(watching_info_events_returns_line_info) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + + info = gpiod_chip_watch_line_info(chip, 3); + g_assert_nonnull(info); + g_assert_cmpuint(gpiod_line_info_get_offset(info), ==, 3); +} + +GPIOD_TEST_CASE(try_offset_out_of_range) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + + info = gpiod_chip_watch_line_info(chip, 10); + g_assert_null(info); + gpiod_test_expect_errno(EINVAL); +} + +GPIOD_TEST_CASE(event_timeout) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + gint ret; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + + info = gpiod_chip_watch_line_info(chip, 6); + g_assert_nonnull(info); + gpiod_test_return_if_failed(); + + ret = gpiod_chip_info_event_wait(chip, 100000000); + g_assert_cmpint(ret, ==, 0); +} + +struct request_ctx { + struct gpiod_chip *chip; + struct gpiod_request_config *req_cfg; + struct gpiod_line_config *line_cfg; +}; + +static gpointer request_release_line(gpointer data) +{ + g_autoptr(struct_gpiod_line_request) request = NULL; + struct request_ctx *ctx = data; + gint ret; + + g_usleep(50); + + request = gpiod_chip_request_lines(ctx->chip, + ctx->req_cfg, ctx->line_cfg); + g_assert_nonnull(request); + if (g_test_failed()) + return NULL; + + g_usleep(50); + + gpiod_line_config_set_direction_default(ctx->line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT); + + ret = gpiod_line_request_reconfigure_lines(request, ctx->line_cfg); + g_assert_cmpint(ret, ==, 0); + if (g_test_failed()) + return NULL; + + g_usleep(50); + + gpiod_line_request_release(request); + request = NULL; + + return NULL; +} + +GPIOD_TEST_CASE(request_reconfigure_release_events) +{ + static const guint offset = 3; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + g_autoptr(struct_gpiod_info_event) request_event = NULL; + g_autoptr(struct_gpiod_info_event) reconfigure_event = NULL; + g_autoptr(struct_gpiod_info_event) release_event = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(GThread) thread = NULL; + struct gpiod_line_info *request_info, *reconfigure_info, *release_info; + guint64 request_ts, reconfigure_ts, release_ts; + struct request_ctx ctx; + gint ret; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + + info = gpiod_chip_watch_line_info(chip, 3); + g_assert_nonnull(info); + gpiod_test_return_if_failed(); + + g_assert_false(gpiod_line_info_is_used(info)); + + ctx.chip = chip; + ctx.req_cfg = req_cfg; + ctx.line_cfg = line_cfg; + + thread = g_thread_new("request-release", request_release_line, &ctx); + g_thread_ref(thread); + + ret = gpiod_chip_info_event_wait(chip, 1000000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_join_thread_and_return_if_failed(thread); + + request_event = gpiod_chip_info_event_read(chip); + g_assert_nonnull(request_event); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpint(gpiod_info_event_get_event_type(request_event), ==, + GPIOD_INFO_EVENT_LINE_REQUESTED); + + request_info = gpiod_info_event_get_line_info(request_event); + + g_assert_cmpuint(gpiod_line_info_get_offset(request_info), ==, 3); + g_assert_true(gpiod_line_info_is_used(request_info)); + g_assert_cmpint(gpiod_line_info_get_direction(request_info), ==, + GPIOD_LINE_DIRECTION_INPUT); + + ret = gpiod_chip_info_event_wait(chip, 1000000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_join_thread_and_return_if_failed(thread); + + reconfigure_event = gpiod_chip_info_event_read(chip); + g_assert_nonnull(reconfigure_event); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpint(gpiod_info_event_get_event_type(reconfigure_event), ==, + GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED); + + reconfigure_info = gpiod_info_event_get_line_info(reconfigure_event); + + g_assert_cmpuint(gpiod_line_info_get_offset(reconfigure_info), ==, 3); + g_assert_true(gpiod_line_info_is_used(reconfigure_info)); + g_assert_cmpint(gpiod_line_info_get_direction(reconfigure_info), ==, + GPIOD_LINE_DIRECTION_OUTPUT); + + ret = gpiod_chip_info_event_wait(chip, 1000000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_join_thread_and_return_if_failed(thread); + + release_event = gpiod_chip_info_event_read(chip); + g_assert_nonnull(release_event); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpint(gpiod_info_event_get_event_type(release_event), ==, + GPIOD_INFO_EVENT_LINE_RELEASED); + + release_info = gpiod_info_event_get_line_info(release_event); + + g_assert_cmpuint(gpiod_line_info_get_offset(release_info), ==, 3); + g_assert_false(gpiod_line_info_is_used(release_info)); + + g_thread_join(thread); + + request_ts = gpiod_info_event_get_timestamp(request_event); + reconfigure_ts = gpiod_info_event_get_timestamp(reconfigure_event); + release_ts = gpiod_info_event_get_timestamp(release_event); + + g_assert_cmpuint(request_ts, <, reconfigure_ts); + g_assert_cmpuint(reconfigure_ts, <, release_ts); +} + +GPIOD_TEST_CASE(chip_fd_can_be_polled) +{ + static const guint offset = 3; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + g_autoptr(struct_gpiod_info_event) event = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(GThread) thread = NULL; + struct gpiod_line_info *evinfo; + struct request_ctx ctx; + struct timespec ts; + struct pollfd pfd; + gint ret, fd; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + + info = gpiod_chip_watch_line_info(chip, 3); + g_assert_nonnull(info); + gpiod_test_return_if_failed(); + + g_assert_false(gpiod_line_info_is_used(info)); + + ctx.chip = chip; + ctx.req_cfg = req_cfg; + ctx.line_cfg = line_cfg; + + thread = g_thread_new("request-release", request_release_line, &ctx); + g_thread_ref(thread); + + fd = gpiod_chip_get_fd(chip); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLIN | POLLPRI; + + ts.tv_sec = 1; + ts.tv_nsec = 0; + + ret = ppoll(&pfd, 1, &ts, NULL); + g_assert_cmpint(ret, >, 0); + gpiod_test_join_thread_and_return_if_failed(thread); + + event = gpiod_chip_info_event_read(chip); + g_assert_nonnull(event); + gpiod_test_join_thread_and_return_if_failed(thread); + + g_assert_cmpint(gpiod_info_event_get_event_type(event), ==, + GPIOD_INFO_EVENT_LINE_REQUESTED); + + evinfo = gpiod_info_event_get_line_info(event); + + g_assert_cmpuint(gpiod_line_info_get_offset(evinfo), ==, 3); + g_assert_true(gpiod_line_info_is_used(evinfo)); + + g_thread_join(thread); +} + +GPIOD_TEST_CASE(unwatch_and_check_that_no_events_are_generated) +{ + static const guint offset = 3; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + g_autoptr(struct_gpiod_info_event) event = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + gint ret; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + + info = gpiod_chip_watch_line_info(chip, 3); + g_assert_nonnull(info); + gpiod_test_return_if_failed(); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + ret = gpiod_chip_info_event_wait(chip, 100000000); + g_assert_cmpint(ret, >, 0); + gpiod_test_return_if_failed(); + + event = gpiod_chip_info_event_read(chip); + g_assert_nonnull(event); + gpiod_test_return_if_failed(); + + ret = gpiod_chip_unwatch_line_info(chip, 3); + g_assert_cmpint(ret, ==, 0); + gpiod_test_return_if_failed(); + + gpiod_line_request_release(request); + request = NULL; + + ret = gpiod_chip_info_event_wait(chip, 100000000); + g_assert_cmpint(ret, ==, 0); +} diff --git a/tests/tests-line-config.c b/tests/tests-line-config.c new file mode 100644 index 0000000..d227890 --- /dev/null +++ b/tests/tests-line-config.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> + +#include <errno.h> +#include <glib.h> +#include <gpiod.h> +#include <stdint.h> + +#include "gpiod-test.h" +#include "gpiod-test-helpers.h" +#include "gpiod-test-sim.h" + +#define GPIOD_TEST_GROUP "line-config" + +GPIOD_TEST_CASE(default_config) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + g_assert_cmpint(gpiod_line_config_get_direction_default(config), ==, + GPIOD_LINE_DIRECTION_AS_IS); + g_assert_cmpint(gpiod_line_config_get_edge_detection_default(config), + ==, GPIOD_LINE_EDGE_NONE); + g_assert_cmpint(gpiod_line_config_get_bias_default(config), ==, + GPIOD_LINE_BIAS_AS_IS); + g_assert_cmpint(gpiod_line_config_get_drive_default(config), ==, + GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_false(gpiod_line_config_get_active_low_default(config)); + g_assert_cmpuint( + gpiod_line_config_get_debounce_period_us_default(config), ==, + 0); + g_assert_cmpint(gpiod_line_config_get_event_clock_default(config), ==, + GPIOD_LINE_EVENT_CLOCK_MONOTONIC); + g_assert_cmpint(gpiod_line_config_get_output_value_default(config), ==, + 0); + g_assert_cmpuint(gpiod_line_config_get_num_overridden_offsets(config), + ==, 0); +} + +GPIOD_TEST_CASE(defaults_are_used_for_non_overridden_offsets) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + g_assert_cmpint(gpiod_line_config_get_direction_offset(config, 4), ==, + GPIOD_LINE_DIRECTION_AS_IS); + g_assert_cmpint(gpiod_line_config_get_edge_detection_offset(config, 4), + ==, GPIOD_LINE_EDGE_NONE); + g_assert_cmpint(gpiod_line_config_get_bias_offset(config, 4), ==, + GPIOD_LINE_BIAS_AS_IS); + g_assert_cmpint(gpiod_line_config_get_drive_offset(config, 4), ==, + GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_false(gpiod_line_config_get_active_low_offset(config, 4)); + g_assert_cmpuint( + gpiod_line_config_get_debounce_period_us_offset(config, 4), ==, + 0); + g_assert_cmpint(gpiod_line_config_get_event_clock_offset(config, 4), + ==, GPIOD_LINE_EVENT_CLOCK_MONOTONIC); + g_assert_cmpint(gpiod_line_config_get_output_value_offset(config, 4), + ==, 0); + g_assert_cmpuint(gpiod_line_config_get_num_overridden_offsets(config), + ==, 0); +} + +GPIOD_TEST_CASE(set_and_clear_direction_override) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + g_assert_cmpint(gpiod_line_config_get_direction_default(config), ==, + GPIOD_LINE_DIRECTION_AS_IS); + gpiod_line_config_set_direction_override(config, + GPIOD_LINE_DIRECTION_OUTPUT, + 3); + + g_assert_cmpint(gpiod_line_config_get_direction_default(config), ==, + GPIOD_LINE_DIRECTION_AS_IS); + g_assert_cmpint(gpiod_line_config_get_direction_offset(config, 3), ==, + GPIOD_LINE_DIRECTION_OUTPUT); + g_assert_true(gpiod_line_config_direction_is_overridden(config, 3)); + gpiod_line_config_clear_direction_override(config, 3); + g_assert_cmpint(gpiod_line_config_get_direction_offset(config, 3), ==, + GPIOD_LINE_DIRECTION_AS_IS); + g_assert_false(gpiod_line_config_direction_is_overridden(config, 3)); +} + +GPIOD_TEST_CASE(invalid_direction) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + gpiod_line_config_set_direction_default(config, INT32_MAX); + g_assert_cmpint(gpiod_line_config_get_direction_default(config), + ==, GPIOD_LINE_DIRECTION_AS_IS); +} + +GPIOD_TEST_CASE(set_and_clear_edge_override) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + g_assert_cmpint(gpiod_line_config_get_edge_detection_default(config), + ==, GPIOD_LINE_EDGE_NONE); + gpiod_line_config_set_edge_detection_override(config, + GPIOD_LINE_EDGE_FALLING, 3); + + g_assert_cmpint(gpiod_line_config_get_edge_detection_default(config), + ==, GPIOD_LINE_EDGE_NONE); + g_assert_cmpint(gpiod_line_config_get_edge_detection_offset(config, 3), + ==, GPIOD_LINE_EDGE_FALLING); + g_assert_true(gpiod_line_config_edge_detection_is_overridden(config, + 3)); + gpiod_line_config_clear_edge_detection_override(config, 3); + g_assert_cmpint(gpiod_line_config_get_edge_detection_offset(config, 3), + ==, GPIOD_LINE_EDGE_NONE); + g_assert_false(gpiod_line_config_edge_detection_is_overridden(config, + 3)); +} + +GPIOD_TEST_CASE(invalid_edge) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + gpiod_line_config_set_edge_detection_default(config, INT32_MAX); + g_assert_cmpint(gpiod_line_config_get_edge_detection_default(config), + ==, GPIOD_LINE_EDGE_NONE); +} + +GPIOD_TEST_CASE(set_and_clear_bias_override) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + g_assert_cmpint(gpiod_line_config_get_bias_default(config), + ==, GPIOD_LINE_BIAS_AS_IS); + gpiod_line_config_set_bias_override(config, GPIOD_LINE_BIAS_PULL_UP, 3); + + g_assert_cmpint(gpiod_line_config_get_bias_default(config), + ==, GPIOD_LINE_BIAS_AS_IS); + g_assert_cmpint(gpiod_line_config_get_bias_offset(config, 3), + ==, GPIOD_LINE_BIAS_PULL_UP); + g_assert_true(gpiod_line_config_bias_is_overridden(config, 3)); + gpiod_line_config_clear_bias_override(config, 3); + g_assert_cmpint(gpiod_line_config_get_bias_offset(config, 3), + ==, GPIOD_LINE_BIAS_AS_IS); + g_assert_false(gpiod_line_config_bias_is_overridden(config, 3)); +} + +GPIOD_TEST_CASE(invalid_bias) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + gpiod_line_config_set_bias_default(config, INT32_MAX); + g_assert_cmpint(gpiod_line_config_get_bias_default(config), + ==, GPIOD_LINE_BIAS_AS_IS); +} + +GPIOD_TEST_CASE(set_and_clear_drive_override) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + g_assert_cmpint(gpiod_line_config_get_drive_default(config), + ==, GPIOD_LINE_DRIVE_PUSH_PULL); + gpiod_line_config_set_drive_override(config, + GPIOD_LINE_DRIVE_OPEN_DRAIN, 3); + + g_assert_cmpint(gpiod_line_config_get_drive_default(config), + ==, GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_cmpint(gpiod_line_config_get_drive_offset(config, 3), + ==, GPIOD_LINE_DRIVE_OPEN_DRAIN); + g_assert_true(gpiod_line_config_drive_is_overridden(config, 3)); + gpiod_line_config_clear_drive_override(config, 3); + g_assert_cmpint(gpiod_line_config_get_drive_offset(config, 3), + ==, GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_false(gpiod_line_config_drive_is_overridden(config, 3)); +} + +GPIOD_TEST_CASE(invalid_drive) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + gpiod_line_config_set_drive_default(config, INT32_MAX); + g_assert_cmpint(gpiod_line_config_get_drive_default(config), + ==, GPIOD_LINE_BIAS_AS_IS); +} + +GPIOD_TEST_CASE(set_and_clear_active_low_override) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + g_assert_false(gpiod_line_config_get_active_low_default(config)); + gpiod_line_config_set_active_low_override(config, true, 3); + + g_assert_false(gpiod_line_config_get_active_low_default(config)); + g_assert_true(gpiod_line_config_get_active_low_offset(config, 3)); + g_assert_true(gpiod_line_config_active_low_is_overridden(config, 3)); + gpiod_line_config_clear_active_low_override(config, 3); + g_assert_false(gpiod_line_config_get_active_low_offset(config, 3)); + g_assert_false(gpiod_line_config_active_low_is_overridden(config, 3)); +} + +GPIOD_TEST_CASE(set_and_clear_debounce_period_override) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + g_assert_cmpuint( + gpiod_line_config_get_debounce_period_us_default(config), + ==, 0); + gpiod_line_config_set_debounce_period_us_override(config, 5000, 3); + + g_assert_cmpuint( + gpiod_line_config_get_debounce_period_us_default(config), + ==, 0); + g_assert_cmpuint( + gpiod_line_config_get_debounce_period_us_offset(config, 3), + ==, 5000); + g_assert_true( + gpiod_line_config_debounce_period_us_is_overridden(config, 3)); + gpiod_line_config_clear_debounce_period_us_override(config, 3); + g_assert_cmpuint( + gpiod_line_config_get_debounce_period_us_offset(config, 3), + ==, 0); + g_assert_false( + gpiod_line_config_debounce_period_us_is_overridden(config, 3)); +} + +GPIOD_TEST_CASE(set_and_clear_event_clock_override) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + g_assert_cmpint(gpiod_line_config_get_event_clock_default(config), + ==, GPIOD_LINE_EVENT_CLOCK_MONOTONIC); + gpiod_line_config_set_event_clock_override(config, + GPIOD_LINE_EVENT_CLOCK_REALTIME, 3); + + g_assert_cmpint(gpiod_line_config_get_event_clock_default(config), + ==, GPIOD_LINE_EVENT_CLOCK_MONOTONIC); + g_assert_cmpint(gpiod_line_config_get_event_clock_offset(config, 3), + ==, GPIOD_LINE_EVENT_CLOCK_REALTIME); + g_assert_true(gpiod_line_config_event_clock_is_overridden(config, 3)); + gpiod_line_config_clear_event_clock_override(config, 3); + g_assert_cmpint(gpiod_line_config_get_event_clock_offset(config, 3), + ==, GPIOD_LINE_EVENT_CLOCK_MONOTONIC); + g_assert_false(gpiod_line_config_event_clock_is_overridden(config, 3)); +} + +GPIOD_TEST_CASE(invalid_event_clock) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + gpiod_line_config_set_event_clock_default(config, INT32_MAX); + g_assert_cmpint(gpiod_line_config_get_event_clock_default(config), + ==, GPIOD_LINE_EVENT_CLOCK_MONOTONIC); +} + +GPIOD_TEST_CASE(set_and_clear_output_value_override) +{ + g_autoptr(struct_gpiod_line_config) config = NULL; + + config = gpiod_test_create_line_config_or_fail(); + + g_assert_cmpint(gpiod_line_config_get_output_value_default(config), + ==, 0); + gpiod_line_config_set_output_value_override(config, 3, 1); + + g_assert_cmpint(gpiod_line_config_get_output_value_default(config), + ==, 0); + g_assert_cmpint(gpiod_line_config_get_output_value_offset(config, 3), + ==, 1); + g_assert_true(gpiod_line_config_output_value_is_overridden(config, 3)); + gpiod_line_config_clear_output_value_override(config, 3); + g_assert_cmpint(gpiod_line_config_get_output_value_offset(config, 3), + ==, 0); + g_assert_false(gpiod_line_config_output_value_is_overridden(config, 3)); +} + +GPIOD_TEST_CASE(set_multiple_output_values) +{ + static const guint offsets[] = { 3, 4, 5, 6 }; + static const gint values[] = { 1, 0, 1, 0 }; + + g_autoptr(struct_gpiod_line_config) config = NULL; + guint overridden[4]; + guint i; + + config = gpiod_test_create_line_config_or_fail(); + + gpiod_line_config_set_output_values(config, 4, offsets, values); + + g_assert_cmpint(gpiod_line_config_get_output_value_default(config), + ==, 0); + + for (i = 0; i < 4; i++) + g_assert_cmpint( + gpiod_line_config_get_output_value_offset(config, + offsets[i]), + ==, values[i]); + + g_assert_cmpuint(gpiod_line_config_get_num_overridden_offsets(config), + ==, 4); + gpiod_line_config_get_overridden_offsets(config, overridden); + + for (i = 0; i < 4; i++) + g_assert_cmpuint(overridden[i], ==, offsets[i]); +} + +GPIOD_TEST_CASE(config_too_complex) +{ + static guint offsets[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 16, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + line_cfg = gpiod_test_create_line_config_or_fail(); + req_cfg = gpiod_test_create_request_config_or_fail(); + + /* + * We need to make the line_config structure exceed the kernel's + * maximum of 10 attributes. + */ + gpiod_line_config_set_direction_override(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT, 0); + gpiod_line_config_set_direction_override(line_cfg, + GPIOD_LINE_DIRECTION_INPUT, 1); + gpiod_line_config_set_edge_detection_override(line_cfg, + GPIOD_LINE_EDGE_BOTH, 2); + gpiod_line_config_set_debounce_period_us_override(line_cfg, 1000, 2); + gpiod_line_config_set_active_low_override(line_cfg, true, 3); + gpiod_line_config_set_direction_override(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT, 4); + gpiod_line_config_set_drive_override(line_cfg, + GPIOD_LINE_DRIVE_OPEN_DRAIN, 4); + gpiod_line_config_set_direction_override(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT, 8); + gpiod_line_config_set_drive_override(line_cfg, + GPIOD_LINE_DRIVE_OPEN_SOURCE, 8); + gpiod_line_config_set_direction_override(line_cfg, + GPIOD_LINE_DIRECTION_INPUT, 5); + gpiod_line_config_set_bias_override(line_cfg, + GPIOD_LINE_BIAS_PULL_DOWN, 5); + gpiod_line_config_set_event_clock_override(line_cfg, + GPIOD_LINE_EVENT_CLOCK_REALTIME, 6); + gpiod_line_config_set_output_value_override(line_cfg, 7, 1); + + gpiod_request_config_set_offsets(req_cfg, 12, offsets); + + request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); + g_assert_null(request); + gpiod_test_expect_errno(E2BIG); +} + +/* + * This triggers the E2BIG error by exhausting the number of overrides in + * the line_config structure instead of making the kernel representation too + * complex. + */ +GPIOD_TEST_CASE(define_too_many_overrides) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 128, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + guint offsets[65], i; + + for (i = 0; i < 65; i++) + offsets[i] = i; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + line_cfg = gpiod_test_create_line_config_or_fail(); + req_cfg = gpiod_test_create_request_config_or_fail(); + + for (i = 0; i < 65; i++) + gpiod_line_config_set_direction_override(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT, offsets[i]); + + gpiod_request_config_set_offsets(req_cfg, 64, offsets); + + request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); + g_assert_null(request); + gpiod_test_expect_errno(E2BIG); +} diff --git a/tests/tests-line-info.c b/tests/tests-line-info.c new file mode 100644 index 0000000..a2ecc13 --- /dev/null +++ b/tests/tests-line-info.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> + +#include <errno.h> +#include <glib.h> +#include <gpiod.h> + +#include "gpiod-test.h" +#include "gpiod-test-helpers.h" +#include "gpiod-test-sim.h" + +#define GPIOD_TEST_GROUP "line-info" + +GPIOD_TEST_CASE(get_line_info_good) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + + info = gpiod_chip_get_line_info(chip, 3); + g_assert_nonnull(info); + g_assert_cmpuint(gpiod_line_info_get_offset(info), ==, 3); +} + +GPIOD_TEST_CASE(get_line_info_offset_out_of_range) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + + info = gpiod_chip_get_line_info(chip, 8); + g_assert_null(info); + gpiod_test_expect_errno(EINVAL); +} + +GPIOD_TEST_CASE(line_info_basic_properties) +{ + static const struct gpiod_test_line_name names[] = { + { .offset = 1, .name = "foo", }, + { .offset = 2, .name = "bar", }, + { .offset = 4, .name = "baz", }, + { .offset = 5, .name = "xyz", }, + { } + }; + + static const struct gpiod_test_hog hogs[] = { + { + .offset = 3, + .name = "hog3", + .direction = G_GPIOSIM_HOG_DIR_OUTPUT_HIGH, + }, + { + .offset = 4, + .name = "hog4", + .direction = G_GPIOSIM_HOG_DIR_OUTPUT_LOW, + }, + { } + }; + + g_autoptr(GPIOSimChip) sim = NULL; + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_line_info) info4 = NULL; + g_autoptr(struct_gpiod_line_info) info6 = NULL; + + sim = g_gpiosim_chip_new( + "num-lines", 8, + "line-names", gpiod_test_package_line_names(names), + "hogs", gpiod_test_package_hogs(hogs), + NULL); + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + info4 = gpiod_test_get_line_info_or_fail(chip, 4); + info6 = gpiod_test_get_line_info_or_fail(chip, 6); + + g_assert_cmpuint(gpiod_line_info_get_offset(info4), ==, 4); + g_assert_cmpstr(gpiod_line_info_get_name(info4), ==, "baz"); + g_assert_cmpstr(gpiod_line_info_get_consumer(info4), ==, "hog4"); + g_assert_true(gpiod_line_info_is_used(info4)); + g_assert_cmpint(gpiod_line_info_get_direction(info4), ==, + GPIOD_LINE_DIRECTION_OUTPUT); + g_assert_cmpint(gpiod_line_info_get_edge_detection(info4), ==, + GPIOD_LINE_EDGE_NONE); + g_assert_false(gpiod_line_info_is_active_low(info4)); + g_assert_cmpint(gpiod_line_info_get_bias(info4), ==, + GPIOD_LINE_BIAS_UNKNOWN); + g_assert_cmpint(gpiod_line_info_get_drive(info4), ==, + GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_cmpint(gpiod_line_info_get_event_clock(info4), ==, + GPIOD_LINE_EVENT_CLOCK_MONOTONIC); + g_assert_false(gpiod_line_info_is_debounced(info4)); + g_assert_cmpuint(gpiod_line_info_get_debounce_period_us(info4), ==, 0); +} + +GPIOD_TEST_CASE(copy_line_info) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + g_autoptr(struct_gpiod_line_info) copy = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + info = gpiod_test_get_line_info_or_fail(chip, 3); + + copy = gpiod_line_info_copy(info); + g_assert_nonnull(copy); + g_assert_true(info != copy); +} + +GPIOD_TEST_CASE(active_high) +{ + static const guint offset = 5; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + gpiod_line_config_set_active_low_default(line_cfg, true); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + info = gpiod_chip_get_line_info(chip, 5); + + g_assert_true(gpiod_line_info_is_active_low(info)); +} + +GPIOD_TEST_CASE(edge_settings) +{ + static const guint offsets[] = { 0, 1, 2, 3 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(struct_gpiod_line_info) info0 = NULL; + g_autoptr(struct_gpiod_line_info) info1 = NULL; + g_autoptr(struct_gpiod_line_info) info2 = NULL; + g_autoptr(struct_gpiod_line_info) info3 = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 4, offsets); + gpiod_line_config_set_edge_detection_override(line_cfg, + GPIOD_LINE_EDGE_RISING, 1); + gpiod_line_config_set_edge_detection_override(line_cfg, + GPIOD_LINE_EDGE_FALLING, 2); + gpiod_line_config_set_edge_detection_override(line_cfg, + GPIOD_LINE_EDGE_BOTH, 3); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + info0 = gpiod_chip_get_line_info(chip, 0); + info1 = gpiod_chip_get_line_info(chip, 1); + info2 = gpiod_chip_get_line_info(chip, 2); + info3 = gpiod_chip_get_line_info(chip, 3); + + g_assert_cmpint(gpiod_line_info_get_edge_detection(info0), ==, + GPIOD_LINE_EDGE_NONE); + g_assert_cmpint(gpiod_line_info_get_edge_detection(info1), ==, + GPIOD_LINE_EDGE_RISING); + g_assert_cmpint(gpiod_line_info_get_edge_detection(info2), ==, + GPIOD_LINE_EDGE_FALLING); + g_assert_cmpint(gpiod_line_info_get_edge_detection(info3), ==, + GPIOD_LINE_EDGE_BOTH); +} + +GPIOD_TEST_CASE(bias_settings) +{ + static const guint offsets[] = { 0, 1, 2, 3 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(struct_gpiod_line_info) info0 = NULL; + g_autoptr(struct_gpiod_line_info) info1 = NULL; + g_autoptr(struct_gpiod_line_info) info2 = NULL; + g_autoptr(struct_gpiod_line_info) info3 = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 4, offsets); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT); + gpiod_line_config_set_bias_override(line_cfg, + GPIOD_LINE_BIAS_DISABLED, 1); + gpiod_line_config_set_bias_override(line_cfg, + GPIOD_LINE_BIAS_PULL_DOWN, 2); + gpiod_line_config_set_bias_override(line_cfg, + GPIOD_LINE_BIAS_PULL_UP, 3); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + info0 = gpiod_chip_get_line_info(chip, 0); + info1 = gpiod_chip_get_line_info(chip, 1); + info2 = gpiod_chip_get_line_info(chip, 2); + info3 = gpiod_chip_get_line_info(chip, 3); + + g_assert_cmpint(gpiod_line_info_get_bias(info0), ==, + GPIOD_LINE_BIAS_UNKNOWN); + g_assert_cmpint(gpiod_line_info_get_bias(info1), ==, + GPIOD_LINE_BIAS_DISABLED); + g_assert_cmpint(gpiod_line_info_get_bias(info2), ==, + GPIOD_LINE_BIAS_PULL_DOWN); + g_assert_cmpint(gpiod_line_info_get_bias(info3), ==, + GPIOD_LINE_BIAS_PULL_UP); +} + +GPIOD_TEST_CASE(drive_settings) +{ + static const guint offsets[] = { 0, 1, 2 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(struct_gpiod_line_info) info0 = NULL; + g_autoptr(struct_gpiod_line_info) info1 = NULL; + g_autoptr(struct_gpiod_line_info) info2 = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 3, offsets); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT); + gpiod_line_config_set_drive_override(line_cfg, + GPIOD_LINE_DRIVE_OPEN_DRAIN, 1); + gpiod_line_config_set_drive_override(line_cfg, + GPIOD_LINE_DRIVE_OPEN_SOURCE, 2); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + info0 = gpiod_chip_get_line_info(chip, 0); + info1 = gpiod_chip_get_line_info(chip, 1); + info2 = gpiod_chip_get_line_info(chip, 2); + + g_assert_cmpint(gpiod_line_info_get_drive(info0), ==, + GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_cmpint(gpiod_line_info_get_drive(info1), ==, + GPIOD_LINE_DRIVE_OPEN_DRAIN); + g_assert_cmpint(gpiod_line_info_get_drive(info2), ==, + GPIOD_LINE_DRIVE_OPEN_SOURCE); +} + +GPIOD_TEST_CASE(debounce_period) +{ + static const guint offset = 5; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + gpiod_line_config_set_edge_detection_default(line_cfg, + GPIOD_LINE_EDGE_BOTH); + gpiod_line_config_set_debounce_period_us_default(line_cfg, 1000); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + info = gpiod_chip_get_line_info(chip, 5); + + g_assert_cmpuint(gpiod_line_info_get_debounce_period_us(info), + ==, 1000); +} + +GPIOD_TEST_CASE(event_clock) +{ + static const guint offsets[] = { 0, 1 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(struct_gpiod_line_info) info0 = NULL; + g_autoptr(struct_gpiod_line_info) info1 = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 2, offsets); + gpiod_line_config_set_event_clock_override(line_cfg, + GPIOD_LINE_EVENT_CLOCK_REALTIME, 1); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + info0 = gpiod_chip_get_line_info(chip, 0); + info1 = gpiod_chip_get_line_info(chip, 1); + + g_assert_cmpint(gpiod_line_info_get_event_clock(info0), ==, + GPIOD_LINE_EVENT_CLOCK_MONOTONIC); + g_assert_cmpint(gpiod_line_info_get_event_clock(info1), ==, + GPIOD_LINE_EVENT_CLOCK_REALTIME); +} diff --git a/tests/tests-line-request.c b/tests/tests-line-request.c new file mode 100644 index 0000000..44f91df --- /dev/null +++ b/tests/tests-line-request.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> + +#include <glib.h> +#include <gpiod.h> + +#include "gpiod-test.h" +#include "gpiod-test-helpers.h" +#include "gpiod-test-sim.h" + +#define GPIOD_TEST_GROUP "line-request" + +GPIOD_TEST_CASE(request_fails_with_no_offsets) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 4, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + g_assert_cmpuint(gpiod_request_config_get_num_offsets(req_cfg), ==, 0); + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + + request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); + g_assert_null(request); + gpiod_test_expect_errno(EINVAL); +} + +GPIOD_TEST_CASE(request_fails_with_duplicate_offsets) +{ + static const guint offsets[] = { 0, 2, 2, 3 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 4, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 4, offsets); + + request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); + g_assert_null(request); + gpiod_test_expect_errno(EBUSY); +} + +GPIOD_TEST_CASE(request_fails_with_offset_out_of_bounds) +{ + static const guint offsets[] = { 2, 6 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 4, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 2, offsets); + + request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); + g_assert_null(request); + gpiod_test_expect_errno(EINVAL); +} + +GPIOD_TEST_CASE(set_consumer) +{ + static const guint offset = 2; + static const gchar *const consumer = "foobar"; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 4, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + gpiod_request_config_set_consumer(req_cfg, consumer); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + info = gpiod_test_get_line_info_or_fail(chip, offset); + + g_assert_cmpstr(gpiod_line_info_get_consumer(info), ==, consumer); +} + +GPIOD_TEST_CASE(empty_consumer) +{ + static const guint offset = 2; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 4, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(struct_gpiod_line_info) info = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + info = gpiod_test_get_line_info_or_fail(chip, offset); + + g_assert_cmpstr(gpiod_line_info_get_consumer(info), ==, "?"); +} + +GPIOD_TEST_CASE(default_output_value) +{ + /* + * Have a hole in offsets on purpose - make sure it's not set by + * accident. + */ + static const guint offsets[] = { 0, 1, 3, 4 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + guint i; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 4, offsets); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT); + gpiod_line_config_set_output_value_default(line_cfg, 1); + + g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_DOWN); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + for (i = 0; i < 4; i++) + g_assert_cmpint(g_gpiosim_chip_get_value(sim, offsets[i]), + ==, 1); + + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 2), ==, 0); +} + +GPIOD_TEST_CASE(default_and_overridden_output_value) +{ + static const guint offsets[] = { 0, 1, 2, 3 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 4, offsets); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT); + gpiod_line_config_set_output_value_default(line_cfg, 1); + gpiod_line_config_set_output_value_override(line_cfg, 2, 0); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + g_assert_cmpint(g_gpiosim_chip_get_value(sim, offsets[0]), + ==, 1); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, offsets[1]), + ==, 1); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, offsets[2]), + ==, 0); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, offsets[3]), + ==, 1); +} + +GPIOD_TEST_CASE(read_all_values) +{ + static const guint offsets[] = { 0, 2, 4, 5, 7 }; + static const gint pulls[] = { 0, 1, 0, 1, 1 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + gint ret, values[5]; + guint i; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 5, offsets); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_INPUT); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + for (i = 0; i < 5; i++) + g_gpiosim_chip_set_pull(sim, offsets[i], + pulls[i] ? G_GPIOSIM_PULL_UP : G_GPIOSIM_PULL_DOWN); + + ret = gpiod_line_request_get_values(request, values); + g_assert_cmpint(ret, ==, 0); + gpiod_test_return_if_failed(); + + for (i = 0; i < 5; i++) + g_assert_cmpint(values[i], ==, pulls[i]); +} + +GPIOD_TEST_CASE(request_multiple_values_but_read_one) +{ + static const guint offsets[] = { 0, 2, 4, 5, 7 }; + static const gint pulls[] = { 0, 1, 0, 1, 1 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + gint ret; + guint i; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 5, offsets); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_INPUT); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + for (i = 0; i < 5; i++) + g_gpiosim_chip_set_pull(sim, offsets[i], + pulls[i] ? G_GPIOSIM_PULL_UP : G_GPIOSIM_PULL_DOWN); + + ret = gpiod_line_request_get_value(request, 5); + g_assert_cmpint(ret, ==, 1); +} + +GPIOD_TEST_CASE(set_all_values) +{ + static const guint offsets[] = { 0, 2, 4, 5, 6 }; + static const gint values[] = { 1, 0, 1, 1, 1 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + gint ret; + guint i; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 5, offsets); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + ret = gpiod_line_request_set_values(request, values); + g_assert_cmpint(ret, ==, 0); + gpiod_test_return_if_failed(); + + for (i = 0; i < 5; i++) + g_assert_cmpint(g_gpiosim_chip_get_value(sim, offsets[i]), + ==, values[i]); +} + +GPIOD_TEST_CASE(request_survives_parent_chip) +{ + static const guint offset = 0; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 4, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + gint ret; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 1, &offset); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT); + gpiod_line_config_set_output_value_default(line_cfg, 1); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + g_assert_cmpint(gpiod_line_request_get_value(request, offset), ==, 1); + + gpiod_chip_close(chip); + chip = NULL; + + ret = gpiod_line_request_set_value(request, offset, 0); + g_assert_cmpint(ret, ==, 0); + gpiod_test_return_if_failed(); + + ret = gpiod_line_request_set_value(request, offset, 1); + g_assert_cmpint(ret, ==, 0); + gpiod_test_return_if_failed(); +} + +GPIOD_TEST_CASE(request_with_overridden_direction) +{ + static const guint offsets[] = { 0, 1, 2, 3 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 4, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + g_autoptr(struct_gpiod_line_info) info0 = NULL; + g_autoptr(struct_gpiod_line_info) info1 = NULL; + g_autoptr(struct_gpiod_line_info) info2 = NULL; + g_autoptr(struct_gpiod_line_info) info3 = NULL; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 4, offsets); + gpiod_line_config_set_direction_default(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT); + gpiod_line_config_set_direction_override(line_cfg, + GPIOD_LINE_DIRECTION_INPUT, 3); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + info0 = gpiod_test_get_line_info_or_fail(chip, 0); + info1 = gpiod_test_get_line_info_or_fail(chip, 1); + info2 = gpiod_test_get_line_info_or_fail(chip, 2); + info3 = gpiod_test_get_line_info_or_fail(chip, 3); + + g_assert_cmpint(gpiod_line_info_get_direction(info0), ==, + GPIOD_LINE_DIRECTION_OUTPUT); + g_assert_cmpint(gpiod_line_info_get_direction(info1), ==, + GPIOD_LINE_DIRECTION_OUTPUT); + g_assert_cmpint(gpiod_line_info_get_direction(info2), ==, + GPIOD_LINE_DIRECTION_OUTPUT); + g_assert_cmpint(gpiod_line_info_get_direction(info3), ==, + GPIOD_LINE_DIRECTION_INPUT); +} + +GPIOD_TEST_CASE(num_lines) +{ + static const guint offsets[] = { 0, 1, 2, 3, 7, 8, 11, 14 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 16, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + guint read_back[8], i; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 8, offsets); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + g_assert_cmpuint(gpiod_line_request_get_num_lines(request), ==, 8); + gpiod_test_return_if_failed(); + gpiod_line_request_get_offsets(request, read_back); + for (i = 0; i < 8; i++) + g_assert_cmpuint(read_back[i], ==, offsets[i]); +} + +GPIOD_TEST_CASE(active_low_read_value) +{ + static const guint offsets[] = { 2, 3 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(struct_gpiod_chip) chip = NULL; + g_autoptr(struct_gpiod_request_config) req_cfg = NULL; + g_autoptr(struct_gpiod_line_config) line_cfg = NULL; + g_autoptr(struct_gpiod_line_request) request = NULL; + gint value; + + chip = gpiod_test_open_chip_or_fail(g_gpiosim_chip_get_dev_path(sim)); + req_cfg = gpiod_test_create_request_config_or_fail(); + line_cfg = gpiod_test_create_line_config_or_fail(); + + gpiod_request_config_set_offsets(req_cfg, 2, offsets); + gpiod_line_config_set_direction_override(line_cfg, + GPIOD_LINE_DIRECTION_INPUT, 2); + gpiod_line_config_set_direction_override(line_cfg, + GPIOD_LINE_DIRECTION_OUTPUT, 3); + gpiod_line_config_set_active_low_default(line_cfg, true); + gpiod_line_config_set_output_value_default(line_cfg, 1); + + request = gpiod_test_request_lines_or_fail(chip, req_cfg, line_cfg); + + g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_DOWN); + value = gpiod_line_request_get_value(request, 2); + g_assert_cmpint(value, ==, 1); + + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 3), ==, 0); +} diff --git a/tests/tests-line.c b/tests/tests-line.c deleted file mode 100644 index 3985990..0000000 --- a/tests/tests-line.c +++ /dev/null @@ -1,1091 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> - -#include <errno.h> -#include <string.h> - -#include "gpiod-test.h" - -#define GPIOD_TEST_GROUP "line" - -GPIOD_TEST_CASE(request_output, 0, { 8 }) -{ - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line *line0; - struct gpiod_line *line1; - 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, 2); - line1 = gpiod_chip_get_line(chip, 5); - g_assert_nonnull(line0); - g_assert_nonnull(line1); - gpiod_test_return_if_failed(); - - ret = gpiod_line_request_output(line0, GPIOD_TEST_CONSUMER, 0); - g_assert_cmpint(ret, ==, 0); - ret = gpiod_line_request_output(line1, GPIOD_TEST_CONSUMER, 1); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0); - g_assert_cmpint(gpiod_test_chip_get_value(0, 5), ==, 1); -} - -GPIOD_TEST_CASE(request_already_requested, 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, 0); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - ret = gpiod_line_request_input(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - ret = gpiod_line_request_input(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, -1); - g_assert_cmpint(errno, ==, EBUSY); -} - -GPIOD_TEST_CASE(consumer, 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, 0); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - g_assert_null(gpiod_line_consumer(line)); - - ret = gpiod_line_request_input(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpstr(gpiod_line_consumer(line), ==, GPIOD_TEST_CONSUMER); -} - -GPIOD_TEST_CASE(consumer_long_string, 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, 0); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - g_assert_null(gpiod_line_consumer(line)); - - ret = gpiod_line_request_input(line, - "consumer string over 32 characters long"); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - g_assert_cmpstr(gpiod_line_consumer(line), ==, - "consumer string over 32 charact"); - g_assert_cmpuint(strlen(gpiod_line_consumer(line)), ==, 31); -} - -GPIOD_TEST_CASE(request_bulk_output, 0, { 8, 8 }) -{ - g_autoptr(gpiod_line_bulk_struct) bulkA = NULL; - g_autoptr(gpiod_line_bulk_struct) bulkB = NULL; - g_autoptr(gpiod_chip_struct) chipA = NULL; - g_autoptr(gpiod_chip_struct) chipB = NULL; - struct gpiod_line *lineA0, *lineA1, *lineA2, *lineA3, - *lineB0, *lineB1, *lineB2, *lineB3; - gint valA[4], valB[4], ret; - - chipA = gpiod_chip_open(gpiod_test_chip_path(0)); - chipB = gpiod_chip_open(gpiod_test_chip_path(1)); - g_assert_nonnull(chipA); - g_assert_nonnull(chipB); - gpiod_test_return_if_failed(); - - lineA0 = gpiod_chip_get_line(chipA, 0); - lineA1 = gpiod_chip_get_line(chipA, 1); - lineA2 = gpiod_chip_get_line(chipA, 2); - lineA3 = gpiod_chip_get_line(chipA, 3); - lineB0 = gpiod_chip_get_line(chipB, 0); - lineB1 = gpiod_chip_get_line(chipB, 1); - lineB2 = gpiod_chip_get_line(chipB, 2); - lineB3 = gpiod_chip_get_line(chipB, 3); - - g_assert_nonnull(lineA0); - g_assert_nonnull(lineA1); - g_assert_nonnull(lineA2); - g_assert_nonnull(lineA3); - g_assert_nonnull(lineB0); - g_assert_nonnull(lineB1); - g_assert_nonnull(lineB2); - g_assert_nonnull(lineB3); - gpiod_test_return_if_failed(); - - bulkA = gpiod_line_bulk_new(4); - bulkB = gpiod_line_bulk_new(4); - g_assert_nonnull(bulkA); - g_assert_nonnull(bulkB); - gpiod_test_return_if_failed(); - - gpiod_line_bulk_add_line(bulkA, lineA0); - gpiod_line_bulk_add_line(bulkA, lineA1); - gpiod_line_bulk_add_line(bulkA, lineA2); - gpiod_line_bulk_add_line(bulkA, lineA3); - gpiod_line_bulk_add_line(bulkB, lineB0); - gpiod_line_bulk_add_line(bulkB, lineB1); - gpiod_line_bulk_add_line(bulkB, lineB2); - gpiod_line_bulk_add_line(bulkB, lineB3); - - valA[0] = 1; - valA[1] = 0; - valA[2] = 0; - valA[3] = 1; - ret = gpiod_line_request_bulk_output(bulkA, - GPIOD_TEST_CONSUMER, valA); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - valB[0] = 0; - valB[1] = 1; - valB[2] = 0; - valB[3] = 1; - ret = gpiod_line_request_bulk_output(bulkB, - GPIOD_TEST_CONSUMER, valB); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - g_assert_cmpint(gpiod_test_chip_get_value(0, 0), ==, 1); - g_assert_cmpint(gpiod_test_chip_get_value(0, 1), ==, 0); - g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0); - g_assert_cmpint(gpiod_test_chip_get_value(0, 3), ==, 1); - - g_assert_cmpint(gpiod_test_chip_get_value(1, 0), ==, 0); - g_assert_cmpint(gpiod_test_chip_get_value(1, 1), ==, 1); - g_assert_cmpint(gpiod_test_chip_get_value(1, 2), ==, 0); - g_assert_cmpint(gpiod_test_chip_get_value(1, 3), ==, 1); -} - -GPIOD_TEST_CASE(request_null_default_vals_for_output, 0, { 8 }) -{ - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - 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(); - - bulk = gpiod_line_bulk_new(3); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - - gpiod_line_bulk_add_line(bulk, line0); - gpiod_line_bulk_add_line(bulk, line1); - gpiod_line_bulk_add_line(bulk, line2); - - ret = gpiod_line_request_bulk_output(bulk, GPIOD_TEST_CONSUMER, NULL); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - 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_value, 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); - gpiod_test_return_if_failed(); - 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_value(line, 0); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpint(gpiod_test_chip_get_value(0, 2), ==, 0); -} - -GPIOD_TEST_CASE(set_value_bulk, 0, { 8 }) -{ - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - 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(); - - bulk = gpiod_line_bulk_new(3); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - - gpiod_line_bulk_add_line(bulk, line0); - gpiod_line_bulk_add_line(bulk, line1); - gpiod_line_bulk_add_line(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); - gpiod_test_return_if_failed(); - 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); - - values[0] = 2; - values[1] = 1; - values[2] = 0; - - ret = gpiod_line_set_value_bulk(bulk, values); - g_assert_cmpint(ret, ==, 0); - 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_value_bulk(bulk, NULL); - g_assert_cmpint(ret, ==, 0); - 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_config_bulk_null_values, 0, { 8 }) -{ - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - 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(); - - bulk = gpiod_line_bulk_new(3); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - - gpiod_line_bulk_add_line(bulk, line0); - gpiod_line_bulk_add_line(bulk, line1); - gpiod_line_bulk_add_line(bulk, line2); - - ret = gpiod_line_request_bulk_output(bulk, GPIOD_TEST_CONSUMER, 0); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - g_assert_false(gpiod_line_is_active_low(line0)); - g_assert_false(gpiod_line_is_active_low(line1)); - g_assert_false(gpiod_line_is_active_low(line2)); - - 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_true(gpiod_line_is_active_low(line0)); - g_assert_true(gpiod_line_is_active_low(line1)); - g_assert_true(gpiod_line_is_active_low(line2)); - 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_false(gpiod_line_is_active_low(line0)); - g_assert_false(gpiod_line_is_active_low(line1)); - g_assert_false(gpiod_line_is_active_low(line2)); - 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); - gpiod_test_return_if_failed(); - g_assert_false(gpiod_line_is_active_low(line)); - 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_true(gpiod_line_is_active_low(line)); - 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_false(gpiod_line_is_active_low(line)); - 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); - gpiod_test_return_if_failed(); - g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_UNKNOWN); - - ret = gpiod_line_set_flags(line, - GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLED); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_DISABLED); - - 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); - gpiod_test_return_if_failed(); - g_assert_cmpint(gpiod_line_drive(line), ==, GPIOD_LINE_DRIVE_PUSH_PULL); - - ret = gpiod_line_set_flags(line, - GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpint(gpiod_line_drive(line), ==, - GPIOD_LINE_DRIVE_OPEN_DRAIN); - - ret = gpiod_line_set_flags(line, - GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpint(gpiod_line_drive(line), ==, - GPIOD_LINE_DRIVE_OPEN_SOURCE); -} - -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); - gpiod_test_return_if_failed(); - 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_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - 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(); - - bulk = gpiod_line_bulk_new(3); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - - gpiod_line_bulk_add_line(bulk, line0); - gpiod_line_bulk_add_line(bulk, line1); - gpiod_line_bulk_add_line(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); - gpiod_test_return_if_failed(); - 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_line_bulk_struct) bulk = NULL; - 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(); - - /* check cached by request... */ - ret = gpiod_line_request_output(line, GPIOD_TEST_CONSUMER, 1); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - 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); - - bulk = gpiod_line_bulk_new(1); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - - gpiod_line_bulk_add_line(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(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, 5); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - ret = gpiod_line_request_output(line, GPIOD_TEST_CONSUMER, 1); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - 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_input(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpint(gpiod_line_direction(line), ==, - GPIOD_LINE_DIRECTION_INPUT); -} - -GPIOD_TEST_CASE(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, 5); - g_assert_nonnull(line); - gpiod_test_return_if_failed(); - - ret = gpiod_line_request_input(line, GPIOD_TEST_CONSUMER); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - g_assert_false(gpiod_line_is_active_low(line)); - - gpiod_line_release(line); - - ret = gpiod_line_request_input_flags(line, GPIOD_TEST_CONSUMER, - GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - 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); - gpiod_test_return_if_failed(); - - 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); - gpiod_test_return_if_failed(); - - 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 }) -{ - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line_request_config config; - 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(); - - g_assert_false(gpiod_line_is_used(line)); - g_assert_cmpint(gpiod_line_drive(line), ==, GPIOD_LINE_DRIVE_PUSH_PULL); - g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_UNKNOWN); - - config.request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; - config.consumer = GPIOD_TEST_CONSUMER; - config.flags = GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN; - - ret = gpiod_line_request(line, &config, 0); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - g_assert_true(gpiod_line_is_used(line)); - g_assert_cmpint(gpiod_line_drive(line), ==, - GPIOD_LINE_DRIVE_OPEN_DRAIN); - g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_UNKNOWN); - g_assert_cmpint(gpiod_line_direction(line), ==, - GPIOD_LINE_DIRECTION_OUTPUT); - - gpiod_line_release(line); - - config.flags = GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE; - - ret = gpiod_line_request(line, &config, 0); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - g_assert_true(gpiod_line_is_used(line)); - g_assert_cmpint(gpiod_line_drive(line), ==, - GPIOD_LINE_DRIVE_OPEN_SOURCE); - g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_UNKNOWN); - g_assert_cmpint(gpiod_line_direction(line), ==, - GPIOD_LINE_DIRECTION_OUTPUT); - - gpiod_line_release(line); -} - -GPIOD_TEST_CASE(misc_flags_work_together, 0, { 8 }) -{ - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line_request_config config; - 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(); - - /* - * Verify that open drain/source flags work together - * with active_low. - */ - - config.request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; - config.consumer = GPIOD_TEST_CONSUMER; - config.flags = GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN | - GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; - - ret = gpiod_line_request(line, &config, 0); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - g_assert_true(gpiod_line_is_used(line)); - g_assert_cmpint(gpiod_line_drive(line), ==, - GPIOD_LINE_DRIVE_OPEN_DRAIN); - g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_UNKNOWN); - g_assert_true(gpiod_line_is_active_low(line)); - g_assert_cmpint(gpiod_line_direction(line), ==, - GPIOD_LINE_DIRECTION_OUTPUT); - - gpiod_line_release(line); - - config.flags = GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE | - GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW; - - ret = gpiod_line_request(line, &config, 0); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - g_assert_true(gpiod_line_is_used(line)); - g_assert_cmpint(gpiod_line_drive(line), ==, - GPIOD_LINE_DRIVE_OPEN_SOURCE); - g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_UNKNOWN); - g_assert_true(gpiod_line_is_active_low(line)); - - 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); - gpiod_test_return_if_failed(); - - g_assert_true(gpiod_line_is_used(line)); - g_assert_cmpint(gpiod_line_drive(line), ==, GPIOD_LINE_DRIVE_PUSH_PULL); - g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_PULL_DOWN); - g_assert_true(gpiod_line_is_active_low(line)); - g_assert_cmpint(gpiod_line_direction(line), ==, - GPIOD_LINE_DIRECTION_INPUT); - - ret = gpiod_line_get_value(line); - g_assert_cmpint(ret, ==, 1); - - 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); - gpiod_test_return_if_failed(); - - g_assert_true(gpiod_line_is_used(line)); - g_assert_cmpint(gpiod_line_drive(line), ==, GPIOD_LINE_DRIVE_PUSH_PULL); - g_assert_cmpint(gpiod_line_bias(line), ==, GPIOD_LINE_BIAS_PULL_UP); - g_assert_true(gpiod_line_is_active_low(line)); - g_assert_cmpint(gpiod_line_direction(line), ==, - GPIOD_LINE_DIRECTION_INPUT); - - ret = gpiod_line_get_value(line); - g_assert_cmpint(ret, ==, 0); - - gpiod_line_release(line); -} - -GPIOD_TEST_CASE(open_source_open_drain_input_mode, 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_OPEN_DRAIN); - g_assert_cmpint(ret, ==, -1); - g_assert_cmpint(errno, ==, EINVAL); - - ret = gpiod_line_request_input_flags(line, GPIOD_TEST_CONSUMER, - GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE); - g_assert_cmpint(ret, ==, -1); - g_assert_cmpint(errno, ==, EINVAL); -} - -GPIOD_TEST_CASE(open_source_open_drain_simultaneously, 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_flags(line, GPIOD_TEST_CONSUMER, - GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE | - GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN, 1); - g_assert_cmpint(ret, ==, -1); - 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_DISABLED | - 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_DISABLED | - 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_DISABLED | - 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 }) -{ - g_autoptr(gpiod_line_bulk_struct) bulk = NULL; - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line *line0; - struct gpiod_line *line1; - gint ret, vals[2]; - - chip = gpiod_chip_open(gpiod_test_chip_path(0)); - g_assert_nonnull(chip); - gpiod_test_return_if_failed(); - - line0 = gpiod_chip_get_line(chip, 2); - line1 = gpiod_chip_get_line(chip, 3); - g_assert_nonnull(line0); - g_assert_nonnull(line1); - gpiod_test_return_if_failed(); - - bulk = gpiod_line_bulk_new(2); - g_assert_nonnull(bulk); - gpiod_test_return_if_failed(); - - gpiod_line_bulk_add_line(bulk, line0); - gpiod_line_bulk_add_line(bulk, line1); - - vals[0] = vals[1] = 1; - - ret = gpiod_line_request_bulk_output(bulk, GPIOD_TEST_CONSUMER, vals); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - - gpiod_line_release(line0); - - ret = gpiod_line_get_value(line0); - g_assert_cmpint(ret, ==, -1); - g_assert_cmpint(errno, ==, EPERM); -} - -GPIOD_TEST_CASE(null_consumer, 0, { 8 }) -{ - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line_request_config config; - 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(); - - config.request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT; - config.consumer = NULL; - config.flags = 0; - - ret = gpiod_line_request(line, &config, 0); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - g_assert_cmpstr(gpiod_line_consumer(line), ==, "?"); - - gpiod_line_release(line); - - /* - * Internally we use different structures for event requests, so we - * need to test that explicitly too. - */ - config.request_type = GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES; - - ret = gpiod_line_request(line, &config, 0); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpstr(gpiod_line_consumer(line), ==, "?"); -} - -GPIOD_TEST_CASE(empty_consumer, 0, { 8 }) -{ - g_autoptr(gpiod_chip_struct) chip = NULL; - struct gpiod_line_request_config config; - 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(); - - config.request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT; - config.consumer = ""; - config.flags = 0; - - ret = gpiod_line_request(line, &config, 0); - g_assert_cmpint(ret, ==, 0); - gpiod_test_return_if_failed(); - g_assert_cmpstr(gpiod_line_consumer(line), ==, "?"); - - gpiod_line_release(line); - - /* - * Internally we use different structures for event requests, so we - * need to test that explicitly too. - */ - config.request_type = GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES; - - ret = gpiod_line_request(line, &config, 0); - g_assert_cmpint(ret, ==, 0); - g_assert_cmpstr(gpiod_line_consumer(line), ==, "?"); -} diff --git a/tests/tests-misc.c b/tests/tests-misc.c index 051ab81..3f7b33e 100644 --- a/tests/tests-misc.c +++ b/tests/tests-misc.c @@ -1,25 +1,97 @@ // SPDX-License-Identifier: GPL-2.0-or-later // SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> -#include <string.h> +#include <errno.h> +#include <glib.h> +#include <gpiod.h> +#include <unistd.h> #include "gpiod-test.h" +#include "gpiod-test-helpers.h" +#include "gpiod-test-sim.h" #define GPIOD_TEST_GROUP "misc" -GPIOD_TEST_CASE(version_string, 0, { 1 }) +GPIOD_TEST_CASE(is_gpiochip_bad) { + g_assert_false(gpiod_is_gpiochip_device("/dev/null")); + g_assert_false(gpiod_is_gpiochip_device("/dev/nonexistent")); +} + +GPIOD_TEST_CASE(is_gpiochip_good) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + + g_assert_true(gpiod_is_gpiochip_device( + g_gpiosim_chip_get_dev_path(sim))); +} + +GPIOD_TEST_CASE(is_gpiochip_link_bad) +{ + g_autofree gchar *uuid; + g_autofree gchar *link; + gint ret; + + uuid = g_uuid_string_random(); + link = g_strdup_printf("/tmp/%s", uuid); + + ret = symlink("/dev/null", link); + g_assert_cmpint(ret, ==, 0); + gpiod_test_return_if_failed(); + + g_assert_false(gpiod_is_gpiochip_device(link)); + ret = unlink(link); + g_assert_cmpint(ret, ==, 0); +} + +GPIOD_TEST_CASE(is_gpiochip_link_good) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autofree gchar *uuid; + g_autofree gchar *link; + gint ret; + + uuid = g_uuid_string_random(); + link = g_strdup_printf("/tmp/%s", uuid); + + ret = symlink(g_gpiosim_chip_get_dev_path(sim), link); + g_assert_cmpint(ret, ==, 0); + gpiod_test_return_if_failed(); + + g_assert_true(gpiod_is_gpiochip_device(link)); + ret = unlink(link); + g_assert_cmpint(ret, ==, 0); +} + +GPIOD_TEST_CASE(version_string) +{ + static const gchar *const pattern = "^[0-9][1-9]?\\.[0-9][1-9]?([\\.0-9]?|\\-devel)$"; + + g_autoptr(GError) err = NULL; g_autoptr(GRegex) regex = NULL; - GError *err = NULL; + g_autoptr(GMatchInfo) match = NULL; + g_autofree gchar *res = NULL; const gchar *ver; + gboolean ret; ver = gpiod_version_string(); g_assert_nonnull(ver); gpiod_test_return_if_failed(); - g_assert_cmpuint(strlen(ver), >, 0); - regex = g_regex_new("^[0-9]+\\.[0-9]+[0-9a-zA-Z\\.-]*$", 0, 0, &err); - g_assert_null(err); + regex = g_regex_new(pattern, 0, 0, &err); + g_assert_nonnull(regex); + g_assert_no_error(err); gpiod_test_return_if_failed(); - g_assert_true(g_regex_match(regex, ver, 0, NULL)); + + ret = g_regex_match(regex, ver, 0, &match); + g_assert_true(ret); + gpiod_test_return_if_failed(); + + g_assert_true(g_match_info_matches(match)); + res = g_match_info_fetch(match, 0); + g_assert_nonnull(res); + g_assert_cmpstr(res, ==, ver); + g_match_info_next(match, &err); + g_assert_no_error(err); + g_assert_false(g_match_info_matches(match)); } diff --git a/tests/tests-request-config.c b/tests/tests-request-config.c new file mode 100644 index 0000000..becb414 --- /dev/null +++ b/tests/tests-request-config.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@xxxxxxxxx> + +#include <glib.h> +#include <gpiod.h> + +#include "gpiod-test.h" +#include "gpiod-test-helpers.h" + +#define GPIOD_TEST_GROUP "request-config" + +GPIOD_TEST_CASE(default_config) +{ + g_autoptr(struct_gpiod_request_config) config = NULL; + + config = gpiod_test_create_request_config_or_fail(); + + g_assert_null(gpiod_request_config_get_consumer(config)); + g_assert_cmpuint(gpiod_request_config_get_num_offsets(config), ==, 0); + g_assert_cmpuint(gpiod_request_config_get_event_buffer_size(config), + ==, 0); +} + +GPIOD_TEST_CASE(consumer) +{ + g_autoptr(struct_gpiod_request_config) config = NULL; + + config = gpiod_test_create_request_config_or_fail(); + + gpiod_request_config_set_consumer(config, "foobar"); + g_assert_cmpstr(gpiod_request_config_get_consumer(config), + ==, "foobar"); +} + +GPIOD_TEST_CASE(offsets) +{ + static const guint offsets[] = { 0, 3, 4, 7 }; + + g_autoptr(struct_gpiod_request_config) config = NULL; + guint read_back[4], i; + + config = gpiod_test_create_request_config_or_fail(); + + gpiod_request_config_set_offsets(config, 4, offsets); + g_assert_cmpuint(gpiod_request_config_get_num_offsets(config), ==, 4); + memset(read_back, 0, sizeof(read_back)); + gpiod_request_config_get_offsets(config, read_back); + for (i = 0; i < 4; i++) + g_assert_cmpuint(read_back[i], ==, offsets[i]); +} + +GPIOD_TEST_CASE(max_offsets) +{ + static const guint offsets_good[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 + }; + + static const guint offsets_bad[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64 + }; + + g_autoptr(struct_gpiod_request_config) config = NULL; + + config = gpiod_test_create_request_config_or_fail(); + + gpiod_request_config_set_offsets(config, 64, offsets_good); + g_assert_cmpuint(gpiod_request_config_get_num_offsets(config), ==, 64); + + gpiod_request_config_set_offsets(config, 65, offsets_bad); + /* Should get truncated. */ + g_assert_cmpuint(gpiod_request_config_get_num_offsets(config), ==, 64); +} + +GPIOD_TEST_CASE(event_buffer_size) +{ + g_autoptr(struct_gpiod_request_config) config = NULL; + + config = gpiod_test_create_request_config_or_fail(); + + gpiod_request_config_set_event_buffer_size(config, 128); + g_assert_cmpuint(gpiod_request_config_get_event_buffer_size(config), + ==, 128); +} -- 2.30.1