From patchwork Fri Jun 28 18:58:20 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808534 Received: from mail-wr1-f44.google.com (mail-wr1-f44.google.com [209.85.221.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6EFA555884 for ; Fri, 28 Jun 2024 18:58:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601141; cv=none; b=S4hqZ/zChn9MF77ryrZcxOhNiHF8HhmN0YwEQrLWfgRPIRLmoHJQg40F7jB2HkPEmTU1kwZ8ilzo4nblVTtLJz8+uWWfjmszkrXaxCEA2fqrb2TunQDx1isRDhlEv7EJlLH7qa1tNXuo5iVkHLQbiGbPdoFzA4z+1PgMxC9s9+4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601141; c=relaxed/simple; bh=4PGbVRotP5756TPi5zGgCzLioTg3uvuxJTeoiAOcsLQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Otmm7EPVpLSl3qVnAt/+rnPnLjnK6BS5dDkjVWN1Dyg1DgG/hikvHOoC4St3MxClkqrVuElWzI6DCuUeA8QlPDKnWTvWC0cFzfNMGXLZD3PrXCxkcRvvsy/PAGuL81jIexELMCKWb2iqD39Wx6mmDDkQMUtiDy/XWwI12sX43ag= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=xgQtP+cI; arc=none smtp.client-ip=209.85.221.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="xgQtP+cI" Received: by mail-wr1-f44.google.com with SMTP id ffacd0b85a97d-36279cf6414so568496f8f.3 for ; Fri, 28 Jun 2024 11:58:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601138; x=1720205938; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=bmqPpwF2aE1B3oXsxyYtSza9my0vs08e8yNUGNiifL4=; b=xgQtP+cI8H+PX8TtnKCPJ5SGOHfbtCrNEvIuMRAJfdB/4zACBm4uA6dAdh/ZN4MQ07 /EF7To+R81rh6UKL6gJtfqKajZ7Uf701CKfuZxqcBbtuk1xsYuibIDlEc5akJr8WTJCz +XVp0wwKaRSbmgzKJOAE3Fka7Gqpj/wHg4JZySfRcN37w+0hCMy8XMhlAJVRm/9Fvube WkkoWTFgIuN2xVO0cU402NQfSckwufSF79GLi5E/6BA8Wf43HdNwND+mWvkDxU9+1Yc2 Ud21BovBXu4yjK/+1on1frgQsj+aP2KcmEbTpDMtHVIZ6EAmJwx3kE6XsMMZN7KgWWkn oPcw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601138; x=1720205938; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=bmqPpwF2aE1B3oXsxyYtSza9my0vs08e8yNUGNiifL4=; b=FNhCnljwVcaSoAF/VjZ8E+dpmsmFcyguFj8VQJzDaNcJpCpG30Y7ou9E4NppAHKprp vW+6CoRVULqiP7FqAXQ/TGrkm448tRUeURne2pE72lIrYXNP01rQu82T1uGVbRGDBh/z h3X/JbXwGK6LPduAVGYQN+Y8zGconXHpKK1MbML8Ac2MID5i8tdd6qYcrElGY+aHT2X6 HMgF2GAd7wCBgH9IA7k1q7kVU7g4nvGRAoRSXk8nK/JaU2SHYQOAMzdfi6frXiRyvvPt t0vREAwUjyooqWrGQlod6WCa2a/z+iCfb9OcZtVrX/mzefwNcH+AxsPKh3rzh/WVXeK+ acSw== X-Forwarded-Encrypted: i=1; AJvYcCXa6rV0ruAjjpC0xVzcN7N7+wYf0c9p6ZG4Tx6jdjJT3YXECUfexMDDbjw6dn8X+XzThT+kG2SQu6jrJSRl+7Cyy/IZqMGvdzSyoA== X-Gm-Message-State: AOJu0YzFZH57u/SWvJ9YOUE/nPTeG+HIq1yAZEq50v4dLHCavpdkVC8B P/Yq6dP1LDsJEwZPrqvWlDKLr/NncHiynbnU6tEl7n0YiKra9gzdEtWD4OpBiVM= X-Google-Smtp-Source: AGHT+IE796x9ccnLd6y1Wbv4u4xDL1n0EqHiwDg6EiIstpxv7NWTkeCC15DeYuKX3P2PIO/AvBduew== X-Received: by 2002:a5d:49ca:0:b0:362:42eb:6f21 with SMTP id ffacd0b85a97d-366e9465163mr10011931f8f.10.1719601137550; Fri, 28 Jun 2024 11:58:57 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.58.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:58:57 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:20 +0200 Subject: [PATCH RESEND libgpiod v2 01/18] tests: split out reusable test code into a local static library Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-1-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=18592; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=ZUXsIVxOn3owvEPkyfSvn82+dbVYuH0IKyss5JuL8Ek=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfpJMfERmNdPbhL4OzhEztRx96fu94X6n0jH 2CXouRTkyeJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H6QAKCRARpy6gFHHX ckoRD/wOafZv0kYcU5VcqKz2edGH//VbGI98Y7tILccrmVmdko6Zb5IkekxBLy0YVI7Q7zok0xp 92TQRu/LRLrfDZauFQZiqbKIS3tpW76jPukyNL+FYIx7j2DkXrChQ0hAfpg77Upz5CmSuK7LhZv qZOTV273/1sZPHHRmvfMguaA2GWT46PwTq8afDnopOnAzJB1Q9/PvCKnEQUdcngxMiKuYcS4fZY li3QTbyorh8XNrMnQhEbzrEVTW4f9LKWZmbVWdKBSo7CGGmzu3lWD244yjIuioT1O9WjsYdqtNB n0gMveLyVBchja2DYVEHSV4mEFOZwgIZ9aetisOzK8cu8y4kjUu9PLBxt9qxPAWKyfZBFNw2lYU opjxzCYw0bqP0yfta0BS3QBWjc3ZlVuHmwO/DaMORhFkSq6pnX1LLJOsk9nsvZRzcSO++MCI2d9 sKq/reis3GaHivzAQwMazfy5ArRwIRVsQL6U8Kswm+jXjBahAATp+X+PD1Hv5rFfSeSLmdkAneP y60dFVogp8KIesYmg7X2TgcOqLuWkAIHthoTFRMtHGGuW4Xg2wji7vChTYVog/1X7jR1UoB6Ip0 CKwk1bZTc1GuRoBDK2tOO4Gv8dv2NtMFmkenptifNFf+L2j1BQ9Vr5omxOFK7SLsTBLsjqahDlB fU9GK39RXmVWARA== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski In order to allow the upcoming GLib and DBus bindings to reuse the test code, let's put all common elements into reusable libtool objects and export the relevant symbols in internal headers. Signed-off-by: Bartosz Golaszewski --- configure.ac | 2 ++ tests/Makefile.am | 14 ++++---- tests/gpiod-test-helpers.c | 41 ---------------------- tests/gpiosim-glib/Makefile.am | 13 +++++++ .../gpiosim-glib.c} | 30 +++++++++++++++- .../gpiosim-glib.h} | 14 ++++++++ tests/harness/Makefile.am | 12 +++++++ tests/harness/gpiod-test-common.h | 23 ++++++++++++ tests/{ => harness}/gpiod-test.c | 0 tests/{ => harness}/gpiod-test.h | 0 tests/{gpiod-test-helpers.h => helpers.h} | 36 ++----------------- tests/tests-chip-info.c | 7 ++-- tests/tests-chip.c | 15 ++++---- tests/tests-edge-event.c | 7 ++-- tests/tests-info-event.c | 7 ++-- tests/tests-kernel-uapi.c | 7 ++-- tests/tests-line-config.c | 7 ++-- tests/tests-line-info.c | 11 +++--- tests/tests-line-request.c | 7 ++-- tests/tests-line-settings.c | 5 +-- tests/tests-misc.c | 7 ++-- tests/tests-request-config.c | 5 +-- 22 files changed, 150 insertions(+), 120 deletions(-) diff --git a/configure.ac b/configure.ac index b86eee0..d1f49ac 100644 --- a/configure.ac +++ b/configure.ac @@ -275,6 +275,8 @@ AC_CONFIG_FILES([Makefile tools/Makefile tests/Makefile tests/gpiosim/Makefile + tests/gpiosim-glib/Makefile + tests/harness/Makefile bindings/cxx/libgpiodcxx.pc bindings/Makefile bindings/cxx/Makefile diff --git a/tests/Makefile.am b/tests/Makefile.am index a5e1fe0..c89fd8d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,25 +1,23 @@ # SPDX-License-Identifier: GPL-2.0-or-later # SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski -SUBDIRS = gpiosim +SUBDIRS = gpiosim gpiosim-glib harness -AM_CFLAGS = -I$(top_srcdir)/include/ -I$(top_srcdir)/tests/gpiosim/ +AM_CFLAGS = -I$(top_srcdir)/include/ -I$(top_srcdir)/tests/gpiosim-glib/ +AM_CFLAGS += -I$(top_srcdir)/tests/harness/ AM_CFLAGS += -include $(top_builddir)/config.h AM_CFLAGS += -Wall -Wextra -g -std=gnu89 $(GLIB_CFLAGS) $(GIO_CFLAGS) AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiod-test\" LDADD = $(top_builddir)/lib/libgpiod.la LDADD += $(top_builddir)/tests/gpiosim/libgpiosim.la +LDADD += $(top_builddir)/tests/gpiosim-glib/libgpiosim-glib.la +LDADD += $(top_builddir)/tests/harness/libgpiod-test-harness.la LDADD += $(GLIB_LIBS) $(GIO_LIBS) noinst_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 \ + helpers.h \ tests-chip.c \ tests-chip-info.c \ tests-edge-event.c \ diff --git a/tests/gpiod-test-helpers.c b/tests/gpiod-test-helpers.c deleted file mode 100644 index 7e5b396..0000000 --- a/tests/gpiod-test-helpers.c +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski */ - -/* - * 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 GPIOSimLineName *names) -{ - g_autoptr(GVariantBuilder) builder = NULL; - const GPIOSimLineName *name; - - 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); - - return g_variant_ref_sink(g_variant_new("a(us)", builder)); -} - -GVariant *gpiod_test_package_hogs(const GPIOSimHog *hogs) -{ - g_autoptr(GVariantBuilder) builder = NULL; - const GPIOSimHog *hog; - - 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); - - return g_variant_ref_sink(g_variant_new("a(usi)", builder)); -} diff --git a/tests/gpiosim-glib/Makefile.am b/tests/gpiosim-glib/Makefile.am new file mode 100644 index 0000000..1c01629 --- /dev/null +++ b/tests/gpiosim-glib/Makefile.am @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski + +noinst_LTLIBRARIES = libgpiosim-glib.la +libgpiosim_glib_la_SOURCES = \ + gpiosim-glib.c \ + gpiosim-glib.h + +AM_CFLAGS = -I$(top_srcdir)/tests/gpiosim/ +AM_CFLAGS += -include $(top_builddir)/config.h +AM_CFLAGS += -Wall -Wextra -g -std=gnu89 $(GLIB_CFLAGS) $(GIO_CFLAGS) +AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiosim-glib\" +libgpiosim_glib_la_LDFLAGS = -lgpiosim diff --git a/tests/gpiod-test-sim.c b/tests/gpiosim-glib/gpiosim-glib.c similarity index 93% rename from tests/gpiod-test-sim.c rename to tests/gpiosim-glib/gpiosim-glib.c index ac6c71a..4eaeace 100644 --- a/tests/gpiod-test-sim.c +++ b/tests/gpiosim-glib/gpiosim-glib.c @@ -6,7 +6,7 @@ #include #include -#include "gpiod-test-sim.h" +#include "gpiosim-glib.h" G_DEFINE_QUARK(g-gpiosim-error, g_gpiosim_error); @@ -462,3 +462,31 @@ void g_gpiosim_chip_set_pull(GPIOSimChip *chip, guint offset, GPIOSimPull pull) g_critical("Unable to set the pull setting for simulated line: %s", g_strerror(errno)); } + +GVariant *g_gpiosim_package_line_names(const GPIOSimLineName *names) +{ + g_autoptr(GVariantBuilder) builder = NULL; + const GPIOSimLineName *name; + + 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); + + return g_variant_ref_sink(g_variant_new("a(us)", builder)); +} + +GVariant *g_gpiosim_package_hogs(const GPIOSimHog *hogs) +{ + g_autoptr(GVariantBuilder) builder = NULL; + const GPIOSimHog *hog; + + 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); + + return g_variant_ref_sink(g_variant_new("a(usi)", builder)); +} diff --git a/tests/gpiod-test-sim.h b/tests/gpiosim-glib/gpiosim-glib.h similarity index 86% rename from tests/gpiod-test-sim.h rename to tests/gpiosim-glib/gpiosim-glib.h index f6a4bf0..fa76736 100644 --- a/tests/gpiod-test-sim.h +++ b/tests/gpiosim-glib/gpiosim-glib.h @@ -74,6 +74,20 @@ void g_gpiosim_chip_set_pull(GPIOSimChip *self, guint offset, GPIOSimPull pull); _val; \ }) +typedef struct { + guint offset; + const gchar *name; +} GPIOSimLineName; + +typedef struct { + guint offset; + const gchar *name; + GPIOSimDirection direction; +} GPIOSimHog; + +GVariant *g_gpiosim_package_line_names(const GPIOSimLineName *names); +GVariant *g_gpiosim_package_hogs(const GPIOSimHog *hogs); + G_END_DECLS #endif /* __GPIOD_TEST_SIM_H__ */ diff --git a/tests/harness/Makefile.am b/tests/harness/Makefile.am new file mode 100644 index 0000000..185c00f --- /dev/null +++ b/tests/harness/Makefile.am @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski + +noinst_LTLIBRARIES = libgpiod-test-harness.la +libgpiod_test_harness_la_SOURCES = \ + gpiod-test.c \ + gpiod-test.h \ + gpiod-test-common.h + +AM_CFLAGS = -include $(top_builddir)/config.h +AM_CFLAGS += -Wall -Wextra -g -std=gnu89 $(GLIB_CFLAGS) +AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiod-test\" diff --git a/tests/harness/gpiod-test-common.h b/tests/harness/gpiod-test-common.h new file mode 100644 index 0000000..7aaec05 --- /dev/null +++ b/tests/harness/gpiod-test-common.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski */ + +#ifndef __GPIOD_TEST_COMMON_H__ +#define __GPIOD_TEST_COMMON_H__ + +#include + +#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) + +#endif /* __GPIOD_TEST_COMMON_H__ */ diff --git a/tests/gpiod-test.c b/tests/harness/gpiod-test.c similarity index 100% rename from tests/gpiod-test.c rename to tests/harness/gpiod-test.c diff --git a/tests/gpiod-test.h b/tests/harness/gpiod-test.h similarity index 100% rename from tests/gpiod-test.h rename to tests/harness/gpiod-test.h diff --git a/tests/gpiod-test-helpers.h b/tests/helpers.h similarity index 87% rename from tests/gpiod-test-helpers.h rename to tests/helpers.h index 41791a3..ecb7baf 100644 --- a/tests/gpiod-test-helpers.h +++ b/tests/helpers.h @@ -1,14 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski */ +/* SPDX-FileCopyrightText: 2022 Bartosz Golaszewski */ #ifndef __GPIOD_TEST_HELPERS_H__ #define __GPIOD_TEST_HELPERS_H__ -#include #include #include - -#include "gpiod-test-sim.h" +#include /* * These typedefs are needed to make g_autoptr work - it doesn't accept @@ -49,20 +47,6 @@ 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); \ @@ -184,20 +168,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(struct_gpiod_edge_event_buffer, } while (0) #define gpiod_test_expect_errno(_expected) \ - g_assert_cmpint(_expected, ==, errno) - -typedef struct { - guint offset; - const gchar *name; -} GPIOSimLineName; - -typedef struct { - guint offset; - const gchar *name; - GPIOSimDirection direction; -} GPIOSimHog; - -GVariant *gpiod_test_package_line_names(const GPIOSimLineName *names); -GVariant *gpiod_test_package_hogs(const GPIOSimHog *hogs); + g_assert_cmpint((_expected), ==, errno) #endif /* __GPIOD_TEST_HELPERS_H__ */ diff --git a/tests/tests-chip-info.c b/tests/tests-chip-info.c index db76385..7b2e857 100644 --- a/tests/tests-chip-info.c +++ b/tests/tests-chip-info.c @@ -4,10 +4,11 @@ #include #include #include +#include +#include +#include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" -#include "gpiod-test-sim.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "chip-info" diff --git a/tests/tests-chip.c b/tests/tests-chip.c index 815b4c7..13e3f61 100644 --- a/tests/tests-chip.c +++ b/tests/tests-chip.c @@ -4,10 +4,11 @@ #include #include #include +#include +#include +#include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" -#include "gpiod-test-sim.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "chip" @@ -89,7 +90,7 @@ GPIOD_TEST_CASE(find_line_bad) g_autoptr(GPIOSimChip) sim = NULL; g_autoptr(struct_gpiod_chip) chip = NULL; - g_autoptr(GVariant) vnames = gpiod_test_package_line_names(names); + g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names); sim = g_gpiosim_chip_new( "num-lines", 8, @@ -116,7 +117,7 @@ GPIOD_TEST_CASE(find_line_good) g_autoptr(GPIOSimChip) sim = NULL; g_autoptr(struct_gpiod_chip) chip = NULL; - g_autoptr(GVariant) vnames = gpiod_test_package_line_names(names); + g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names); sim = g_gpiosim_chip_new( "num-lines", 8, @@ -142,7 +143,7 @@ GPIOD_TEST_CASE(find_line_duplicate) g_autoptr(GPIOSimChip) sim = NULL; g_autoptr(struct_gpiod_chip) chip = NULL; - g_autoptr(GVariant) vnames = gpiod_test_package_line_names(names); + g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names); sim = g_gpiosim_chip_new( "num-lines", 8, @@ -165,7 +166,7 @@ GPIOD_TEST_CASE(find_line_non_standard_names) { } }; - g_autoptr(GVariant) vnames = gpiod_test_package_line_names(names); + g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names); g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, "line-names", vnames, NULL); diff --git a/tests/tests-edge-event.c b/tests/tests-edge-event.c index b744ca5..6389455 100644 --- a/tests/tests-edge-event.c +++ b/tests/tests-edge-event.c @@ -3,11 +3,12 @@ #include #include +#include +#include +#include #include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" -#include "gpiod-test-sim.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "edge-event" diff --git a/tests/tests-info-event.c b/tests/tests-info-event.c index cbd9e9e..e014500 100644 --- a/tests/tests-info-event.c +++ b/tests/tests-info-event.c @@ -3,11 +3,12 @@ #include #include +#include +#include +#include #include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" -#include "gpiod-test-sim.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "info-event" diff --git a/tests/tests-kernel-uapi.c b/tests/tests-kernel-uapi.c index e54cfcc..ff220fc 100644 --- a/tests/tests-kernel-uapi.c +++ b/tests/tests-kernel-uapi.c @@ -4,10 +4,11 @@ #include #include #include +#include +#include +#include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" -#include "gpiod-test-sim.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "kernel-uapi" diff --git a/tests/tests-line-config.c b/tests/tests-line-config.c index 469500b..b61a445 100644 --- a/tests/tests-line-config.c +++ b/tests/tests-line-config.c @@ -4,10 +4,11 @@ #include #include #include +#include +#include +#include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" -#include "gpiod-test-sim.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "line-config" diff --git a/tests/tests-line-info.c b/tests/tests-line-info.c index cf2c650..92cd7e0 100644 --- a/tests/tests-line-info.c +++ b/tests/tests-line-info.c @@ -4,10 +4,11 @@ #include #include #include +#include +#include +#include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" -#include "gpiod-test-sim.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "line-info" @@ -64,8 +65,8 @@ GPIOD_TEST_CASE(line_info_basic_properties) g_autoptr(struct_gpiod_chip) chip = NULL; g_autoptr(struct_gpiod_line_info) info4 = NULL; g_autoptr(struct_gpiod_line_info) info6 = NULL; - g_autoptr(GVariant) vnames = gpiod_test_package_line_names(names); - g_autoptr(GVariant) vhogs = gpiod_test_package_hogs(hogs); + g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names); + g_autoptr(GVariant) vhogs = g_gpiosim_package_hogs(hogs); sim = g_gpiosim_chip_new( "num-lines", 8, diff --git a/tests/tests-line-request.c b/tests/tests-line-request.c index 7bba078..dd4e9a8 100644 --- a/tests/tests-line-request.c +++ b/tests/tests-line-request.c @@ -3,10 +3,11 @@ #include #include +#include +#include +#include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" -#include "gpiod-test-sim.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "line-request" diff --git a/tests/tests-line-settings.c b/tests/tests-line-settings.c index b86fd26..18fde50 100644 --- a/tests/tests-line-settings.c +++ b/tests/tests-line-settings.c @@ -4,9 +4,10 @@ #include #include #include +#include +#include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "line-settings" diff --git a/tests/tests-misc.c b/tests/tests-misc.c index 240dd02..9d4f3de 100644 --- a/tests/tests-misc.c +++ b/tests/tests-misc.c @@ -4,11 +4,12 @@ #include #include #include +#include +#include +#include #include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" -#include "gpiod-test-sim.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "misc" diff --git a/tests/tests-request-config.c b/tests/tests-request-config.c index d3c679a..a38befd 100644 --- a/tests/tests-request-config.c +++ b/tests/tests-request-config.c @@ -3,9 +3,10 @@ #include #include +#include +#include -#include "gpiod-test.h" -#include "gpiod-test-helpers.h" +#include "helpers.h" #define GPIOD_TEST_GROUP "request-config" From patchwork Fri Jun 28 18:58:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808533 Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F261855887 for ; Fri, 28 Jun 2024 18:59:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601144; cv=none; b=MR+S6WQ/xbPmGg5qkI4Vwn8MkgsXPDYOo0F2cwmDQ7X3bGq6rbVlxxA2BZHylaIxCR11tp43z1m5fYKaxv12e7Ha6lu+swXSBD91LoqanOQswpnkYPg3WkGNYVFCuMAsEzAZLhU5QPiEv6Xon63NalpJXS8EFYDdJnin0qW992g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601144; c=relaxed/simple; bh=k2xbLX8kmYM7/Q/dLI4NGEd0yPQHmi443C7fSaQRRJk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=i2AbamsQHfgnXA6glHYM0SCmICpFInggqEwWPhXQ0+Si/YyM5+JUSM9GsQViffl3xv+ZhlKUMaAnuM9pjsmu6QTX4btwng1AGe2LPsGTeYTTfpZwI7PLOfJsZaS5shJi9K6XvzcXhcJlILx39BQTQLE1UB+YQ3cb4o2kaLYhHPU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=VTKcQ9rD; arc=none smtp.client-ip=209.85.221.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="VTKcQ9rD" Received: by mail-wr1-f43.google.com with SMTP id ffacd0b85a97d-3676447928bso549795f8f.0 for ; Fri, 28 Jun 2024 11:59:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601139; x=1720205939; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=x8OAXQw9s2MrfcewrXeB2jj3VO7TisVuW3r6LI6Onag=; b=VTKcQ9rDMrRORu8UeUV8sqFqxAtvRGELLS0cDZJ9TVzRC+TB23IST+tNq3AsnucOKG 1S4bGwWrdOAdbQXdSueCKTJsyBSAxgAvSvpCTKBLqJdfDdtEUgSsPmFUVhSkoA0JtMbm xoTVUDcVaeILvCd+doixKQKTixvAz2bHYxq9y1nzINuQeHGOrQtXNanR4MAK6IwNQpFG 1zSzqgyqexZ0C3xdpAqWlWWgd9RmOEV1YUWtOKCc75Mv6uuQSDXAn4nrsEKNIUs8uTUD AckXbVIvnINRtnKLyaN9IjAezywRdgv6/MACe3yrlaZCDQGuxzyWgAh1TuYiFgxvJl4y S27A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601139; x=1720205939; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=x8OAXQw9s2MrfcewrXeB2jj3VO7TisVuW3r6LI6Onag=; b=Luh2UUBBiJuj5Gvb9jWLEuXgBAL5WaeLsZQ/7RIorpl/t4iuwJq0B4OlrP1ipLie/H cbDpz9KzThavb1zdz+aWvI1cKNRPPrFlelGQ+iU6HsCXMbZs/hB7JbnqpR3/KzPnhFjV OXwMmspbgD/j0B0F2CvEyUvJ8SE6kFY9NFs0iOW5HFicX6wypUBrgFwvwz34opEnm2Id NhRt0XnEqTztUacej0KKxk/7rImIQ/ouqUct8ka7nMCKTWz8UtHkxiInpsqq0XsH0Smh MW+0uQGqRkqgs6DpK2NSB+Wts6+6qOcR33ULD6DTTw197DiahNC20OkebGgvbfHZeJ90 To/A== X-Forwarded-Encrypted: i=1; AJvYcCUGuK8qpaW8MWdLlhNj6v6l+Jwdi9gzmWD28qyU2BXOTsiCvwOfVSh7BwU80xlnpbXWXDNObWG/lrf7caDhx3PyY+FLgPCg1m/hFg== X-Gm-Message-State: AOJu0YykNFOQi1X/klNy2YbDvjgS6UxHMcDq8QLa676JCow0liT+hpfp B80YEo/JuEpJupzH8eEjsci3eH4cig6giZj2ghCD0fYeRXdkLPe8tWAGoB6GFwM= X-Google-Smtp-Source: AGHT+IGZAKLrqVsMkELvqUT4bbYGXAWnLT2L0ILGhUj42uW9NneRylBkq+6KQfi7tlfUC+ESzTdj+g== X-Received: by 2002:a5d:400a:0:b0:362:e1f5:db0e with SMTP id ffacd0b85a97d-366e94d17b5mr10200983f8f.32.1719601139124; Fri, 28 Jun 2024 11:58:59 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.58.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:58:58 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:21 +0200 Subject: [PATCH RESEND libgpiod v2 02/18] tests: split out the common test code for bash scripts Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-2-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=49419; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=474QOSsO92ZhWiUzyCtU/8aEDAzo7Vj87W4OmVWhYnM=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfp2ayMCy/XAVqFV5q2Xi+jOVPPm6xe9ogen l+Qz0Bw7ziJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H6QAKCRARpy6gFHHX cvWcD/9r5m6zHRG70xg5QxIjKfMAvZuRcAmtVxt87VTv/7nr+34qCocviq+ZZGVgJSSzMqIrZwI qkDvB3zLrWcr+LG2E6ysvc+nOg5SzMACAVqUSyYA+Fd3nraA3jVmw+aJuO/4T9765RDiCZWXA3k RJWVBri9Hham++ZsLsrW+i3MGK1ssoZMEfxP4SDRzlQxjeW/b1/mHNIjzIyo2dgpJCghYbh3w2R fHALZiceQucgAdgV7r3ih639gkqFhxzZVvz5EBtXk96e//AYhlutFYmU6mVDZ6L/VkIZstWeBHb 6JhFBKLLzK0SNqEhbN7UoZyRoWjwRGpI2Q6KWJ3pEqoZ7J+UmcSkS8aqKYl5gtpHMDxPjBSgCsi Rp6ZMETvYMA9z4Jk3IYE69yDaPEwRpBFkmjKL3bEQR5KWhWb7eVEDYVLeyRuZi2EBlVv2UV5B/N eRKzmGVH1nF7bHzCGytaxzkm8WZUoObI7xrY+hDJRAd26ujLXfGcGeo43v11T2ybKYe199lEPnO /8/oIpepKNk/6IixR4s87IWJFN0o0ylBhgAP0Q1t0uClqE3rumsGrUrLl1uy+5iW4XSDtBj+94a +JXST7hYGZTSA82UoDfsKQJRcIdfNLlKztCmYMhYJT9uc0FbSNyA6SBc7PJ2ABemfs/uH1bw4Kb FU/iNsJfWVaBTcQ== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski In order to allow the upcoming DBus command-line client tests to reuse the existing bash test harness, let's put the common code into an importable file and rename run_tool to run_prog to reflect that it now can run any program. Signed-off-by: Bartosz Golaszewski --- configure.ac | 1 + tests/Makefile.am | 2 +- tests/scripts/Makefile.am | 4 + tests/scripts/gpiod-bash-test-helper.inc | 330 ++++++++++++++++++ tools/gpio-tools-test.bash | 566 +++++++------------------------ 5 files changed, 458 insertions(+), 445 deletions(-) diff --git a/configure.ac b/configure.ac index d1f49ac..93d9d75 100644 --- a/configure.ac +++ b/configure.ac @@ -277,6 +277,7 @@ AC_CONFIG_FILES([Makefile tests/gpiosim/Makefile tests/gpiosim-glib/Makefile tests/harness/Makefile + tests/scripts/Makefile bindings/cxx/libgpiodcxx.pc bindings/Makefile bindings/cxx/Makefile diff --git a/tests/Makefile.am b/tests/Makefile.am index c89fd8d..7049d21 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later # SPDX-FileCopyrightText: 2017-2022 Bartosz Golaszewski -SUBDIRS = gpiosim gpiosim-glib harness +SUBDIRS = gpiosim gpiosim-glib harness scripts AM_CFLAGS = -I$(top_srcdir)/include/ -I$(top_srcdir)/tests/gpiosim-glib/ AM_CFLAGS += -I$(top_srcdir)/tests/harness/ diff --git a/tests/scripts/Makefile.am b/tests/scripts/Makefile.am new file mode 100644 index 0000000..5766593 --- /dev/null +++ b/tests/scripts/Makefile.am @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +EXTRA_DIST = gpiod-bash-test-helper.inc diff --git a/tests/scripts/gpiod-bash-test-helper.inc b/tests/scripts/gpiod-bash-test-helper.inc new file mode 100644 index 0000000..d0f8a6d --- /dev/null +++ b/tests/scripts/gpiod-bash-test-helper.inc @@ -0,0 +1,330 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski +# SPDX-FileCopyrightText: 2022 Kent Gibson +# SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +# Simple test harness for the gpio-tools. + +# Where output from the dut is stored (must be used together +# with SHUNIT_TMPDIR). +DUT_OUTPUT=gpio-tools-test-output + +# Save the PID of coprocess - otherwise we won't be able to wait for it +# once it exits as the COPROC_PID will be cleared. +DUT_PID="" + +# mappings from local name to system chip name, path, dev name +declare -A GPIOSIM_CHIP_NAME +declare -A GPIOSIM_CHIP_PATH +declare -A GPIOSIM_DEV_NAME +GPIOSIM_CONFIGFS="/sys/kernel/config/gpio-sim" +GPIOSIM_SYSFS="/sys/devices/platform/" +GPIOSIM_APP_NAME="gpio-tools-test" + +MIN_KERNEL_VERSION="5.17.4" +MIN_SHUNIT_VERSION="2.1.8" + +# Run the command in $@ and fail the test if the command succeeds. +assert_fail() { + "$@" || return 0 + fail " '$*': command did not fail as expected" +} + +# Check if the string in $2 matches against the pattern in $1. +regex_matches() { + [[ $2 =~ $1 ]] + assertEquals " '$2' did not match '$1':" "0" "$?" +} + +output_contains_line() { + assertContains "$1" "$output" +} + +output_is() { + assertEquals " output:" "$1" "$output" +} + +num_lines_is() { + [ "$1" -eq "0" ] || [ -z "$output" ] && return 0 + local NUM_LINES + NUM_LINES=$(echo "$output" | wc -l) + assertEquals " number of lines:" "$1" "$NUM_LINES" +} + +status_is() { + assertEquals " status:" "$1" "$status" +} + +# Same as above but match against the regex pattern in $1. +output_regex_match() { + [[ "$output" =~ $1 ]] + assertEquals " '$output' did not match '$1'" "0" "$?" +} + +gpiosim_chip() { + local VAR=$1 + local NAME=${GPIOSIM_APP_NAME}-$$-${VAR} + local DEVPATH=$GPIOSIM_CONFIGFS/$NAME + local BANKPATH=$DEVPATH/bank0 + + mkdir -p "$BANKPATH" + + for ARG in "$@" + do + local KEY VAL + KEY=$(echo "$ARG" | cut -d"=" -f1) + VAL=$(echo "$ARG" | cut -d"=" -f2) + + if [ "$KEY" = "num_lines" ] + then + echo "$VAL" > "$BANKPATH/num_lines" + elif [ "$KEY" = "line_name" ] + then + local OFFSET LINENAME + OFFSET=$(echo "$VAL" | cut -d":" -f1) + LINENAME=$(echo "$VAL" | cut -d":" -f2) + local LINEPATH=$BANKPATH/line$OFFSET + + mkdir -p "$LINEPATH" + echo "$LINENAME" > "$LINEPATH/name" + fi + done + + echo 1 > "$DEVPATH/live" + + local CHIP_NAME + CHIP_NAME=$(<"$BANKPATH/chip_name") + GPIOSIM_CHIP_NAME[$1]=$CHIP_NAME + GPIOSIM_CHIP_PATH[$1]="/dev/$CHIP_NAME" + GPIOSIM_DEV_NAME[$1]=$(<"$DEVPATH/dev_name") +} + +gpiosim_chip_number() { + local NAME=${GPIOSIM_CHIP_NAME[$1]} + echo "${NAME#gpiochip}" +} + +gpiosim_chip_symlink() { + GPIOSIM_CHIP_LINK="$2/${GPIOSIM_APP_NAME}-$$-lnk" + ln -s "${GPIOSIM_CHIP_PATH[$1]}" "$GPIOSIM_CHIP_LINK" +} + +gpiosim_chip_symlink_cleanup() { + if [ -n "$GPIOSIM_CHIP_LINK" ] + then + rm "$GPIOSIM_CHIP_LINK" + fi + unset GPIOSIM_CHIP_LINK +} + +gpiosim_set_pull() { + local OFFSET=$2 + local PULL=$3 + local DEVNAME=${GPIOSIM_DEV_NAME[$1]} + local CHIPNAME=${GPIOSIM_CHIP_NAME[$1]} + + echo "$PULL" > "$GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/pull" +} + +gpiosim_check_value() { + local OFFSET=$2 + local EXPECTED=$3 + local DEVNAME=${GPIOSIM_DEV_NAME[$1]} + local CHIPNAME=${GPIOSIM_CHIP_NAME[$1]} + + VAL=$(<"$GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/value") + [ "$VAL" = "$EXPECTED" ] +} + +gpiosim_wait_value() { + local OFFSET=$2 + local EXPECTED=$3 + local DEVNAME=${GPIOSIM_DEV_NAME[$1]} + local CHIPNAME=${GPIOSIM_CHIP_NAME[$1]} + local PORT=$GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/value + + for _i in {1..30}; do + [ "$(<"$PORT")" = "$EXPECTED" ] && return + sleep 0.01 + done + return 1 +} + +gpiosim_cleanup() { + for CHIP in "${!GPIOSIM_CHIP_NAME[@]}" + do + local NAME=${GPIOSIM_APP_NAME}-$$-$CHIP + + local DEVPATH=$GPIOSIM_CONFIGFS/$NAME + + echo 0 > "$DEVPATH/live" + find "$DEVPATH" -type d -name hog -exec rmdir '{}' '+' + find "$DEVPATH" -type d -name "line*" -exec rmdir '{}' '+' + find "$DEVPATH" -type d -name "bank*" -exec rmdir '{}' '+' + rmdir "$DEVPATH" + done + + gpiosim_chip_symlink_cleanup + + GPIOSIM_CHIP_NAME=() + GPIOSIM_CHIP_PATH=() + GPIOSIM_DEV_NAME=() +} + +run_prog() { + # Executables to test are expected to be in the same directory as the + # testing script. + cmd=$1 + shift + output=$(timeout 10s "$SOURCE_DIR/$cmd" "$@" 2>&1) + status=$? +} + +dut_run() { + cmd=$1 + shift + coproc timeout 10s "$SOURCE_DIR/$cmd" "$@" 2>&1 + DUT_PID=$COPROC_PID + read -r -t1 -n1 -u "${COPROC[0]}" DUT_FIRST_CHAR +} + +dut_run_redirect() { + cmd=$1 + shift + coproc timeout 10s "$SOURCE_DIR/$cmd" "$@" > "$SHUNIT_TMPDIR/$DUT_OUTPUT" 2>&1 + DUT_PID=$COPROC_PID + # give the process time to spin up + # FIXME - find a better solution + sleep 0.2 +} + +dut_read_redirect() { + output=$(<"$SHUNIT_TMPDIR/$DUT_OUTPUT") + local ORIG_IFS="$IFS" + IFS=$'\n' mapfile -t lines <<< "$output" + IFS="$ORIG_IFS" +} + +dut_read() { + local LINE + lines=() + while read -r -t 0.2 -u "${COPROC[0]}" LINE + do + if [ -n "$DUT_FIRST_CHAR" ] + then + LINE=${DUT_FIRST_CHAR}${LINE} + unset DUT_FIRST_CHAR + fi + lines+=("$LINE") + done + output="${lines[*]}" +} + +dut_readable() { + read -t 0 -u "${COPROC[0]}" LINE +} + +dut_flush() { + local _JUNK + lines=() + output= + unset DUT_FIRST_CHAR + while read -t 0 -u "${COPROC[0]}" _JUNK + do + read -r -t 0.1 -u "${COPROC[0]}" _JUNK || true + done +} + +# check the next line of output matches the regex +dut_regex_match() { + PATTERN=$1 + + read -r -t 0.2 -u "${COPROC[0]}" LINE || (echo Timeout && false) + if [ -n "$DUT_FIRST_CHAR" ] + then + LINE=${DUT_FIRST_CHAR}${LINE} + unset DUT_FIRST_CHAR + fi + [[ $LINE =~ $PATTERN ]] + assertEquals "'$LINE' did not match '$PATTERN'" "0" "$?" +} + +dut_write() { + echo "$@" >&"${COPROC[1]}" +} + +dut_kill() { + kill "$@" "$DUT_PID" +} + +dut_wait() { + wait "$DUT_PID" + export status=$? + unset DUT_PID +} + +dut_cleanup() { + if [ -n "$DUT_PID" ] + then + kill -SIGTERM "$DUT_PID" 2> /dev/null + wait "$DUT_PID" || false + fi + rm -f "$SHUNIT_TMPDIR/$DUT_OUTPUT" +} + +tearDown() { + dut_cleanup + gpiosim_cleanup +} + +request_release_line() { + "$SOURCE_DIR/gpioget" -c "$@" >/dev/null +} + +die() { + echo "$@" 1>&2 + exit 1 +} + +# Must be done after we sources shunit2 as we need SHUNIT_VERSION to be set. +oneTimeSetUp() { + test "$SHUNIT_VERSION" = "$MIN_SHUNIT_VERSION" && return 0 + local FIRST + FIRST=$(printf "%s\n%s\n" "$SHUNIT_VERSION" "$MIN_SHUNIT_VERSION" | sort -Vr | head -1) + test "$FIRST" = "$MIN_SHUNIT_VERSION" && \ + die "minimum shunit version required is $MIN_SHUNIT_VERSION (current version is $SHUNIT_VERSION" +} + +check_kernel() { + local REQUIRED=$1 + local CURRENT + CURRENT=$(uname -r) + + SORTED=$(printf "%s\n%s" "$REQUIRED" "$CURRENT" | sort -V | head -n 1) + + if [ "$SORTED" != "$REQUIRED" ] + then + die "linux kernel version must be at least: v$REQUIRED - got: v$CURRENT" + fi +} + +check_prog() { + local PROG=$1 + + if ! which "$PROG" > /dev/null + then + die "$PROG not found - needed to run the tests" + fi +} + +# Check all required non-coreutils tools +check_prog shunit2 +check_prog modprobe +check_prog timeout + +# Check if we're running a kernel at the required version or later +check_kernel $MIN_KERNEL_VERSION + +modprobe gpio-sim || die "unable to load the gpio-sim module" +mountpoint /sys/kernel/config/ > /dev/null 2> /dev/null || \ + die "configfs not mounted at /sys/kernel/config/" diff --git a/tools/gpio-tools-test.bash b/tools/gpio-tools-test.bash index 3b93388..359960a 100755 --- a/tools/gpio-tools-test.bash +++ b/tools/gpio-tools-test.bash @@ -4,285 +4,8 @@ # SPDX-FileCopyrightText: 2022 Kent Gibson # SPDX-FileCopyrightText: 2023 Bartosz Golaszewski -# Simple test harness for the gpio-tools. - -# Where output from the dut is stored (must be used together -# with SHUNIT_TMPDIR). -DUT_OUTPUT=gpio-tools-test-output - -# Save the PID of coprocess - otherwise we won't be able to wait for it -# once it exits as the COPROC_PID will be cleared. -DUT_PID="" - -SOURCE_DIR=$(dirname "${BASH_SOURCE[0]}") - -# mappings from local name to system chip name, path, dev name -declare -A GPIOSIM_CHIP_NAME -declare -A GPIOSIM_CHIP_PATH -declare -A GPIOSIM_DEV_NAME -GPIOSIM_CONFIGFS="/sys/kernel/config/gpio-sim" -GPIOSIM_SYSFS="/sys/devices/platform/" -GPIOSIM_APP_NAME="gpio-tools-test" - -MIN_KERNEL_VERSION="5.17.4" -MIN_SHUNIT_VERSION="2.1.8" - -# Run the command in $@ and fail the test if the command succeeds. -assert_fail() { - "$@" || return 0 - fail " '$*': command did not fail as expected" -} - -# Check if the string in $2 matches against the pattern in $1. -regex_matches() { - [[ $2 =~ $1 ]] - assertEquals " '$2' did not match '$1':" "0" "$?" -} - -output_contains_line() { - assertContains "$1" "$output" -} - -output_is() { - assertEquals " output:" "$1" "$output" -} - -num_lines_is() { - [ "$1" -eq "0" ] || [ -z "$output" ] && return 0 - local NUM_LINES - NUM_LINES=$(echo "$output" | wc -l) - assertEquals " number of lines:" "$1" "$NUM_LINES" -} - -status_is() { - assertEquals " status:" "$1" "$status" -} - -# Same as above but match against the regex pattern in $1. -output_regex_match() { - [[ "$output" =~ $1 ]] - assertEquals " '$output' did not match '$1'" "0" "$?" -} - -gpiosim_chip() { - local VAR=$1 - local NAME=${GPIOSIM_APP_NAME}-$$-${VAR} - local DEVPATH=$GPIOSIM_CONFIGFS/$NAME - local BANKPATH=$DEVPATH/bank0 - - mkdir -p "$BANKPATH" - - for ARG in "$@" - do - local KEY VAL - KEY=$(echo "$ARG" | cut -d"=" -f1) - VAL=$(echo "$ARG" | cut -d"=" -f2) - - if [ "$KEY" = "num_lines" ] - then - echo "$VAL" > "$BANKPATH/num_lines" - elif [ "$KEY" = "line_name" ] - then - local OFFSET LINENAME - OFFSET=$(echo "$VAL" | cut -d":" -f1) - LINENAME=$(echo "$VAL" | cut -d":" -f2) - local LINEPATH=$BANKPATH/line$OFFSET - - mkdir -p "$LINEPATH" - echo "$LINENAME" > "$LINEPATH/name" - fi - done - - echo 1 > "$DEVPATH/live" - - local CHIP_NAME - CHIP_NAME=$(<"$BANKPATH/chip_name") - GPIOSIM_CHIP_NAME[$1]=$CHIP_NAME - GPIOSIM_CHIP_PATH[$1]="/dev/$CHIP_NAME" - GPIOSIM_DEV_NAME[$1]=$(<"$DEVPATH/dev_name") -} - -gpiosim_chip_number() { - local NAME=${GPIOSIM_CHIP_NAME[$1]} - echo "${NAME#gpiochip}" -} - -gpiosim_chip_symlink() { - GPIOSIM_CHIP_LINK="$2/${GPIOSIM_APP_NAME}-$$-lnk" - ln -s "${GPIOSIM_CHIP_PATH[$1]}" "$GPIOSIM_CHIP_LINK" -} - -gpiosim_chip_symlink_cleanup() { - if [ -n "$GPIOSIM_CHIP_LINK" ] - then - rm "$GPIOSIM_CHIP_LINK" - fi - unset GPIOSIM_CHIP_LINK -} - -gpiosim_set_pull() { - local OFFSET=$2 - local PULL=$3 - local DEVNAME=${GPIOSIM_DEV_NAME[$1]} - local CHIPNAME=${GPIOSIM_CHIP_NAME[$1]} - - echo "$PULL" > "$GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/pull" -} - -gpiosim_check_value() { - local OFFSET=$2 - local EXPECTED=$3 - local DEVNAME=${GPIOSIM_DEV_NAME[$1]} - local CHIPNAME=${GPIOSIM_CHIP_NAME[$1]} - - VAL=$(<"$GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/value") - [ "$VAL" = "$EXPECTED" ] -} - -gpiosim_wait_value() { - local OFFSET=$2 - local EXPECTED=$3 - local DEVNAME=${GPIOSIM_DEV_NAME[$1]} - local CHIPNAME=${GPIOSIM_CHIP_NAME[$1]} - local PORT=$GPIOSIM_SYSFS/$DEVNAME/$CHIPNAME/sim_gpio$OFFSET/value - - for _i in {1..30}; do - [ "$(<"$PORT")" = "$EXPECTED" ] && return - sleep 0.01 - done - return 1 -} - -gpiosim_cleanup() { - for CHIP in "${!GPIOSIM_CHIP_NAME[@]}" - do - local NAME=${GPIOSIM_APP_NAME}-$$-$CHIP - - local DEVPATH=$GPIOSIM_CONFIGFS/$NAME - - echo 0 > "$DEVPATH/live" - find "$DEVPATH" -type d -name hog -exec rmdir '{}' '+' - find "$DEVPATH" -type d -name "line*" -exec rmdir '{}' '+' - find "$DEVPATH" -type d -name "bank*" -exec rmdir '{}' '+' - rmdir "$DEVPATH" - done - - gpiosim_chip_symlink_cleanup - - GPIOSIM_CHIP_NAME=() - GPIOSIM_CHIP_PATH=() - GPIOSIM_DEV_NAME=() -} - -run_tool() { - # Executables to test are expected to be in the same directory as the - # testing script. - cmd=$1 - shift - output=$(timeout 10s "$SOURCE_DIR/$cmd" "$@" 2>&1) - status=$? -} - -dut_run() { - cmd=$1 - shift - coproc timeout 10s "$SOURCE_DIR/$cmd" "$@" 2>&1 - DUT_PID=$COPROC_PID - read -r -t1 -n1 -u "${COPROC[0]}" DUT_FIRST_CHAR -} - -dut_run_redirect() { - cmd=$1 - shift - coproc timeout 10s "$SOURCE_DIR/$cmd" "$@" > "$SHUNIT_TMPDIR/$DUT_OUTPUT" 2>&1 - DUT_PID=$COPROC_PID - # give the process time to spin up - # FIXME - find a better solution - sleep 0.2 -} - -dut_read_redirect() { - output=$(<"$SHUNIT_TMPDIR/$DUT_OUTPUT") - local ORIG_IFS="$IFS" - IFS=$'\n' mapfile -t lines <<< "$output" - IFS="$ORIG_IFS" -} - -dut_read() { - local LINE - lines=() - while read -r -t 0.2 -u "${COPROC[0]}" LINE - do - if [ -n "$DUT_FIRST_CHAR" ] - then - LINE=${DUT_FIRST_CHAR}${LINE} - unset DUT_FIRST_CHAR - fi - lines+=("$LINE") - done - output="${lines[*]}" -} - -dut_readable() { - read -t 0 -u "${COPROC[0]}" LINE -} - -dut_flush() { - local _JUNK - lines=() - output= - unset DUT_FIRST_CHAR - while read -t 0 -u "${COPROC[0]}" _JUNK - do - read -r -t 0.1 -u "${COPROC[0]}" _JUNK || true - done -} - -# check the next line of output matches the regex -dut_regex_match() { - PATTERN=$1 - - read -r -t 0.2 -u "${COPROC[0]}" LINE || (echo Timeout && false) - if [ -n "$DUT_FIRST_CHAR" ] - then - LINE=${DUT_FIRST_CHAR}${LINE} - unset DUT_FIRST_CHAR - fi - [[ $LINE =~ $PATTERN ]] - assertEquals "'$LINE' did not match '$PATTERN'" "0" "$?" -} - -dut_write() { - echo "$@" >&"${COPROC[1]}" -} - -dut_kill() { - kill "$@" "$DUT_PID" -} - -dut_wait() { - wait "$DUT_PID" - export status=$? - unset DUT_PID -} - -dut_cleanup() { - if [ -n "$DUT_PID" ] - then - kill -SIGTERM "$DUT_PID" 2> /dev/null - wait "$DUT_PID" || false - fi - rm -f "$SHUNIT_TMPDIR/$DUT_OUTPUT" -} - -tearDown() { - dut_cleanup - gpiosim_cleanup -} - -request_release_line() { - "$SOURCE_DIR/gpioget" -c "$@" >/dev/null -} +export SOURCE_DIR +SOURCE_DIR="$(dirname "${BASH_SOURCE[0]}")" # # gpiodetect test cases @@ -300,7 +23,7 @@ test_gpiodetect_all_chips() { local sim1dev=${GPIOSIM_DEV_NAME[sim1]} local sim2dev=${GPIOSIM_DEV_NAME[sim2]} - run_tool gpiodetect + run_prog gpiodetect output_regex_match "$sim0 \[${sim0dev}[-:]node0\] \(4 lines\)" output_regex_match "$sim1 \[${sim1dev}[-:]node0\] \(8 lines\)" @@ -311,7 +34,7 @@ test_gpiodetect_all_chips() { local initial_output=$output gpiosim_chip_symlink sim1 /dev - run_tool gpiodetect + run_prog gpiodetect output_is "$initial_output" status_is 0 @@ -330,21 +53,21 @@ test_gpiodetect_a_chip() { local sim2dev=${GPIOSIM_DEV_NAME[sim2]} # by name - run_tool gpiodetect "$sim0" + run_prog gpiodetect "$sim0" output_regex_match "$sim0 \[${sim0dev}[-:]node0\] \(4 lines\)" num_lines_is 1 status_is 0 # by path - run_tool gpiodetect "${GPIOSIM_CHIP_PATH[sim1]}" + run_prog gpiodetect "${GPIOSIM_CHIP_PATH[sim1]}" output_regex_match "$sim1 \[${sim1dev}[-:]node0\] \(8 lines\)" num_lines_is 1 status_is 0 # by number - run_tool gpiodetect "$(gpiosim_chip_number sim2)" + run_prog gpiodetect "$(gpiosim_chip_number sim2)" output_regex_match "$sim2 \[${sim2dev}[-:]node0\] \(16 lines\)" num_lines_is 1 @@ -352,7 +75,7 @@ test_gpiodetect_a_chip() { # by symlink gpiosim_chip_symlink sim2 . - run_tool gpiodetect "$GPIOSIM_CHIP_LINK" + run_prog gpiodetect "$GPIOSIM_CHIP_LINK" output_regex_match "$sim2 \[${sim2dev}[-:]node0\] \(16 lines\)" num_lines_is 1 @@ -371,7 +94,7 @@ test_gpiodetect_multiple_chips() { local sim1dev=${GPIOSIM_DEV_NAME[sim1]} local sim2dev=${GPIOSIM_DEV_NAME[sim2]} - run_tool gpiodetect "$sim0" "$sim1" "$sim2" + run_prog gpiodetect "$sim0" "$sim1" "$sim2" output_regex_match "$sim0 \[${sim0dev}[-:]node0\] \(4 lines\)" output_regex_match "$sim1 \[${sim1dev}[-:]node0\] \(8 lines\)" @@ -381,7 +104,7 @@ test_gpiodetect_multiple_chips() { } test_gpiodetect_with_nonexistent_chip() { - run_tool gpiodetect nonexistent-chip + run_prog gpiodetect nonexistent-chip status_is 1 output_regex_match \ @@ -396,7 +119,7 @@ test_gpioinfo_all_chips() { gpiosim_chip sim0 num_lines=4 gpiosim_chip sim1 num_lines=8 - run_tool gpioinfo + run_prog gpioinfo output_contains_line "${GPIOSIM_CHIP_NAME[sim0]} - 4 lines:" output_contains_line "${GPIOSIM_CHIP_NAME[sim1]} - 8 lines:" @@ -408,7 +131,7 @@ test_gpioinfo_all_chips() { local initial_output=$output gpiosim_chip_symlink sim1 /dev - run_tool gpioinfo + run_prog gpioinfo output_is "$initial_output" status_is 0 @@ -420,7 +143,7 @@ test_gpioinfo_all_chips_with_some_used_lines() { dut_run gpioset --banner --active-low foo=1 baz=0 - run_tool gpioinfo + run_prog gpioinfo output_contains_line "${GPIOSIM_CHIP_NAME[sim0]} - 4 lines:" output_contains_line "${GPIOSIM_CHIP_NAME[sim1]} - 8 lines:" @@ -439,7 +162,7 @@ test_gpioinfo_a_chip() { local sim1=${GPIOSIM_CHIP_NAME[sim1]} # by name - run_tool gpioinfo --chip "$sim1" + run_prog gpioinfo --chip "$sim1" output_contains_line "$sim1 - 4 lines:" output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" @@ -450,7 +173,7 @@ test_gpioinfo_a_chip() { status_is 0 # by path - run_tool gpioinfo --chip "$sim1" + run_prog gpioinfo --chip "$sim1" output_contains_line "$sim1 - 4 lines:" output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" @@ -461,7 +184,7 @@ test_gpioinfo_a_chip() { status_is 0 # by number - run_tool gpioinfo --chip "$sim1" + run_prog gpioinfo --chip "$sim1" output_contains_line "$sim1 - 4 lines:" output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" @@ -473,7 +196,7 @@ test_gpioinfo_a_chip() { # by symlink gpiosim_chip_symlink sim1 . - run_tool gpioinfo --chip "$GPIOSIM_CHIP_LINK" + run_prog gpioinfo --chip "$GPIOSIM_CHIP_LINK" output_contains_line "$sim1 - 4 lines:" output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" @@ -492,28 +215,28 @@ test_gpioinfo_a_line() { local sim1=${GPIOSIM_CHIP_NAME[sim1]} # by offset - run_tool gpioinfo --chip "$sim1" 2 + run_prog gpioinfo --chip "$sim1" 2 output_regex_match "$sim1 2\\s+\"bar\"\\s+input" num_lines_is 1 status_is 0 # by name - run_tool gpioinfo bar + run_prog gpioinfo bar output_regex_match "$sim0 5\\s+\"bar\"\\s+input" num_lines_is 1 status_is 0 # by chip and name - run_tool gpioinfo --chip "$sim1" 2 + run_prog gpioinfo --chip "$sim1" 2 output_regex_match "$sim1 2\\s+\"bar\"\\s+input" num_lines_is 1 status_is 0 # unquoted - run_tool gpioinfo --unquoted --chip "$sim1" 2 + run_prog gpioinfo --unquoted --chip "$sim1" 2 output_regex_match "$sim1 2\\s+bar\\s+input" num_lines_is 1 @@ -530,7 +253,7 @@ test_gpioinfo_first_matching_named_line() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpioinfo foobar + run_prog gpioinfo foobar output_regex_match "$sim0 3\\s+\"foobar\"\\s+input" num_lines_is 1 @@ -545,7 +268,7 @@ test_gpioinfo_multiple_lines() { local sim1=${GPIOSIM_CHIP_NAME[sim1]} # by offset - run_tool gpioinfo --chip "$sim1" 1 2 + run_prog gpioinfo --chip "$sim1" 1 2 output_regex_match "$sim1 1\\s+unnamed\\s+input" output_regex_match "$sim1 2\\s+\"baz\"\\s+input" @@ -553,7 +276,7 @@ test_gpioinfo_multiple_lines() { status_is 0 # by name - run_tool gpioinfo bar baz + run_prog gpioinfo bar baz output_regex_match "$sim0 5\\s+\"bar\"\\s+input" output_regex_match "$sim1 2\\s+\"baz\"\\s+input" @@ -561,7 +284,7 @@ test_gpioinfo_multiple_lines() { status_is 0 # by name and offset - run_tool gpioinfo --chip "$sim0" bar 3 + run_prog gpioinfo --chip "$sim0" bar 3 output_regex_match "$sim0 5\\s+\"bar\"\\s+input" output_regex_match "$sim0 3\\s+unnamed\\s+input" @@ -578,7 +301,7 @@ test_gpioinfo_line_attribute_menagerie() { dut_run gpioset --banner --active-low --bias=pull-up --drive=open-source foo=1 baz=0 - run_tool gpioinfo foo baz + run_prog gpioinfo foo baz output_regex_match \ "$sim0 1\\s+\"foo\"\\s+output active-low drive=open-source bias=pull-up consumer=\"gpioset\"" @@ -592,7 +315,7 @@ test_gpioinfo_line_attribute_menagerie() { dut_run gpioset --banner --bias=pull-down --drive=open-drain foo=1 baz=0 - run_tool gpioinfo foo baz + run_prog gpioinfo foo baz output_regex_match \ "$sim0 1\\s+\"foo\"\\s+output drive=open-drain bias=pull-down consumer=\"gpioset\"" @@ -606,7 +329,7 @@ test_gpioinfo_line_attribute_menagerie() { dut_run gpiomon --banner --bias=disabled --utc -p 10ms foo baz - run_tool gpioinfo foo baz + run_prog gpioinfo foo baz output_regex_match \ "$sim0 1\\s+\"foo\"\\s+input bias=disabled edges=both event-clock=realtime debounce-period=10ms consumer=\"gpiomon\"" @@ -620,7 +343,7 @@ test_gpioinfo_line_attribute_menagerie() { dut_run gpiomon --banner --edges=rising --localtime foo baz - run_tool gpioinfo foo baz + run_prog gpioinfo foo baz output_regex_match \ "$sim0 1\\s+\"foo\"\\s+input edges=rising event-clock=realtime consumer=\"gpiomon\"" @@ -634,7 +357,7 @@ test_gpioinfo_line_attribute_menagerie() { dut_run gpiomon --banner --edges=falling foo baz - run_tool gpioinfo foo baz + run_prog gpioinfo foo baz output_regex_match \ "$sim0 1\\s+\"foo\"\\s+input edges=falling consumer=\"gpiomon\"" @@ -650,7 +373,7 @@ test_gpioinfo_with_same_line_twice() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} # by offset - run_tool gpioinfo --chip "$sim0" 1 1 + run_prog gpioinfo --chip "$sim0" 1 1 output_regex_match "$sim0 1\\s+\"foo\"\\s+input" output_regex_match ".*lines '1' and '1' are the same line" @@ -658,7 +381,7 @@ test_gpioinfo_with_same_line_twice() { status_is 1 # by name - run_tool gpioinfo foo foo + run_prog gpioinfo foo foo output_regex_match "$sim0 1\\s+\"foo\"\\s+input" output_regex_match ".*lines 'foo' and 'foo' are the same line" @@ -666,7 +389,7 @@ test_gpioinfo_with_same_line_twice() { status_is 1 # by name and offset - run_tool gpioinfo --chip "$sim0" foo 1 + run_prog gpioinfo --chip "$sim0" foo 1 output_regex_match "$sim0 1\\s+\"foo\"\\s+input" output_regex_match ".*lines 'foo' and '1' are the same line" @@ -684,7 +407,7 @@ test_gpioinfo_all_lines_matching_name() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} local sim1=${GPIOSIM_CHIP_NAME[sim1]} - run_tool gpioinfo --strict foobar + run_prog gpioinfo --strict foobar output_regex_match "$sim0 3\\s+\"foobar\"\\s+input" output_regex_match "$sim1 2\\s+\"foobar\"\\s+input" @@ -701,7 +424,7 @@ test_gpioinfo_with_lines_strictly_by_name() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} # first by offset (to show offsets match first) - run_tool gpioinfo --chip "$sim0" 1 6 + run_prog gpioinfo --chip "$sim0" 1 6 output_regex_match "$sim0 1\\s+\"6\"\\s+input" output_regex_match "$sim0 6\\s+\"1\"\\s+input" @@ -709,7 +432,7 @@ test_gpioinfo_with_lines_strictly_by_name() { status_is 0 # then strictly by name - run_tool gpioinfo --by-name --chip "$sim0" 1 + run_prog gpioinfo --by-name --chip "$sim0" 1 output_regex_match "$sim0 6\\s+\"1\"\\s+input" num_lines_is 1 @@ -717,7 +440,7 @@ test_gpioinfo_with_lines_strictly_by_name() { } test_gpioinfo_with_nonexistent_chip() { - run_tool gpioinfo --chip nonexistent-chip + run_prog gpioinfo --chip nonexistent-chip output_regex_match \ ".*cannot find GPIO chip character device 'nonexistent-chip'" @@ -727,12 +450,12 @@ test_gpioinfo_with_nonexistent_chip() { test_gpioinfo_with_nonexistent_line() { gpiosim_chip sim0 num_lines=8 - run_tool gpioinfo nonexistent-line + run_prog gpioinfo nonexistent-line output_regex_match ".*cannot find line 'nonexistent-line'" status_is 1 - run_tool gpioinfo --chip "${GPIOSIM_CHIP_NAME[sim0]}" nonexistent-line + run_prog gpioinfo --chip "${GPIOSIM_CHIP_NAME[sim0]}" nonexistent-line output_regex_match ".*cannot find line 'nonexistent-line'" status_is 1 @@ -743,7 +466,7 @@ test_gpioinfo_with_offset_out_of_range() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpioinfo --chip "$sim0" 0 1 2 3 4 5 + run_prog gpioinfo --chip "$sim0" 0 1 2 3 4 5 output_regex_match "$sim0 0\\s+unnamed\\s+input" output_regex_match "$sim0 1\\s+unnamed\\s+input" @@ -764,12 +487,12 @@ test_gpioget_by_name() { gpiosim_set_pull sim0 1 pull-up - run_tool gpioget foo + run_prog gpioget foo output_is "\"foo\"=active" status_is 0 - run_tool gpioget --unquoted foo + run_prog gpioget --unquoted foo output_is "foo=active" status_is 0 @@ -780,12 +503,12 @@ test_gpioget_by_offset() { gpiosim_set_pull sim0 1 pull-up - run_tool gpioget --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 + run_prog gpioget --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 output_is "\"1\"=active" status_is 0 - run_tool gpioget --unquoted --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 + run_prog gpioget --unquoted --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 output_is "1=active" status_is 0 @@ -797,7 +520,7 @@ test_gpioget_by_symlink() { gpiosim_set_pull sim0 1 pull-up - run_tool gpioget --chip "$GPIOSIM_CHIP_LINK" 1 + run_prog gpioget --chip "$GPIOSIM_CHIP_LINK" 1 output_is "\"1\"=active" status_is 0 @@ -809,12 +532,12 @@ test_gpioget_by_chip_and_name() { gpiosim_set_pull sim1 3 pull-up - run_tool gpioget --chip "${GPIOSIM_CHIP_NAME[sim1]}" foo + run_prog gpioget --chip "${GPIOSIM_CHIP_NAME[sim1]}" foo output_is "\"foo\"=active" status_is 0 - run_tool gpioget --unquoted --chip "${GPIOSIM_CHIP_NAME[sim1]}" foo + run_prog gpioget --unquoted --chip "${GPIOSIM_CHIP_NAME[sim1]}" foo output_is "foo=active" status_is 0 @@ -829,7 +552,7 @@ test_gpioget_first_matching_named_line() { gpiosim_set_pull sim0 3 pull-up - run_tool gpioget foobar + run_prog gpioget foobar output_is "\"foobar\"=active" status_is 0 @@ -843,7 +566,7 @@ test_gpioget_multiple_lines() { gpiosim_set_pull sim0 5 pull-up gpiosim_set_pull sim0 7 pull-up - run_tool gpioget --unquoted --chip "${GPIOSIM_CHIP_NAME[sim0]}" 0 1 2 3 4 5 6 7 + run_prog gpioget --unquoted --chip "${GPIOSIM_CHIP_NAME[sim0]}" 0 1 2 3 4 5 6 7 output_is \ "0=inactive 1=inactive 2=active 3=active 4=inactive 5=active 6=inactive 7=active" @@ -859,7 +582,7 @@ test_gpioget_multiple_lines_by_name_and_offset() { gpiosim_set_pull sim0 4 pull-up gpiosim_set_pull sim0 6 pull-up - run_tool gpioget --chip "$sim0" 0 foo 4 bar + run_prog gpioget --chip "$sim0" 0 foo 4 bar output_is "\"0\"=inactive \"foo\"=active \"4\"=active \"bar\"=active" status_is 0 @@ -872,7 +595,7 @@ test_gpioget_multiple_lines_across_multiple_chips() { gpiosim_set_pull sim0 1 pull-up gpiosim_set_pull sim1 4 pull-up - run_tool gpioget baz bar foo xyz + run_prog gpioget baz bar foo xyz output_is "\"baz\"=inactive \"bar\"=inactive \"foo\"=active \"xyz\"=active" status_is 0 @@ -888,7 +611,7 @@ test_gpioget_with_numeric_values() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpioget --numeric --chip "$sim0" 0 1 2 3 4 5 6 7 + run_prog gpioget --numeric --chip "$sim0" 0 1 2 3 4 5 6 7 output_is "0 0 1 1 0 1 0 1" status_is 0 @@ -904,7 +627,7 @@ test_gpioget_with_active_low() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpioget --active-low --unquoted --chip "$sim0" 0 1 2 3 4 5 6 7 + run_prog gpioget --active-low --unquoted --chip "$sim0" 0 1 2 3 4 5 6 7 output_is \ "0=active 1=active 2=inactive 3=inactive 4=active 5=inactive 6=active 7=inactive" @@ -917,7 +640,7 @@ test_gpioget_with_consumer() { dut_run gpionotify --banner -F "%l %E %C" foo baz - run_tool gpioget --consumer gpio-tools-tests foo baz + run_prog gpioget --consumer gpio-tools-tests foo baz status_is 0 dut_read @@ -935,7 +658,7 @@ test_gpioget_with_pull_up() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpioget --bias=pull-up --unquoted --chip "$sim0" 0 1 2 3 4 5 6 7 + run_prog gpioget --bias=pull-up --unquoted --chip "$sim0" 0 1 2 3 4 5 6 7 output_is \ "0=active 1=active 2=active 3=active 4=active 5=active 6=active 7=active" @@ -952,7 +675,7 @@ test_gpioget_with_pull_down() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpioget --bias=pull-down --unquoted --chip "$sim0" 0 1 2 3 4 5 6 7 + run_prog gpioget --bias=pull-down --unquoted --chip "$sim0" 0 1 2 3 4 5 6 7 output_is \ "0=inactive 1=inactive 2=inactive 3=inactive 4=inactive 5=inactive 6=inactive 7=inactive" @@ -965,31 +688,31 @@ test_gpioget_with_direction_as_is() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} # flip to output - run_tool gpioset -t0 foo=1 + run_prog gpioset -t0 foo=1 status_is 0 - run_tool gpioinfo foo + run_prog gpioinfo foo output_regex_match "$sim0 1\\s+\"foo\"\\s+output" status_is 0 - run_tool gpioget --as-is foo + run_prog gpioget --as-is foo # note gpio-sim reverts line to its pull when released output_is "\"foo\"=inactive" status_is 0 - run_tool gpioinfo foo + run_prog gpioinfo foo output_regex_match "$sim0 1\\s+\"foo\"\\s+output" status_is 0 # whereas the default behaviour forces to input - run_tool gpioget foo + run_prog gpioget foo # note gpio-sim reverts line to its pull when released # (defaults to pull-down) output_is "\"foo\"=inactive" status_is 0 - run_tool gpioinfo foo + run_prog gpioinfo foo output_regex_match "$sim0 1\\s+\"foo\"\\s+input" status_is 0 } @@ -998,7 +721,7 @@ test_gpioget_with_hold_period() { gpiosim_chip sim0 num_lines=8 line_name=1:foo # only test parsing - testing the hold-period itself is tricky - run_tool gpioget --hold-period=100ms foo + run_prog gpioget --hold-period=100ms foo output_is "\"foo\"=inactive" status_is 0 } @@ -1010,7 +733,7 @@ test_gpioget_with_strict_named_line_check() { line_name=4:xyz line_name=7:foobar gpiosim_chip sim2 num_lines=16 - run_tool gpioget --strict foobar + run_prog gpioget --strict foobar output_regex_match ".*line 'foobar' is not unique" status_is 1 @@ -1024,12 +747,12 @@ test_gpioget_with_lines_by_offset() { gpiosim_set_pull sim0 1 pull-up gpiosim_set_pull sim0 6 pull-down - run_tool gpioget --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 6 + run_prog gpioget --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 6 output_is "\"1\"=active \"6\"=inactive" status_is 0 - run_tool gpioget --unquoted --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 6 + run_prog gpioget --unquoted --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 6 output_is "1=active 6=inactive" status_is 0 @@ -1043,19 +766,19 @@ test_gpioget_with_lines_strictly_by_name() { gpiosim_set_pull sim0 1 pull-up gpiosim_set_pull sim0 6 pull-down - run_tool gpioget --by-name --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 6 + run_prog gpioget --by-name --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 6 output_is "\"1\"=inactive \"6\"=active" status_is 0 - run_tool gpioget --by-name --unquoted --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 6 + run_prog gpioget --by-name --unquoted --chip "${GPIOSIM_CHIP_NAME[sim0]}" 1 6 output_is "1=inactive 6=active" status_is 0 } test_gpioget_with_no_arguments() { - run_tool gpioget + run_prog gpioget output_regex_match ".*at least one GPIO line must be specified" status_is 1 @@ -1064,7 +787,7 @@ test_gpioget_with_no_arguments() { test_gpioget_with_chip_but_no_line_specified() { gpiosim_chip sim0 num_lines=8 - run_tool gpioget --chip "${GPIOSIM_CHIP_NAME[sim0]}" + run_prog gpioget --chip "${GPIOSIM_CHIP_NAME[sim0]}" output_regex_match ".*at least one GPIO line must be specified" status_is 1 @@ -1074,7 +797,7 @@ test_gpioget_with_offset_out_of_range() { gpiosim_chip sim0 num_lines=4 local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpioget --chip "$sim0" 0 1 2 3 4 5 + run_prog gpioget --chip "$sim0" 0 1 2 3 4 5 output_regex_match ".*offset 4 is out of range on chip '$sim0'" output_regex_match ".*offset 5 is out of range on chip '$sim0'" @@ -1082,7 +805,7 @@ test_gpioget_with_offset_out_of_range() { } test_gpioget_with_nonexistent_line() { - run_tool gpioget nonexistent-line + run_prog gpioget nonexistent-line output_regex_match ".*cannot find line 'nonexistent-line'" status_is 1 @@ -1093,31 +816,31 @@ test_gpioget_with_same_line_twice() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} # by offset - run_tool gpioget --chip "$sim0" 0 0 + run_prog gpioget --chip "$sim0" 0 0 output_regex_match ".*lines '0' and '0' are the same line" status_is 1 # by name - run_tool gpioget foo foo + run_prog gpioget foo foo output_regex_match ".*lines 'foo' and 'foo' are the same line" status_is 1 # by chip and name - run_tool gpioget --chip "$sim0" foo foo + run_prog gpioget --chip "$sim0" foo foo output_regex_match ".*lines 'foo' and 'foo' are the same line" status_is 1 # by name and offset - run_tool gpioget --chip "$sim0" foo 1 + run_prog gpioget --chip "$sim0" foo 1 output_regex_match ".*lines 'foo' and '1' are the same line" status_is 1 # by offset and name - run_tool gpioget --chip "$sim0" 1 foo + run_prog gpioget --chip "$sim0" 1 foo output_regex_match ".*lines '1' and 'foo' are the same line" status_is 1 @@ -1126,7 +849,7 @@ test_gpioget_with_same_line_twice() { test_gpioget_with_invalid_bias() { gpiosim_chip sim0 num_lines=8 - run_tool gpioget --bias=bad --chip "${GPIOSIM_CHIP_NAME[sim0]}" 0 1 + run_prog gpioget --bias=bad --chip "${GPIOSIM_CHIP_NAME[sim0]}" 0 1 output_regex_match ".*invalid bias.*" status_is 1 @@ -1135,7 +858,7 @@ test_gpioget_with_invalid_bias() { test_gpioget_with_invalid_hold_period() { gpiosim_chip sim0 num_lines=8 - run_tool gpioget --hold-period=bad --chip "${GPIOSIM_CHIP_NAME[sim0]}" 0 + run_prog gpioget --hold-period=bad --chip "${GPIOSIM_CHIP_NAME[sim0]}" 0 output_regex_match ".*invalid period.*" status_is 1 @@ -1255,7 +978,7 @@ test_gpioset_with_consumer() { dut_run gpioset --banner --consumer gpio-tools-tests foo=1 baz=0 - run_tool gpioinfo + run_prog gpioinfo output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" output_regex_match \ @@ -1627,7 +1350,7 @@ test_gpioset_with_invalid_toggle_period() { gpiosim_chip sim0 num_lines=8 line_name=1:foo line_name=4:bar \ line_name=7:baz - run_tool gpioset --toggle 1ns foo=1 bar=0 baz=0 + run_prog gpioset --toggle 1ns foo=1 bar=0 baz=0 output_regex_match ".*invalid period.*" status_is 1 @@ -1640,7 +1363,7 @@ test_gpioset_with_strict_named_line_check() { line_name=4:xyz line_name=7:foobar gpiosim_chip sim2 num_lines=16 - run_tool gpioset --strict foobar=active + run_prog gpioset --strict foobar=active output_regex_match ".*line 'foobar' is not unique" status_is 1 @@ -1697,7 +1420,7 @@ test_gpioset_interactive_after_SIGTERM() { } test_gpioset_with_no_arguments() { - run_tool gpioset + run_prog gpioset status_is 1 output_regex_match ".*at least one GPIO line value must be specified" @@ -1706,7 +1429,7 @@ test_gpioset_with_no_arguments() { test_gpioset_with_chip_but_no_line_specified() { gpiosim_chip sim0 num_lines=8 - run_tool gpioset --chip "${GPIOSIM_CHIP_NAME[sim0]}" + run_prog gpioset --chip "${GPIOSIM_CHIP_NAME[sim0]}" output_regex_match ".*at least one GPIO line value must be specified" status_is 1 @@ -1717,7 +1440,7 @@ test_gpioset_with_offset_out_of_range() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpioset --chip "$sim0" 0=1 1=1 2=1 3=1 4=1 5=1 + run_prog gpioset --chip "$sim0" 0=1 1=1 2=1 3=1 4=1 5=1 output_regex_match ".*offset 4 is out of range on chip '$sim0'" output_regex_match ".*offset 5 is out of range on chip '$sim0'" @@ -1729,7 +1452,7 @@ test_gpioset_with_invalid_hold_period() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpioset --hold-period=bad --chip "$sim0" 0=1 + run_prog gpioset --hold-period=bad --chip "$sim0" 0=1 output_regex_match ".*invalid period.*" status_is 1 @@ -1741,13 +1464,13 @@ test_gpioset_with_invalid_value() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} # by name - run_tool gpioset --chip "$sim0" 0=c + run_prog gpioset --chip "$sim0" 0=c output_regex_match ".*invalid line value.*" status_is 1 # by value - run_tool gpioset --chip "$sim0" 0=3 + run_prog gpioset --chip "$sim0" 0=3 output_regex_match ".*invalid line value.*" status_is 1 @@ -1756,7 +1479,7 @@ test_gpioset_with_invalid_value() { test_gpioset_with_invalid_offset() { gpiosim_chip sim0 num_lines=8 - run_tool gpioset --chip "${GPIOSIM_CHIP_NAME[sim0]}" 4000000000=0 + run_prog gpioset --chip "${GPIOSIM_CHIP_NAME[sim0]}" 4000000000=0 output_regex_match ".*cannot find line '4000000000'" status_is 1 @@ -1765,7 +1488,7 @@ test_gpioset_with_invalid_offset() { test_gpioset_with_invalid_bias() { gpiosim_chip sim0 num_lines=8 - run_tool gpioset --bias=bad --chip "${GPIOSIM_CHIP_NAME[sim0]}" 0=1 1=1 + run_prog gpioset --bias=bad --chip "${GPIOSIM_CHIP_NAME[sim0]}" 0=1 1=1 output_regex_match ".*invalid bias.*" status_is 1 @@ -1774,7 +1497,7 @@ test_gpioset_with_invalid_bias() { test_gpioset_with_invalid_drive() { gpiosim_chip sim0 num_lines=8 - run_tool gpioset --drive=bad --chip "${GPIOSIM_CHIP_NAME[sim0]}" 0=1 1=1 + run_prog gpioset --drive=bad --chip "${GPIOSIM_CHIP_NAME[sim0]}" 0=1 1=1 output_regex_match ".*invalid drive.*" status_is 1 @@ -1785,14 +1508,14 @@ test_gpioset_with_interactive_and_toggle() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpioset --interactive --toggle 1s --chip "$sim0" 0=1 + run_prog gpioset --interactive --toggle 1s --chip "$sim0" 0=1 output_regex_match ".*can't combine interactive with toggle" status_is 1 } test_gpioset_with_nonexistent_line() { - run_tool gpioset nonexistent-line=0 + run_prog gpioset nonexistent-line=0 output_regex_match ".*cannot find line 'nonexistent-line'" status_is 1 @@ -1804,25 +1527,25 @@ test_gpioset_with_same_line_twice() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} # by offset - run_tool gpioset --chip "$sim0" 0=1 0=1 + run_prog gpioset --chip "$sim0" 0=1 0=1 output_regex_match ".*lines '0' and '0' are the same line" status_is 1 # by name - run_tool gpioset --chip "$sim0" foo=1 foo=1 + run_prog gpioset --chip "$sim0" foo=1 foo=1 output_regex_match ".*lines 'foo' and 'foo' are the same line" status_is 1 # by name and offset - run_tool gpioset --chip "$sim0" foo=1 1=1 + run_prog gpioset --chip "$sim0" foo=1 1=1 output_regex_match ".*lines 'foo' and '1' are the same line" status_is 1 # by offset and name - run_tool gpioset --chip "$sim0" 1=1 foo=1 + run_prog gpioset --chip "$sim0" 1=1 foo=1 output_regex_match ".*lines '1' and 'foo' are the same line" status_is 1 @@ -2018,7 +1741,7 @@ test_gpiomon_with_consumer() { dut_run gpiomon --banner --consumer gpio-tools-tests foo baz - run_tool gpioinfo + run_prog gpioinfo output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" output_regex_match \ @@ -2089,7 +1812,7 @@ test_gpiomon_with_debounce_period() { dut_run gpiomon --banner --debounce-period 123us foo baz - run_tool gpioinfo + run_prog gpioinfo output_regex_match "\\s+line\\s+0:\\s+unnamed\\s+input" output_regex_match \ @@ -2195,7 +1918,7 @@ test_gpiomon_exit_after_SIGTERM() { } test_gpiomon_with_nonexistent_line() { - run_tool gpiomon nonexistent-line + run_prog gpiomon nonexistent-line status_is 1 output_regex_match ".*cannot find line 'nonexistent-line'" @@ -2207,19 +1930,19 @@ test_gpiomon_with_same_line_twice() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} # by offset - run_tool gpiomon --chip "$sim0" 0 0 + run_prog gpiomon --chip "$sim0" 0 0 output_regex_match ".*lines '0' and '0' are the same line" status_is 1 # by name - run_tool gpiomon foo foo + run_prog gpiomon foo foo output_regex_match ".*lines 'foo' and 'foo' are the same line" status_is 1 # by name and offset - run_tool gpiomon --chip "$sim0" 1 foo + run_prog gpiomon --chip "$sim0" 1 foo output_regex_match ".*lines '1' and 'foo' are the same line" status_is 1 @@ -2232,7 +1955,7 @@ test_gpiomon_with_strict_named_line_check() { line_name=4:xyz line_name=7:foobar gpiosim_chip sim2 num_lines=16 - run_tool gpiomon --strict foobar + run_prog gpiomon --strict foobar output_regex_match ".*line 'foobar' is not unique" status_is 1 @@ -2292,7 +2015,7 @@ test_gpiomon_with_lines_strictly_by_name() { } test_gpiomon_with_no_arguments() { - run_tool gpiomon + run_prog gpiomon output_regex_match ".*at least one GPIO line must be specified" status_is 1 @@ -2301,7 +2024,7 @@ test_gpiomon_with_no_arguments() { test_gpiomon_with_no_line_specified() { gpiosim_chip sim0 num_lines=8 - run_tool gpiomon --chip "${GPIOSIM_CHIP_NAME[sim0]}" + run_prog gpiomon --chip "${GPIOSIM_CHIP_NAME[sim0]}" output_regex_match ".*at least one GPIO line must be specified" status_is 1 @@ -2312,7 +2035,7 @@ test_gpiomon_with_offset_out_of_range() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpiomon --chip "$sim0" 5 + run_prog gpiomon --chip "$sim0" 5 output_regex_match ".*offset 5 is out of range on chip '$sim0'" status_is 1 @@ -2323,7 +2046,7 @@ test_gpiomon_with_invalid_bias() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpiomon --bias=bad -c "$sim0" 0 1 + run_prog gpiomon --bias=bad -c "$sim0" 0 1 output_regex_match ".*invalid bias.*" status_is 1 @@ -2334,7 +2057,7 @@ test_gpiomon_with_invalid_debounce_period() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpiomon --debounce-period bad -c "$sim0" 0 1 + run_prog gpiomon --debounce-period bad -c "$sim0" 0 1 output_regex_match ".*invalid period: bad" status_is 1 @@ -2345,7 +2068,7 @@ test_gpiomon_with_invalid_idle_timeout() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpiomon --idle-timeout bad -c "$sim0" 0 1 + run_prog gpiomon --idle-timeout bad -c "$sim0" 0 1 output_regex_match ".*invalid period: bad" status_is 1 @@ -2770,7 +2493,7 @@ test_gpionotify_exit_after_SIGTERM() { } test_gpionotify_with_nonexistent_line() { - run_tool gpionotify nonexistent-line + run_prog gpionotify nonexistent-line status_is 1 output_regex_match ".*cannot find line 'nonexistent-line'" @@ -2782,21 +2505,21 @@ test_gpionotify_with_same_line_twice() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} # by offset - run_tool gpionotify --chip "$sim0" 0 0 + run_prog gpionotify --chip "$sim0" 0 0 output_regex_match ".*lines '0' and '0' are the same line" num_lines_is 1 status_is 1 # by name - run_tool gpionotify foo foo + run_prog gpionotify foo foo output_regex_match ".*lines 'foo' and 'foo' are the same line" num_lines_is 1 status_is 1 # by name and offset - run_tool gpionotify --chip "$sim0" 1 foo + run_prog gpionotify --chip "$sim0" 1 foo output_regex_match ".*lines '1' and 'foo' are the same line" num_lines_is 1 @@ -2810,7 +2533,7 @@ test_gpionotify_with_strict_named_line_check() { line_name=4:xyz line_name=7:foobar gpiosim_chip sim2 num_lines=16 - run_tool gpionotify --strict foobar + run_prog gpionotify --strict foobar output_regex_match ".*line 'foobar' is not unique" status_is 1 @@ -2854,7 +2577,7 @@ test_gpionotify_with_lines_strictly_by_name() { } test_gpionotify_with_no_arguments() { - run_tool gpionotify + run_prog gpionotify output_regex_match ".*at least one GPIO line must be specified" status_is 1 @@ -2863,7 +2586,7 @@ test_gpionotify_with_no_arguments() { test_gpionotify_with_no_line_specified() { gpiosim_chip sim0 num_lines=8 - run_tool gpionotify --chip "${GPIOSIM_CHIP_NAME[sim0]}" + run_prog gpionotify --chip "${GPIOSIM_CHIP_NAME[sim0]}" output_regex_match ".*at least one GPIO line must be specified" status_is 1 @@ -2874,7 +2597,7 @@ test_gpionotify_with_offset_out_of_range() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpionotify --chip "$sim0" 5 + run_prog gpionotify --chip "$sim0" 5 output_regex_match ".*offset 5 is out of range on chip '$sim0'" status_is 1 @@ -2885,7 +2608,7 @@ test_gpionotify_with_invalid_idle_timeout() { local sim0=${GPIOSIM_CHIP_NAME[sim0]} - run_tool gpionotify --idle-timeout bad -c "$sim0" 0 1 + run_prog gpionotify --idle-timeout bad -c "$sim0" 0 1 output_regex_match ".*invalid period: bad" status_is 1 @@ -3037,53 +2760,8 @@ test_gpionotify_with_custom_format_unknown_specifier() { output_is "%x" } -die() { - echo "$@" 1>&2 - exit 1 -} - -# Must be done after we sources shunit2 as we need SHUNIT_VERSION to be set. -oneTimeSetUp() { - test "$SHUNIT_VERSION" = "$MIN_SHUNIT_VERSION" && return 0 - local FIRST - FIRST=$(printf "%s\n%s\n" "$SHUNIT_VERSION" "$MIN_SHUNIT_VERSION" | sort -Vr | head -1) - test "$FIRST" = "$MIN_SHUNIT_VERSION" && \ - die "minimum shunit version required is $MIN_SHUNIT_VERSION (current version is $SHUNIT_VERSION" -} - -check_kernel() { - local REQUIRED=$1 - local CURRENT - CURRENT=$(uname -r) - - SORTED=$(printf "%s\n%s" "$REQUIRED" "$CURRENT" | sort -V | head -n 1) - - if [ "$SORTED" != "$REQUIRED" ] - then - die "linux kernel version must be at least: v$REQUIRED - got: v$CURRENT" - fi -} - -check_prog() { - local PROG=$1 - - if ! which "$PROG" > /dev/null - then - die "$PROG not found - needed to run the tests" - fi -} - -# Check all required non-coreutils tools -check_prog shunit2 -check_prog modprobe -check_prog timeout - -# Check if we're running a kernel at the required version or later -check_kernel $MIN_KERNEL_VERSION - -modprobe gpio-sim || die "unable to load the gpio-sim module" -mountpoint /sys/kernel/config/ > /dev/null 2> /dev/null || \ - die "configfs not mounted at /sys/kernel/config/" +# shellcheck source=tests/scripts/gpiod-bash-test-helper.inc +source gpiod-bash-test-helper.inc # shellcheck source=/dev/null -. shunit2 +source shunit2 From patchwork Fri Jun 28 18:58:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808383 Received: from mail-wr1-f49.google.com (mail-wr1-f49.google.com [209.85.221.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8368255E58 for ; Fri, 28 Jun 2024 18:59:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601143; cv=none; b=bLaX6qyoiOtQMCXwOsK9aCltYoQpH+mTxswHMLoxqtRFKjEM70x0j+yCSyLddcY1syKfepqbsGaEGXIzD793VWjov23mpQDsQxQJmKLzHJeQZi71kwGQSRJSJThk0zCRkD4dBEr+J4iLuZvcxNeJKE37L+0lHRfBisohD61yBnw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601143; c=relaxed/simple; bh=nx35SMGyCmGDoHSsGlRMPayVQHogftwsCObguTDAj6I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WQJx+od9dLoEvuLMkufv1CGsvGmwkstidVYsbJ3jcTxCAxS9PSlQdy+ndq11MPyeA8XReFoqX12obQajsVMVpZeaELtmCfAQle6shnNu0GYMjG5TtkUJfnqfBRcG6FwpvA8m5dis4RZERtncqp163zBXn/x8TbEVpJWK+eUtbrc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=UYN5ysoU; arc=none smtp.client-ip=209.85.221.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="UYN5ysoU" Received: by mail-wr1-f49.google.com with SMTP id ffacd0b85a97d-363bd55bcc2so522295f8f.2 for ; Fri, 28 Jun 2024 11:59:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601140; x=1720205940; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=bhudZ7lKqVZB2QRIZFDSc9FQA2MNVAfGe2CqNxgvX4M=; b=UYN5ysoUACcavxjN0zBzvrY94ooa7j2XCeUBZPHK+Z8PXkVBdf2TOvfS6PA36kVKic WHS6bC3dZzNu+HX0Y5iSoTlcd6v6Irg5qtpgHbLq/atkCECY8jQ/8Wq0KgdFigql1c7I ouXqRooanEFY83n63WOLCkP0S73vOf4lkgc7lywJ/mj9z2TdDhgr2MXCrpg0BCB6nFI5 QepcklQPGLlWngv6QYShQJOsf5qoRFnYRagqzFhLBs3kPWKRcjxlbayeUzQqtEZiQsK4 Pf9X3gwiSv0XAKR1sdV69o0gyygx5tDREJ7WedFxz0R+hqN3j5pFGg6o6WxUrAdtK3qS ftDA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601140; x=1720205940; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=bhudZ7lKqVZB2QRIZFDSc9FQA2MNVAfGe2CqNxgvX4M=; b=LrxU5ptBI1iydTqQlNTqiEiQc4G7N5KOQBQorywIylNJbHsaAxjtYWLAsjWNRTcH3Y mQhvkOZMYO7/8+/lAufChT3Hn7Pc5DzNxjjWrmwOGPlEut1y+P5w60ABwFDpL5JALQ1I 9UlpjrOHc+j2NK/N1m+/aRh9we64Il2wAYlYp6Ljl2Txg+WPo4NhmXbgKCIcYnECnX9A VBEHNRTL+VAsGQ680QUCaVfh5K9qu0Izl7y2qOkX/5YkXVAg5b6drQxl+yN6TFSkmKJG T6B2HUF1qkvM59sVscHYXfZbfc7swKC0oN2umHWgWWML9g9EqN40TcQ48HorFtsRKPnk nwkA== X-Forwarded-Encrypted: i=1; AJvYcCW9yFQwCuMfrIQM73j3LPqvxoCoJfc81sqUW0+apNMj2RGulIGogVK7GyBR0AXpdi/d8xCy0aTZ9jVIk6ON6rOlyQKWm0hZu7IGdQ== X-Gm-Message-State: AOJu0YwxAvjeZJVCDXF+QIAqW9CRqwVbx0tpUJ696ZSHWA2Paq3Xx4RN i0+sP3/73ex81Lto3b/OB1Vq8y4t9eLtu0fkNXO6YeA9WBfpJ5BbmNSr2nPzKQYTZI5j2IkOJxs 3 X-Google-Smtp-Source: AGHT+IEeT6G/FW+K4IGt5QnrNEdpGnI0BFSVYIJSDkSDPyPtkr5uxmQv3Jl8+QXYcr7bW8m/Ixfh4A== X-Received: by 2002:adf:f78e:0:b0:367:4354:52c2 with SMTP id ffacd0b85a97d-3674354534bmr4082500f8f.22.1719601139946; Fri, 28 Jun 2024 11:58:59 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.58.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:58:59 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:22 +0200 Subject: [PATCH RESEND libgpiod v2 03/18] bindings: glib: add build files Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-3-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=10872; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=7iHAWmvZxf8paOvWUKi12iHzBsy0F9vg70QLyYNBX0Q=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfqwLdv+sJ9sXVkJotlBSYC91cHIKLdUl3Tw FnQ+kpTsgeJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H6gAKCRARpy6gFHHX ct/ZEACbA6nr64OPd+z6tbI6/rDolcnrtSYAs5yQQ62AbGeBn/0l7rruFAaD6KeFMbB3rEhE5v6 XGsfMzLTHFdsFay3tq17s5rIKYCQ1sYNaTlHo58tX/BcWw8WNE1wRODj76Cl6OVTU1rFD8D85EZ ubTyrVfwG5ZHxF8vtXUTb9RlBAQDW+ejvD+BkHVDsid7MWB48TtzUdm11vEx5UclkZ2ztMZWuaY S/Zp6XVkjTP8IWudXoX7CenVgvKCjOUUd1V5eciChFVM6zf8EfUsKR7DB8SJ5oEFUTdSOVB95pP tdGfEHfsEWRIsBP1yl4IMy5dxoAFWE04VwABncxaTjKvVzAZk2SCMAfCKO2V8LlP7B7j9+0UZJH 5bKwq8Y4flxcZ+m1c0pnOI23i2EH8leZuvFxe12rYjZXpmcmVbpanDoo3fUjmM86I/pu3ljL94N NKXDykYb8hbkd5ynmYu2OsVmHiGQNEYkgWMIS2gIRo0OaPtApuYnUscKwED723gqZ9aPN8Pg81a cr97PsZj2tdD1Kcwya0tKiSYN2K0G2R5BGWGRb4SW6U4Vjub9vjkd/GaKOYCaEe8nDXXzQxuj49 QMyOq22m0eJvSjGkf4qLf7vEsJUwhFFaHswmsVQjSBqOLbVZBxXywIeRiK5Y6xihcTHDcQIz3cs LkyOg3q8p5RHVOw== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add the directory structure and build files as well as changes to .gitignore and Doxygen.in for GLib bindings. Signed-off-by: Bartosz Golaszewski --- .gitignore | 2 + Doxyfile.in | 4 +- bindings/Makefile.am | 7 ++++ bindings/glib/Makefile.am | 78 ++++++++++++++++++++++++++++++++++++ bindings/glib/examples/.gitignore | 14 +++++++ bindings/glib/examples/Makefile.am | 22 ++++++++++ bindings/glib/gpiod-glib.pc.in | 14 +++++++ bindings/glib/gpiod-glib/Makefile.am | 18 +++++++++ bindings/glib/tests/.gitignore | 4 ++ bindings/glib/tests/Makefile.am | 29 ++++++++++++++ configure.ac | 24 +++++++++++ 11 files changed, 215 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cf66e97..c3a29d8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ *.o *.lo *.la +generated-*.c +generated-*.h doc *.pc *.tar.gz diff --git a/Doxyfile.in b/Doxyfile.in index 9c85e21..548a0da 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -46,7 +46,9 @@ WARN_FORMAT = WARN_LOGFILE = INPUT = @top_srcdir@/include/gpiod.h \ @top_srcdir@/bindings/cxx/gpiod.hpp \ - @top_srcdir@/bindings/cxx/gpiodcxx/ + @top_srcdir@/bindings/cxx/gpiodcxx/ \ + @top_srcdir@/bindings/glib/gpiod-glib.h \ + @top_srcdir@/bindings/glib/gpiod-glib/ SOURCE_BROWSER = YES INLINE_SOURCES = NO REFERENCED_BY_RELATION = YES diff --git a/bindings/Makefile.am b/bindings/Makefile.am index 004ae23..a177187 100644 --- a/bindings/Makefile.am +++ b/bindings/Makefile.am @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later # SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski SUBDIRS = . @@ -20,3 +21,9 @@ if WITH_BINDINGS_RUST SUBDIRS += rust endif + +if WITH_BINDINGS_GLIB + +SUBDIRS += glib + +endif diff --git a/bindings/glib/Makefile.am b/bindings/glib/Makefile.am new file mode 100644 index 0000000..750a913 --- /dev/null +++ b/bindings/glib/Makefile.am @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +SUBDIRS = . gpiod-glib + +if WITH_TESTS + +SUBDIRS += tests + +endif + +if WITH_EXAMPLES + +SUBDIRS += examples + +endif + +lib_LTLIBRARIES = libgpiod-glib.la + +libgpiod_glib_la_SOURCES = \ + chip.c \ + chip-info.c \ + edge-event.c \ + error.c \ + info-event.c \ + internal.c \ + internal.h \ + line-config.c \ + line-info.c \ + line-request.c \ + line-settings.c \ + misc.c \ + request-config.c + +EXTRA_DIST = \ + generated-enums.c.template \ + generated-enums.h.template + +project_headers = \ + $(srcdir)/gpiod-glib/line.h \ + $(srcdir)/gpiod-glib/edge-event.h \ + $(srcdir)/gpiod-glib/info-event.h + +generated-enums.c: $(project_headers) generated-enums.c.template + $(AM_V_GEN)$(GLIB_MKENUMS) \ + --symbol-prefix=G \ + --template=$(srcdir)/generated-enums.c.template \ + --output=$(builddir)/$@ \ + $(project_headers) + +gpiod-glib/generated-enums.h: $(project_headers) generated-enums.h.template + $(AM_V_GEN)$(GLIB_MKENUMS) \ + --symbol-prefix=G \ + --template=$(srcdir)/generated-enums.h.template \ + --output=$(srcdir)/$@ \ + $(project_headers) + +nodist_libgpiod_glib_la_SOURCES = \ + generated-enums.c \ + gpiod-glib/generated-enums.h + +BUILT_SOURCES = $(nodist_libgpiod_glib_la_SOURCES) +CLEANFILES = $(nodist_libgpiod_glib_la_SOURCES) + +libgpiod_glib_la_CFLAGS = -Wall -Wextra -g +libgpiod_glib_la_CFLAGS += -I$(top_srcdir)/include/ -include $(top_builddir)/config.h +libgpiod_glib_la_CFLAGS += $(GLIB_CFLAGS) $(GIO_CFLAGS) $(GIO_UNIX_CFLAGS) +libgpiod_glib_la_CFLAGS += -DG_LOG_DOMAIN=\"gpiod-glib\" +libgpiod_glib_la_CFLAGS += $(PROFILING_CFLAGS) +libgpiod_glib_la_LDFLAGS = -version-info $(subst .,:,$(ABI_GLIB_VERSION)) +libgpiod_glib_la_LDFLAGS += -lgpiod -L$(top_builddir)/lib +libgpiod_glib_la_LDFLAGS += $(GLIB_LIBS) $(GIO_LIBS) $(GIO_UNIX_LIBS) +libgpiod_glib_la_LDFLAGS += $(PROFILING_LDFLAGS) + +include_HEADERS = gpiod-glib.h + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = gpiod-glib.pc diff --git a/bindings/glib/examples/.gitignore b/bindings/glib/examples/.gitignore new file mode 100644 index 0000000..c2415ae --- /dev/null +++ b/bindings/glib/examples/.gitignore @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +find_line_by_name_glib +get_chip_info_glib +get_line_info_glib +get_line_value_glib +get_multiple_line_values_glib +reconfigure_input_to_output_glib +toggle_line_value_glib +toggle_multiple_line_values_glib +watch_line_info_glib +watch_line_value_glib +watch_multiple_edge_rising_glib diff --git a/bindings/glib/examples/Makefile.am b/bindings/glib/examples/Makefile.am new file mode 100644 index 0000000..fb4e5b1 --- /dev/null +++ b/bindings/glib/examples/Makefile.am @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +bin_PROGRAMS = \ + find_line_by_name_glib \ + get_chip_info_glib \ + get_line_info_glib \ + get_line_value_glib \ + get_multiple_line_values_glib \ + reconfigure_input_to_output_glib \ + toggle_line_value_glib \ + toggle_multiple_line_values_glib \ + watch_line_info_glib \ + watch_line_value_glib \ + watch_multiple_edge_rising_glib + +AM_CFLAGS = -I$(top_srcdir)/bindings/glib/ +AM_CFLAGS += -include $(top_builddir)/config.h +AM_CFLAGS += -Wall -Wextra -g -std=gnu89 $(GLIB_CFLAGS) $(GOBJECT_CFLAGS) +AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiotools-glib\" +LDADD = $(top_builddir)/bindings/glib/libgpiod-glib.la +LDADD += $(GLIB_LIBS) $(GOBJECT_LIBS) diff --git a/bindings/glib/gpiod-glib.pc.in b/bindings/glib/gpiod-glib.pc.in new file mode 100644 index 0000000..5da56da --- /dev/null +++ b/bindings/glib/gpiod-glib.pc.in @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: gpiod-glib +Description: GObject bindings for libgpiod +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lgpiod-glib +Cflags: -I${includedir} diff --git a/bindings/glib/gpiod-glib/Makefile.am b/bindings/glib/gpiod-glib/Makefile.am new file mode 100644 index 0000000..3d47772 --- /dev/null +++ b/bindings/glib/gpiod-glib/Makefile.am @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +otherincludedir = $(includedir)/gpiod-glib +otherinclude_HEADERS = \ + chip.h \ + chip-info.h \ + edge-event.h \ + error.h \ + generated-enums.h \ + info-event.h \ + line.h \ + line-config.h \ + line-info.h \ + line-request.h \ + line-settings.h \ + misc.h \ + request-config.h diff --git a/bindings/glib/tests/.gitignore b/bindings/glib/tests/.gitignore new file mode 100644 index 0000000..8eb499f --- /dev/null +++ b/bindings/glib/tests/.gitignore @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski + +gpiod-glib-test diff --git a/bindings/glib/tests/Makefile.am b/bindings/glib/tests/Makefile.am new file mode 100644 index 0000000..a90587a --- /dev/null +++ b/bindings/glib/tests/Makefile.am @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +noinst_PROGRAMS = gpiod-glib-test +gpiod_glib_test_SOURCES = \ + helpers.c \ + helpers.h \ + tests-chip.c \ + tests-chip-info.c \ + tests-edge-event.c \ + tests-info-event.c \ + tests-line-config.c \ + tests-line-info.c \ + tests-line-request.c \ + tests-line-settings.c \ + tests-misc.c \ + tests-request-config.c + +AM_CFLAGS = -I$(top_srcdir)/bindings/glib/ +AM_CFLAGS += -I$(top_srcdir)/tests/gpiosim-glib/ +AM_CFLAGS += -I$(top_srcdir)/tests/harness/ +AM_CFLAGS += -include $(top_builddir)/config.h +AM_CFLAGS += -Wall -Wextra -g -std=gnu89 $(GLIB_CFLAGS) $(GIO_CFLAGS) +AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiod-glib-test\" +LDADD = $(top_builddir)/bindings/glib/libgpiod-glib.la +LDADD += $(top_builddir)/tests/gpiosim/libgpiosim.la +LDADD += $(top_builddir)/tests/gpiosim-glib/libgpiosim-glib.la +LDADD += $(top_builddir)/tests/harness/libgpiod-test-harness.la +LDADD += $(GLIB_LIBS) $(GIO_LIBS) diff --git a/configure.ac b/configure.ac index 93d9d75..74ba004 100644 --- a/configure.ac +++ b/configure.ac @@ -31,6 +31,8 @@ AC_SUBST(ABI_CXX_VERSION, [3.0.1]) # ABI version for libgpiosim (we need this since it can be installed if we # enable tests). AC_SUBST(ABI_GPIOSIM_VERSION, [1.1.0]) +# ... and another one for GLib bindings: +AC_SUBST(ABI_GLIB_VERSION, [1.0.0]) AC_CONFIG_AUX_DIR([autostuff]) AC_CONFIG_MACRO_DIRS([m4]) @@ -248,6 +250,23 @@ then fi fi +AC_ARG_ENABLE([bindings-glib], + [AS_HELP_STRING([--enable-bindings-glib],[enable GLib 2.0 bindings [default=no]])], + [if test "x$enableval" = xyes; then with_bindings_glib=true; fi], + [with_bindings_glib=false]) +AM_CONDITIONAL([WITH_BINDINGS_GLIB], [test "x$with_bindings_glib" = xtrue]) + +if test "x$with_bindings_glib" = xtrue +then + PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.54]) + PKG_CHECK_MODULES([GOBJECT], [gobject-2.0 >= 2.54]) + PKG_CHECK_MODULES([GIO], [gio-2.0 >= 2.54]) + PKG_CHECK_MODULES([GIO_UNIX], [gio-unix-2.0 >= 2.54]) + PKG_PROG_PKG_CONFIG([0.28]) + PKG_CHECK_VAR([GLIB_MKENUMS], [glib-2.0], [glib_mkenums], [], + AC_MSG_ERROR([glib-mkenums not found - needed to build GLib bindings])) +fi + AC_CHECK_PROG([has_doxygen], [doxygen], [true], [false]) AM_CONDITIONAL([HAS_DOXYGEN], [test "x$has_doxygen" = xtrue]) if test "x$has_doxygen" = xfalse @@ -284,6 +303,11 @@ AC_CONFIG_FILES([Makefile bindings/cxx/gpiodcxx/Makefile bindings/cxx/examples/Makefile bindings/cxx/tests/Makefile + bindings/glib/gpiod-glib.pc + bindings/glib/Makefile + bindings/glib/examples/Makefile + bindings/glib/gpiod-glib/Makefile + bindings/glib/tests/Makefile bindings/python/Makefile bindings/python/gpiod/Makefile bindings/python/gpiod/ext/Makefile From patchwork Fri Jun 28 18:58:23 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808382 Received: from mail-wm1-f43.google.com (mail-wm1-f43.google.com [209.85.128.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 10B1A55E58 for ; Fri, 28 Jun 2024 18:59:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601147; cv=none; b=Cye4arnFqeto1/NY4bJybyPEFxFfdqagLp1h264LBGDL988pHu+mtHI9q53n3MQtOb06Hadh/Xd8HwuL+iTbuPyDcHKP5tkuqWg8lSLQzg+YmA0UaRJKXRkaKbHqJo3V7WeZhTObnEI3b69v83QmyE3rj/kRpr7Url2Q5S8PLbY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601147; c=relaxed/simple; bh=jHDNApF6Py0Jb/L4gKZb/5Q3dfZO/6Yy20UkG2CM944=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hWOa9qY5ndgGle7OFIRvMykbgmMD+AXXMQGJ+GmNngOPquxu3I4Sff/tVQxCvnZCSW8FEONqwj0ed1Fb2JWFzxx75/2sEdkUTlz0IPEoNOgPrxWKgw7Zdx8M1eQd5il6w59xoeIKInDfe12v9b6C6elvnf6MlYbeOwATjWgkaBo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=SoPZOSsv; arc=none smtp.client-ip=209.85.128.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="SoPZOSsv" Received: by mail-wm1-f43.google.com with SMTP id 5b1f17b1804b1-42562e4b5d1so6920535e9.1 for ; Fri, 28 Jun 2024 11:59:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601142; x=1720205942; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=YvTuvy1Ai6TZS13lF1q6X9ZQcEeWbyNLSCkPhm4FmQI=; b=SoPZOSsv+G6Wi5co0hdkCD4YXvlyRsUfzuU7Dti+Y+zJc8pHVm+g4zOF9e+EqAvYBR qXBly6479Ugvv/fWuqEoYHk0WSWeuXgkB4GEdMNFUoI6uE69TwGuKdnZSWiANDmxkO+f sCWNwj+Y1P07BbsQcr/QkLdFb0/0dw6+I7P1r9jcx3UeiYH13AgDnwekDJaGo4VYJcxW d4IoOUe2J904SvqjIEC9NoE2QV8q4s4ZCwn7bwH0lIAMDMU66LOXZE3tnlzjZvoB23ef kqmRE7eEONMsl0+P5aLxO5NwHrgBsaNbTs2rL4SoVmbdhv/EEORxyTCCq9AU5xu5thFN 5t8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601142; x=1720205942; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=YvTuvy1Ai6TZS13lF1q6X9ZQcEeWbyNLSCkPhm4FmQI=; b=rvF1QALL5Z5aq1Hq2mgyLUQHgUXrILYk3dKh9+HDaGVzgbj/nzlM2ar9fbCu6H88kY 9omZyoplhd5FC0aEFOkxY3Ia31p7Lr4t/at1DfuIYcbX+6RGmzWz3L7jojZLC1Djjt8Y sHVmWm0kZLucNLzRoRp11hL/0HHQHc1cJbtZhZAHSpkHk6FS9z98CINlTNoNB/kYm4cC 6dR6b21zx5i7sshZIN2KMfknqxllL3AOzsZzO+kzTAXYRR0+GTFFAwmvsY1TVsHwo4Me KkeQdQeq732BEdEVMZ4McpG2yeAV4nT4GTq7LcqDSvE3yMEbWAdL60SWYoM3e1xHGmv9 nxkg== X-Forwarded-Encrypted: i=1; AJvYcCUaW/mO1NbxPCFKhn+ZnpRJHlhTCpJ1DSPN0MjwaEyFcPx8+CJ4O7pPaN42q9FA4x3Aw5vnx5ZfkKd33YWQup/zVWfhvQ6uArUykg== X-Gm-Message-State: AOJu0YzLxt6cm66BC6MyJS2wCnnpMTcYT6uhfSonZc0HTRJarpnx3SVI JLn9Uppyiq3UMWFnaP8MRvMH+HF9s2nl3TO2QRDS37kgDSlSMMTjYlBaBIBosXE= X-Google-Smtp-Source: AGHT+IG4C+LvpRHaDKX3xH2xK+TeA5HdHQEyj4m6r4kn0xqUMhbeO65JSMJ8V+Bdb/IaalLpn1lWJg== X-Received: by 2002:a05:6000:154d:b0:366:e508:c7d7 with SMTP id ffacd0b85a97d-366e508c9d5mr15952943f8f.7.1719601141987; Fri, 28 Jun 2024 11:59:01 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:00 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:23 +0200 Subject: [PATCH RESEND libgpiod v2 04/18] bindings: glib: add public headers Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-4-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=49036; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=yASdN/rfYx1HxoyUfgQTVlxDhy8bZbAEnv+bDBPwhdY=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfq4jw3/hhZ1fgtFBetfOsGeeL/LPq/sE7s0 clIXIzM7DiJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H6gAKCRARpy6gFHHX csOqD/9JLxN34QcliguvOdHtl4RgsOds6zh3Xcc21LsAYcyTYinIrVzL154CYfK0D41+61+69sO 1ZT0NLNjfnugiZpochKTDVTlTgI0KKbF6QZ/REqoAI9chR9o2HApjjnwLx9aCkh0mWUJV8k4LcK w9JRzcKUaKsX7slrQNoe1sBDWDimQhxZIuSiHzmpRGVLjdykxzxpR09IdF2o8mM3frSHKd3LgSk CwZbJJwpK8Sd6XI4skIN/RcNbF+LB2g0pMmYm+cYKakulFZPa+eThJVouBQow8LrS1soUiQ/ksP gaY3JZAwbHHz55uqQpiobAjQYoo8Z0psZtrU60hPljbHhYb5zFgK4Q3x7yN5ffYcS6u+eSXSLsb FtBm1PN68Ej+FM3uUkrShe5qruopGTneb9/Phh3Vo3zc43lbWdt/p7ZIkRkRLll9zFerTOqIdij 56Do3yr9N+QXRPuha+ZV1fzgJHD4Qbur2s64ZgISu7+b1oqUAN5io9p4qlZbpA0Um9knC8BV+FL 7PtHtUV7vW6Zt/Ze3Kmoo2qBnNtSVijlIIiwyLvUs6Si5/yiJgox6S4pDA+GLIirmeDghibEBuw hF58ZXzIsoJtOfL2Abj4LIp/ezC98Ic7yeIY/t9c9pVhdhz7KVfF/IeRtYrdE6UiI8vSKpUGXND VUIypGPu7+bTHxg== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add the public headers for GLib bindings. They contain the entire API together with Doxygen comments. Signed-off-by: Bartosz Golaszewski --- bindings/glib/generated-enums.h.template | 30 +++++ bindings/glib/gpiod-glib.h | 33 +++++ bindings/glib/gpiod-glib/chip-info.h | 84 +++++++++++++ bindings/glib/gpiod-glib/chip.h | 164 ++++++++++++++++++++++++ bindings/glib/gpiod-glib/edge-event.h | 114 +++++++++++++++++ bindings/glib/gpiod-glib/error.h | 67 ++++++++++ bindings/glib/gpiod-glib/info-event.h | 97 ++++++++++++++ bindings/glib/gpiod-glib/line-config.h | 116 +++++++++++++++++ bindings/glib/gpiod-glib/line-info.h | 171 +++++++++++++++++++++++++ bindings/glib/gpiod-glib/line-request.h | 182 +++++++++++++++++++++++++++ bindings/glib/gpiod-glib/line-settings.h | 202 ++++++++++++++++++++++++++++++ bindings/glib/gpiod-glib/line.h | 114 +++++++++++++++++ bindings/glib/gpiod-glib/misc.h | 51 ++++++++ bindings/glib/gpiod-glib/request-config.h | 107 ++++++++++++++++ 14 files changed, 1532 insertions(+) diff --git a/bindings/glib/generated-enums.h.template b/bindings/glib/generated-enums.h.template new file mode 100644 index 0000000..53ea247 --- /dev/null +++ b/bindings/glib/generated-enums.h.template @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/*** BEGIN file-header ***/ + +#ifndef __GPIOD_GLIB_GENERATED_ENUMS_H__ +#define __GPIOD_GLIB_GENERATED_ENUMS_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +G_BEGIN_DECLS +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType @enum_name@_get_type(void) G_GNUC_CONST; +#define @ENUMPREFIX@_@ENUMSHORT@_TYPE (@enum_name@_get_type()) +/*** END value-header ***/ + +/*** BEGIN file-tail ***/ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_GENERATED_ENUMS_H__ */ +/*** END file-tail ***/ diff --git a/bindings/glib/gpiod-glib.h b/bindings/glib/gpiod-glib.h new file mode 100644 index 0000000..c45064c --- /dev/null +++ b/bindings/glib/gpiod-glib.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +/** + * @file gpiod-glib.h + */ + +#ifndef __GPIOD_GLIB_H__ +#define __GPIOD_GLIB_H__ + +/** + * @defgroup gpiod_glib GLib bindings + * + * GLib bindings for libgpiod representing all data structures using the + * GObject type system. + */ + +#define __GPIOD_GLIB_INSIDE__ +#include "gpiod-glib/chip.h" +#include "gpiod-glib/chip-info.h" +#include "gpiod-glib/edge-event.h" +#include "gpiod-glib/error.h" +#include "gpiod-glib/generated-enums.h" +#include "gpiod-glib/info-event.h" +#include "gpiod-glib/line-config.h" +#include "gpiod-glib/line-info.h" +#include "gpiod-glib/line-request.h" +#include "gpiod-glib/line-settings.h" +#include "gpiod-glib/misc.h" +#include "gpiod-glib/request-config.h" +#undef __GPIOD_GLIB_INSIDE__ + +#endif /* __GPIOD_GLIB_H__ */ diff --git a/bindings/glib/gpiod-glib/chip-info.h b/bindings/glib/gpiod-glib/chip-info.h new file mode 100644 index 0000000..bfa5a02 --- /dev/null +++ b/bindings/glib/gpiod-glib/chip-info.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +/** + * @file chip-info.h + */ + +#ifndef __GPIOD_GLIB_CHIP_INFO_H__ +#define __GPIOD_GLIB_CHIP_INFO_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +/** + * @cond + */ + +G_DECLARE_FINAL_TYPE(GPIODChipInfo, g_gpiod_chip_info, + G_GPIOD, CHIP_INFO, GObject); + +/** + * @endcond + * + * @ingroup gpiod_glib + * @{ + * + * @defgroup gobject_chip_info GPIO chip info GObject + * @{ + * + * Represents an immutable snapshot of GPIO chip information. + */ + +/** + * @brief Get the GObject type for the GPIO chip-info. + */ +#define G_GPIOD_CHIP_INFO_TYPE (g_gpiod_chip_info_get_type()) + +/** + * @brief Cast a GObject to a GPIO chip-info concrete GObject. + */ +#define G_GPIOD_CHIP_INFO_OBJ(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOD_CHIP_INFO_TYPE, \ + GPIODChipInfo)) + +/** + * @brief Get the name of the chip as represented in the kernel. + * @param self GPIO chip info object to manipulate. + * @return Valid pointer to a human-readable string containing the chip name. + * The string lifetime is tied to the chip info object so the pointer + * must not be freed by the caller. + */ +const gchar *g_gpiod_chip_info_get_name(GPIODChipInfo *self); + +/** + * @brief Get the label of the chip as represented in the kernel. + * @param self GPIO chip info object to manipulate. + * @return Valid pointer to a human-readable string containing the chip label. + * The string lifetime is tied to the chip info object so the pointer + * must not be freed by the caller. + */ +const gchar *g_gpiod_chip_info_get_label(GPIODChipInfo *self); + +/** + * @brief Get the number of lines exposed by the chip. + * @param self GPIO chip info object to manipulate. + * @return Number of GPIO lines. + */ +guint g_gpiod_chip_info_get_num_lines(GPIODChipInfo *self); + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_CHIP_INFO_H__ */ diff --git a/bindings/glib/gpiod-glib/chip.h b/bindings/glib/gpiod-glib/chip.h new file mode 100644 index 0000000..3b6e907 --- /dev/null +++ b/bindings/glib/gpiod-glib/chip.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +/** + * @file chip.h + */ + +#ifndef __GPIOD_GLIB_CHIP_H__ +#define __GPIOD_GLIB_CHIP_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include +#include + +#include "chip-info.h" +#include "line-config.h" +#include "line-info.h" +#include "line-request.h" +#include "request-config.h" + +G_BEGIN_DECLS + +/** + * @cond + */ + +G_DECLARE_FINAL_TYPE(GPIODChip, g_gpiod_chip, G_GPIOD, CHIP, GObject); + +/** + * @endcond + * + * @ingroup gpiod_glib + * @{ + * + * @defgroup gobject_chip GPIO chip GObject + * @{ + * + * This type represents a GPIO chip. In addition to the methods defined here, + * it exposes the `info-event` signal with the following signature: + * + * void (*)(GPIODChip *, GPIODInfoEvent *, gpointer) + */ + +/** + * @brief Get the GObject type for the GPIO chip. + */ +#define G_GPIOD_CHIP_TYPE (g_gpiod_chip_get_type()) + +/** + * @brief Cast a GObject to a GPIO chip concrete GObject. + */ +#define G_GPIOD_CHIP_OBJ(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOD_CHIP_TYPE, GPIODChip)) + +/** + * @brief Instantiates a new chip object by opening the device file indicated + * by \p path. + * @param path Path to the device file to open. + * @param err Return location for error or NULL. + * @return New GPIO chip object. + */ +GPIODChip *g_gpiod_chip_new(const gchar *path, GError **err); + +/** + * @brief Close the GPIO chip device file and free associated resources. + * @param self Chip object to manipulate. + * @note The chip object can live after calling this method but any of + * the chip's methods will result in an error being set. + */ +void g_gpiod_chip_close(GPIODChip *self); + +/** + * @brief Check if this object is valid. + * @param self Chip object to manipulate. + * @return TRUE if this object's methods can be used, FALSE otherwise. + * False usually means the chip was closed. If the user calls + * any of the methods of this class on an object for which this + * operator returned false, a logic_error will be thrown. + */ +gboolean g_gpiod_chip_is_closed(GPIODChip *self); + +/** + * @brief Get the filesystem path that was used to open this GPIO chip. + * @param self Chip object to manipulate. + * @return Path to the underlying character device file. + */ +const gchar *g_gpiod_chip_get_path(GPIODChip *self); + +/** + * @brief Get information about the chip. + * @param self Chip object to manipulate. + * @param err Return location for error or NULL. + * @return New GPIO chip info object. + */ +GPIODChipInfo *g_gpiod_chip_get_info(GPIODChip *self, GError **err); + +/** + * @brief Retrieve the current snapshot of line information for a single line. + * @param self Chip object to manipulate. + * @param offset Offset of the line to get the info for. + * @param err Return location for error or NULL. + * @return New GPIO line info object. + */ +GPIODLineInfo * +g_gpiod_chip_get_line_info(GPIODChip *self, guint offset, GError **err); + +/** + * @brief Retrieve the current snapshot of line information for a single line + * and start watching this line for future changes. + * @param self Chip object to manipulate. + * @param offset Offset of the line to get the info for and to watch. + * @param err Return location for error or NULL. + * @return New GPIO line info object. + */ +GPIODLineInfo * +g_gpiod_chip_watch_line_info(GPIODChip *self, guint offset, GError **err); + +/** + * @brief Stop watching the line at given offset for info events. + * @param self Chip object to manipulate. + * @param offset Offset of the line to get the info for. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on failure. + */ +gboolean +g_gpiod_chip_unwatch_line_info(GPIODChip *self, guint offset, GError **err); + +/** + * @brief Map a GPIO line's name to its offset within the chip. + * @param self Chip object to manipulate. + * @param name Name of the GPIO line to map. + * @param offset Return location for the mapped offset. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on failure. + */ +gboolean +g_gpiod_chip_get_line_offset_from_name(GPIODChip *self, const gchar *name, + guint *offset, GError **err); + +/** + * @brief Request a set of lines for exclusive usage. + * @param self Chip object to manipulate. + * @param req_cfg Request config object. Can be NULL for default settings. + * @param line_cfg Line config object. + * @param err Return location for error or NULL. + * @return New GPIO line request object or NULL on failure. + */ +GPIODLineRequest *g_gpiod_chip_request_lines(GPIODChip *self, + GPIODRequestConfig *req_cfg, + GPIODLineConfig *line_cfg, + GError **err); + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_CHIP_H__ */ diff --git a/bindings/glib/gpiod-glib/edge-event.h b/bindings/glib/gpiod-glib/edge-event.h new file mode 100644 index 0000000..711a8fb --- /dev/null +++ b/bindings/glib/gpiod-glib/edge-event.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2023 Bartosz Golaszewski */ + +/** + * @file edge-event.h + */ + +#ifndef __GPIOD_GLIB_EDGE_EVENT_H__ +#define __GPIOD_GLIB_EDGE_EVENT_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include +#include + +#include "line-info.h" + +G_BEGIN_DECLS + +/** + * @cond + */ + +G_DECLARE_FINAL_TYPE(GPIODEdgeEvent, g_gpiod_edge_event, + G_GPIOD, EDGE_EVENT, GObject); + +/** + * @endcond + * + * @ingroup gpiod_glib + * @{ + * + * @defgroup gobject_edge_event GPIO edge-event GObject + * @{ + * + * An edge event object contains information about a single line edge event. + * It contains the event type, timestamp and the offset of the line on which + * the event occurred as well as two sequence numbers (global for all lines + * in the associated request and local for this line only). + */ + +/** + * @brief Get the GObject type for the GPIO edge-event. + */ +#define G_GPIOD_EDGE_EVENT_TYPE (g_gpiod_edge_event_get_type()) + +/** + * @brief Cast a GObject to a GPIO edge-event concrete GObject. + */ +#define G_GPIOD_EDGE_EVENT_OBJ(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOD_EDGE_EVENT_TYPE, \ + GPIODEdgeEvent)) + +/** + * @brief Event types. + */ +typedef enum { + G_GPIOD_EDGE_EVENT_RISING_EDGE = 1, + /**< Rising edge event. */ + G_GPIOD_EDGE_EVENT_FALLING_EDGE, + /**< Falling edge event. */ +} GPIODEdgeEventType; + +/** + * @brief Get the event type. + * @param self GPIO edge event to manipulate. + * @return The event type (::G_GPIOD_EDGE_EVENT_RISING_EDGE or + * ::G_GPIOD_EDGE_EVENT_FALLING_EDGE). + */ +GPIODEdgeEventType g_gpiod_edge_event_get_event_type(GPIODEdgeEvent *self); + +/** + * @brief Get the timestamp of the event. + * @param self GPIO edge event to manipulate. + * @return Timestamp in nanoseconds. + * @note The source clock for the timestamp depends on the event_clock + * setting for the line. + */ +guint64 g_gpiod_edge_event_get_timestamp_ns(GPIODEdgeEvent *self); + +/** + * @brief Get the offset of the line which triggered the event. + * @param self GPIO edge event to manipulate. + * @return Line offset. + */ +guint g_gpiod_edge_event_get_line_offset(GPIODEdgeEvent *self); + +/** + * @brief Get the global sequence number of the event. + * @param self GPIO edge event to manipulate. + * @return Sequence number of the event in the series of events for all lines + * in the associated line request. + */ +gulong g_gpiod_edge_event_get_global_seqno(GPIODEdgeEvent *self); + +/** + * @brief Get the event sequence number specific to the line. + * @param self GPIO edge event to manipulate. + * @return Sequence number of the event in the series of events only for this + * line within the lifetime of the associated line request. + */ +gulong g_gpiod_edge_event_get_line_seqno(GPIODEdgeEvent *self); + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_EDGE_EVENT_H__ */ diff --git a/bindings/glib/gpiod-glib/error.h b/bindings/glib/gpiod-glib/error.h new file mode 100644 index 0000000..3433c11 --- /dev/null +++ b/bindings/glib/gpiod-glib/error.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +/** + * @file error.h + */ + +#ifndef __GPIOD_GLIB_ERROR_H__ +#define __GPIOD_GLIB_ERROR_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * @ingroup gpiod_glib + * @{ + */ + +/** + * @brief Get the libgpiod GObject error domain. + */ +#define G_GPIOD_ERROR g_gpiod_error_quark() + +/** + * @} + * + * @cond + */ + +typedef enum { + G_GPIOD_ERR_FAILED = 1, + G_GPIOD_ERR_CHIP_CLOSED, + G_GPIOD_ERR_REQUEST_RELEASED, + G_GPIOD_ERR_PERM, + G_GPIOD_ERR_NOENT, + G_GPIOD_ERR_INTR, + G_GPIOD_ERR_IO, + G_GPIOD_ERR_NXIO, + G_GPIOD_ERR_E2BIG, + G_GPIOD_ERR_BADFD, + G_GPIOD_ERR_CHILD, + G_GPIOD_ERR_AGAIN, + G_GPIOD_ERR_NOMEM, + G_GPIOD_ERR_ACCES, + G_GPIOD_ERR_FAULT, + G_GPIOD_ERR_BUSY, + G_GPIOD_ERR_EXIST, + G_GPIOD_ERR_NODEV, + G_GPIOD_ERR_INVAL, + G_GPIOD_ERR_NOTTY, + G_GPIOD_ERR_PIPE, +} GPIODError; + +GQuark g_gpiod_error_quark(void); + +/** + * @endcond + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_ERROR_H__ */ diff --git a/bindings/glib/gpiod-glib/info-event.h b/bindings/glib/gpiod-glib/info-event.h new file mode 100644 index 0000000..d9e61e3 --- /dev/null +++ b/bindings/glib/gpiod-glib/info-event.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2023 Bartosz Golaszewski */ + +/** + * @file info-event.h + */ + +#ifndef __GPIOD_GLIB_INFO_EVENT_H__ +#define __GPIOD_GLIB_INFO_EVENT_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include +#include + +#include "line-info.h" + +G_BEGIN_DECLS + +/** + * @cond + */ + +G_DECLARE_FINAL_TYPE(GPIODInfoEvent, g_gpiod_info_event, + G_GPIOD, INFO_EVENT, GObject); + +/** + * @endcond + * + * @ingroup gpiod_glib + * @{ + * + * @defgroup gobject_info_event GPIO info-event GObject + * @{ + * + * An info-event contains information about the event itself (timestamp, type) + * as well as a snapshot of line's status in the form of a line-info object. + */ + +/** + * @brief Get the GObject type for the GPIO info-event. + */ +#define G_GPIOD_INFO_EVENT_TYPE (g_gpiod_info_event_get_type()) + +/** + * @brief Cast a GObject to a GPIO info-event concrete GObject. + */ +#define G_GPIOD_INFO_EVENT_OBJ(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOD_INFO_EVENT_TYPE, \ + GPIODInfoEvent)) + +/** + * @brief Line status change event types. + */ +typedef enum { + G_GPIOD_INFO_EVENT_LINE_REQUESTED = 1, + /**< Line has been requested. */ + G_GPIOD_INFO_EVENT_LINE_RELEASED, + /**< Previously requested line has been released. */ + G_GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED, + /**< Line configuration has changed. */ +} GPIODInfoEventType; + +/** + * @brief Get the event type of the status change event. + * @param self GPIO edge event to manipulate. + * @return One of ::G_GPIOD_INFO_EVENT_LINE_REQUESTED, + * ::G_GPIOD_INFO_EVENT_LINE_RELEASED or + * ::G_GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED. + */ +GPIODInfoEventType g_gpiod_info_event_get_event_type(GPIODInfoEvent *self); + +/** + * @brief Get the timestamp of the event. + * @param self GPIO edge event to manipulate. + * @return Timestamp in nanoseconds, read from the monotonic clock. + */ +guint64 g_gpiod_info_event_get_timestamp_ns(GPIODInfoEvent *self); + +/** + * @brief Get the snapshot of line-info associated with the event. + * @param self GPIO edge event to manipulate. + * @return Returns a new reference to the associated line-info object. + */ +GPIODLineInfo *g_gpiod_info_event_get_line_info(GPIODInfoEvent *self); + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_INFO_EVENT_H__ */ diff --git a/bindings/glib/gpiod-glib/line-config.h b/bindings/glib/gpiod-glib/line-config.h new file mode 100644 index 0000000..a477165 --- /dev/null +++ b/bindings/glib/gpiod-glib/line-config.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2023 Bartosz Golaszewski */ + +/** + * @file line-config.h + */ + +#ifndef __GPIOD_GLIB_LINE_CONFIG_H__ +#define __GPIOD_GLIB_LINE_CONFIG_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include +#include + +#include "line-settings.h" + +G_BEGIN_DECLS + +/** + * @cond + */ + +G_DECLARE_FINAL_TYPE(GPIODLineConfig, g_gpiod_line_config, + G_GPIOD, LINE_CONFIG, GObject); + +/** + * @endcond + * + * @ingroup gpiod_glib + * @{ + * + * @defgroup gobject_line_config GPIO line config GObject + * @{ + * + * The line-config object contains the configuration for lines that can be + * used in two cases: + * - when making a line request + * - when reconfiguring a set of already requested lines. + */ + +/** + * @brief Get the GObject type for the GPIO line-config. + */ +#define G_GPIOD_LINE_CONFIG_TYPE (g_gpiod_line_config_get_type()) + +/** + * @brief Cast a GObject to a GPIO line-config concrete GObject. + */ +#define G_GPIOD_LINE_CONFIG_OBJ(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOD_LINE_CONFIG_TYPE, \ + GPIODLineConfig)) + +/** + * @brief Create a new line config object. + * @return Empty line config object. + */ +GPIODLineConfig *g_gpiod_line_config_new(void); + +/** + * @brief Reset the line config object. + * @param self GPIO line config to manipulate. + */ +void g_gpiod_line_config_reset(GPIODLineConfig *self); + +/** + * @brief Add line settings for a set of offsets. + * @param self GPIO line config to manipulate. + * @param offsets GArray of offsets for which to apply the settings. + * @param settings Line settings to apply. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on failure. + */ +gboolean g_gpiod_line_config_add_line_settings(GPIODLineConfig *self, + const GArray *offsets, + GPIODLineSettings *settings, + GError **err); + +/** + * @brief Get line settings for offset. + * @param self GPIO line config to manipulate. + * @param offset Offset for which to get line settings. + * @return New reference to a line settings object. + */ +GPIODLineSettings * +g_gpiod_line_config_get_line_settings(GPIODLineConfig *self, guint offset); + +/** + * @brief Set output values for a number of lines. + * @param self GPIO line config to manipulate. + * @param values GArray containing the output values. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on error. + */ +gboolean g_gpiod_line_config_set_output_values(GPIODLineConfig *self, + const GArray *values, + GError **err); + +/** + * @brief Get configured offsets. + * @param self GPIO line config to manipulate. + * @return GArray containing the offsets for which configuration has been set. + */ +GArray *g_gpiod_line_config_get_configured_offsets(GPIODLineConfig *self); + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_LINE_CONFIG_H__ */ diff --git a/bindings/glib/gpiod-glib/line-info.h b/bindings/glib/gpiod-glib/line-info.h new file mode 100644 index 0000000..d94750c --- /dev/null +++ b/bindings/glib/gpiod-glib/line-info.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2023 Bartosz Golaszewski */ + +/** + * @file line-info.h + */ + +#ifndef __GPIOD_GLIB_LINE_INFO_H__ +#define __GPIOD_GLIB_LINE_INFO_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include +#include + +#include "line.h" + +G_BEGIN_DECLS + +/** + * @cond + */ + +G_DECLARE_FINAL_TYPE(GPIODLineInfo, g_gpiod_line_info, + G_GPIOD, LINE_INFO, GObject); + +/** + * @endcond + * + * @ingroup gpiod_glib + * @{ + * + * @defgroup gobject_line_info GPIO line-info GObject + * @{ + * + * Line info object contains an immutable snapshot of a line's status. + * + * The line info contains all the publicly available information about a + * line, which does not include the line value. The line must be requested + * to access the line value. + */ + +/** + * @brief Get the GObject type for the GPIO line-info. + */ +#define G_GPIOD_LINE_INFO_TYPE (g_gpiod_line_info_get_type()) + +/** + * @brief Cast a GObject to a GPIO line-info concrete GObject. + */ +#define G_GPIOD_LINE_INFO_OBJ(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOD_LINE_INFO_TYPE, \ + GPIODLineInfo)) + +/** + * @brief Get the offset of the line. + * @param self GPIO line info object to manipulate. + * @return Offset of the line within the parent chip. + * + * The offset uniquely identifies the line on the chip. The combination of the + * chip and offset uniquely identifies the line within the system. + */ +guint g_gpiod_line_info_get_offset(GPIODLineInfo *self); + +/** + * @brief Get the name of the line. + * @param self GPIO line info object to manipulate. + * @return Name of the GPIO line as it is represented in the kernel. + * This function returns a valid pointer to a null-terminated string + * or NULL if the line is unnamed. The string lifetime is tied to the + * line info object so the pointer must not be freed. + */ +const gchar *g_gpiod_line_info_get_name(GPIODLineInfo *self); + +/** + * @brief Check if the line is in use. + * @param self GPIO line info object to manipulate. + * @return True if the line is in use, false otherwise. + * + * The exact reason a line is busy cannot be determined from user space. + * It may have been requested by another process or hogged by the kernel. + * It only matters that the line is used and can't be requested until + * released by the existing consumer. + */ +gboolean g_gpiod_line_info_is_used(GPIODLineInfo *self); + +/** + * @brief Get the name of the consumer of the line. + * @param self GPIO line info object to manipulate. + * @return Name of the GPIO consumer as it is represented in the kernel. + * This function returns a valid pointer to a null-terminated string + * or NULL if the consumer name is not set. The string lifetime is tied + * to the line info object so the pointer must not be freed. + */ +const gchar *g_gpiod_line_info_get_consumer(GPIODLineInfo *self); + +/** + * @brief Get the direction setting of the line. + * @param self GPIO line info object to manipulate. + * @return Returns ::G_GPIOD_LINE_DIRECTION_INPUT or + * ::G_GPIOD_LINE_DIRECTION_OUTPUT. + */ +GPIODLineDirection g_gpiod_line_info_get_direction(GPIODLineInfo *self); + +/** + * @brief Get the edge detection setting of the line. + * @param self GPIO line info object to manipulate. + * @return Returns ::G_GPIOD_LINE_EDGE_NONE, ::G_GPIOD_LINE_EDGE_RISING, + * ::G_GPIOD_LINE_EDGE_FALLING or ::G_GPIOD_LINE_EDGE_BOTH. + */ +GPIODLineEdge g_gpiod_line_info_get_edge_detection(GPIODLineInfo *self); + +/** + * @brief Get the bias setting of the line. + * @param self GPIO line info object to manipulate. + * @return Returns ::G_GPIOD_LINE_BIAS_PULL_UP, ::G_GPIOD_LINE_BIAS_PULL_DOWN, + * ::G_GPIOD_LINE_BIAS_DISABLED or ::G_GPIOD_LINE_BIAS_UNKNOWN. + */ +GPIODLineBias g_gpiod_line_info_get_bias(GPIODLineInfo *self); + +/** + * @brief Get the drive setting of the line. + * @param self GPIO line info object to manipulate. + * @return Returns ::G_GPIOD_LINE_DRIVE_PUSH_PULL, + * ::G_GPIOD_LINE_DRIVE_OPEN_DRAIN or ::G_GPIOD_LINE_DRIVE_OPEN_SOURCE. + */ +GPIODLineDrive g_gpiod_line_info_get_drive(GPIODLineInfo *self); + +/** + * @brief Check if the logical value of the line is inverted compared to the + * physical. + * @param self GPIO line info object to manipulate. + * @return TRUE if the line is "active-low", FALSE otherwise. + */ +gboolean g_gpiod_line_info_is_active_low(GPIODLineInfo *self); + +/** + * @brief Check if the line is debounced (either by hardware or by the kernel + * software debouncer). + * @param self GPIO line info object to manipulate. + * @return TRUE if the line is debounced, FALSE otherwise. + */ +gboolean g_gpiod_line_info_is_debounced(GPIODLineInfo *self); + +/** + * @brief Get the debounce period of the line, in microseconds. + * @param self GPIO line info object to manipulate. + * @return Debounce period in microseconds. 0 if the line is not debounced. + */ +GTimeSpan g_gpiod_line_info_get_debounce_period_us(GPIODLineInfo *self); + +/** + * @brief Get the event clock setting used for edge event timestamps for the + * line. + * @param self GPIO line info object to manipulate. + * @return Returns ::G_GPIOD_LINE_CLOCK_MONOTONIC, ::G_GPIOD_LINE_CLOCK_HTE or + * ::G_GPIOD_LINE_CLOCK_REALTIME. + */ +GPIODLineClock g_gpiod_line_info_get_event_clock(GPIODLineInfo *self); + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_LINE_INFO_H__ */ diff --git a/bindings/glib/gpiod-glib/line-request.h b/bindings/glib/gpiod-glib/line-request.h new file mode 100644 index 0000000..1c906f1 --- /dev/null +++ b/bindings/glib/gpiod-glib/line-request.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2023 Bartosz Golaszewski */ + +/** + * @file line-request.h + */ + +#ifndef __GPIOD_GLIB_LINE_REQUEST_H__ +#define __GPIOD_GLIB_LINE_REQUEST_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +/** + * @cond + */ + +G_DECLARE_FINAL_TYPE(GPIODLineRequest, g_gpiod_line_request, + G_GPIOD, LINE_REQUEST, GObject); + +/** + * @endcond + * + * @ingroup gpiod_glib + * @{ + * + * @defgroup gobject_line_request GPIO line-request GObject + * @{ + * + * Line request object allows interacting with a set of requested GPIO lines. + */ + +/** + * @brief Get the GObject type for the GPIO line-request. + */ +#define G_GPIOD_LINE_REQUEST_TYPE (g_gpiod_line_request_get_type()) + +/** + * @brief Cast a GObject to a GPIO line-request concrete GObject. + */ +#define G_GPIOD_LINE_REQUEST_OBJ(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOD_LINE_REQUEST_TYPE, \ + GPIODLineRequest)) + +/** + * @brief Release the requested lines and free all associated resources. + * @param self GPIO line request object to manipulate. + */ +void g_gpiod_line_request_release(GPIODLineRequest *self); + +/** + * @brief Check if this request was released. + * @param self GPIO line request object to manipulate. + * @return TRUE if this request was released and is no longer valid, FALSE + * otherwise. + */ +gboolean g_gpiod_line_request_is_released(GPIODLineRequest *self); + +/** + * @brief Get the name of the chip this request was made on. + * @param self GPIO line request object to manipulate. + * @return Name the GPIO chip device. + */ +const gchar *g_gpiod_line_request_get_chip_name(GPIODLineRequest *self); + +/** + * @brief Get the offsets of the lines in the request. + * @param self GPIO line request object to manipulate. + * @return Array containing the requested offsets. + */ +GArray *g_gpiod_line_request_get_requested_offsets(GPIODLineRequest *self); + +/** + * @brief Update the configuration of lines associated with a line request. + * @param self GPIO line request object to manipulate. + * @param config New line config to apply. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on failure. + * @note The new line configuration completely replaces the old. + * @note Any requested lines without overrides are configured to the requested + * defaults. + * @note Any configured overrides for lines that have not been requested + * are silently ignored. + */ +gboolean g_gpiod_line_request_reconfigure_lines(GPIODLineRequest *self, + GPIODLineConfig *config, + GError **err); + +/** + * @brief Get the value of a single requested line. + * @param self GPIO line request object to manipulate. + * @param offset The offset of the line of which the value should be read. + * @param value Return location for the value. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on failure. + */ +gboolean +g_gpiod_line_request_get_value(GPIODLineRequest *self, guint offset, + GPIODLineValue *value, GError **err); + +/** + * @brief Get the values of a subset of requested lines. + * @param self GPIO line request object to manipulate. + * @param offsets Array of offsets identifying the subset of requested lines + * from which to read values. + * @param values Array in which the values will be stored. Can be NULL in which + * case a new array will be created and its location stored here. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on failure. + */ +gboolean g_gpiod_line_request_get_values_subset(GPIODLineRequest *self, + const GArray *offsets, + GArray **values, + GError **err); + +/** + * @brief Get the values of all requested lines. + * @param self GPIO line request object to manipulate. + * @param values Array in which the values will be stored. Can be NULL in which + * case a new array will be created and its location stored here. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on failure. + */ +gboolean g_gpiod_line_request_get_values(GPIODLineRequest *self, + GArray **values, GError **err); + +/** + * @brief Set the value of a single requested line. + * @param self GPIO line request object to manipulate. + * @param offset The offset of the line for which the value should be set. + * @param value Value to set. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on failure. + */ +gboolean g_gpiod_line_request_set_value(GPIODLineRequest *self, guint offset, + GPIODLineValue value, GError **err); + +/** + * @brief Set the values of a subset of requested lines. + * @param self GPIO line request object to manipulate. + * @param offsets Array of offsets identifying the requested lines for + * which to set values. + * @param values Array in which the values will be stored. Can be NULL in which + * case a new array will be created and its location stored here. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on failure. + */ +gboolean g_gpiod_line_request_set_values_subset(GPIODLineRequest *self, + const GArray *offsets, + const GArray *values, + GError **err); + +/** + * @brief Set the values of all lines associated with a request. + * @param self GPIO line request object to manipulate. + * @param values Array containing the values to set. Must be sized to + * contain the number of values equal to the number of requested + * lines. Each value is associated with the line identified by + * the corresponding entry in the offset array filled by + * ::g_gpiod_line_request_get_requested_offsets. + * case a new array will be created and its location stored here. + * @param err Return location for error or NULL. + * @return TRUE on success, FALSE on failure. + */ +gboolean g_gpiod_line_request_set_values(GPIODLineRequest *self, + GArray *values, GError **err); + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_LINE_REQUEST_H__ */ diff --git a/bindings/glib/gpiod-glib/line-settings.h b/bindings/glib/gpiod-glib/line-settings.h new file mode 100644 index 0000000..fbdf911 --- /dev/null +++ b/bindings/glib/gpiod-glib/line-settings.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2023 Bartosz Golaszewski */ + +/** + * @file line-settings.h + */ + +#ifndef __GPIOD_GLIB_LINE_SETTINGS_H__ +#define __GPIOD_GLIB_LINE_SETTINGS_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include +#include + +#include "line.h" + +G_BEGIN_DECLS + +/** + * @cond + */ + +G_DECLARE_FINAL_TYPE(GPIODLineSettings, g_gpiod_line_settings, + G_GPIOD, LINE_SETTINGS, GObject); + +/** + * @endcond + * + * @ingroup gpiod_glib + * @{ + * + * @defgroup gobject_line_settings GPIO line-settings GObject + * @{ + * + * Line settings object contains a set of line properties that can be used + * when requesting lines or reconfiguring an existing request. + */ + +/** + * @brief Get the GObject type for the GPIO line-settings. + */ +#define G_GPIOD_LINE_SETTINGS_TYPE (g_gpiod_line_settings_get_type()) + +/** + * @brief Cast a GObject to a GPIO line-settings concrete GObject. + */ +#define G_GPIOD_LINE_SETTINGS_OBJ(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOD_LINE_SETTINGS_TYPE, \ + GPIODLineSettings)) + +/** + * @brief Create a new line settings object. + * @param first_prop Name of the first property to set. + * @return New line settings object. + * + * The constructor allows to set object's properties when it's first created + * instead of having to build an empty object and then call mutators separately. + * + * Currently supported properties are: `direction`, `edge-detection`, `bias`, + * `drive`, `debounce-period-us`, `active-low`, 'event-clock` and + * `output-value`. + */ +GPIODLineSettings *g_gpiod_line_settings_new(const gchar *first_prop, ...); + +/** + * @brief Reset the line settings object to its default values. + * @param self Line settings object to manipulate. + */ +void g_gpiod_line_settings_reset(GPIODLineSettings *self); + +/** + * @brief Set direction. + * @param self Line settings object to manipulate. + * @param direction New direction. + */ +void g_gpiod_line_settings_set_direction(GPIODLineSettings *self, + GPIODLineDirection direction); + +/** + * @brief Get direction. + * @param self Line settings object to manipulate. + * @return Current direction. + */ +GPIODLineDirection g_gpiod_line_settings_get_direction(GPIODLineSettings *self); + +/** + * @brief Set edge detection. + * @param self Line settings object to manipulate. + * @param edge New edge detection setting. + */ +void g_gpiod_line_settings_set_edge_detection(GPIODLineSettings *self, + GPIODLineEdge edge); + +/** + * @brief Get edge detection. + * @param self Line settings object to manipulate. + * @return Current edge detection setting. + */ +GPIODLineEdge g_gpiod_line_settings_get_edge_detection(GPIODLineSettings *self); + +/** + * @brief Set bias. + * @param self Line settings object to manipulate. + * @param bias New bias. + */ +void g_gpiod_line_settings_set_bias(GPIODLineSettings *self, + GPIODLineBias bias); + +/** + * @brief Get bias. + * @param self Line settings object to manipulate. + * @return Current bias setting. + */ +GPIODLineBias g_gpiod_line_settings_get_bias(GPIODLineSettings *self); + +/** + * @brief Set drive. + * @param self Line settings object to manipulate. + * @param drive New drive setting. + */ +void g_gpiod_line_settings_set_drive(GPIODLineSettings *self, + GPIODLineDrive drive); + +/** + * @brief Get drive. + * @param self Line settings object to manipulate. + * @return Current drive setting. + */ +GPIODLineDrive g_gpiod_line_settings_get_drive(GPIODLineSettings *self); + +/** + * @brief Set active-low setting. + * @param self Line settings object to manipulate. + * @param active_low New active-low setting. + */ +void g_gpiod_line_settings_set_active_low(GPIODLineSettings *self, + gboolean active_low); + +/** + * @brief Get active-low setting. + * @param self Line settings object to manipulate. + * @return TRUE if active-low is enabled, FALSE otherwise. + */ +gboolean g_gpiod_line_settings_get_active_low(GPIODLineSettings *self); + +/** + * @brief Set debounce period. + * @param self Line settings object to manipulate. + * @param period New debounce period in microseconds. + */ +void g_gpiod_line_settings_set_debounce_period_us(GPIODLineSettings *self, + GTimeSpan period); + +/** + * @brief Get debounce period. + * @param self Line settings object to manipulate. + * @return Current debounce period in microseconds. + */ +GTimeSpan g_gpiod_line_settings_get_debounce_period_us(GPIODLineSettings *self); + +/** + * @brief Set event clock. + * @param self Line settings object to manipulate. + * @param event_clock New event clock. + */ +void g_gpiod_line_settings_set_event_clock(GPIODLineSettings *self, + GPIODLineClock event_clock); + +/** + * @brief Get event clock setting. + * @param self Line settings object to manipulate. + * @return Current event clock setting. + */ +GPIODLineClock g_gpiod_line_settings_get_event_clock(GPIODLineSettings *self); + +/** + * @brief Set the output value. + * @param self Line settings object to manipulate. + * @param value New output value. + */ +void g_gpiod_line_settings_set_output_value(GPIODLineSettings *self, + GPIODLineValue value); + +/** + * @brief Get the output value. + * @param self Line settings object to manipulate. + * @return Current output value. + */ +GPIODLineValue g_gpiod_line_settings_get_output_value(GPIODLineSettings *self); + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_LINE_SETTINGS_H__ */ diff --git a/bindings/glib/gpiod-glib/line.h b/bindings/glib/gpiod-glib/line.h new file mode 100644 index 0000000..9d90b74 --- /dev/null +++ b/bindings/glib/gpiod-glib/line.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2023 Bartosz Golaszewski */ + +/** + * @file line.h + */ + +#ifndef __GPIOD_GLIB_LINE_H__ +#define __GPIOD_GLIB_LINE_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * @ingroup gpiod_glib + * @{ + * + * @defgroup gpiod_glib_line Line definitions + * @{ + * + * These defines are used across the API. + */ + +/** + * @brief Logical line state. + */ +typedef enum { + G_GPIOD_LINE_VALUE_INACTIVE = 0, + /**< Line is logically inactive. */ + G_GPIOD_LINE_VALUE_ACTIVE = 1, + /**< Line is logically active. */ +} GPIODLineValue; + +/** + * @brief Direction settings. + */ +typedef enum { + G_GPIOD_LINE_DIRECTION_AS_IS = 1, + /**< Request the line(s), but don't change direction. */ + G_GPIOD_LINE_DIRECTION_INPUT, + /**< Direction is input - for reading the value of an externally driven + * GPIO line. */ + G_GPIOD_LINE_DIRECTION_OUTPUT, + /**< Direction is output - for driving the GPIO line. */ +} GPIODLineDirection; + +/** + * @brief Edge detection settings. + */ +typedef enum { + G_GPIOD_LINE_EDGE_NONE = 1, + /**< Line edge detection is disabled. */ + G_GPIOD_LINE_EDGE_RISING, + /**< Line detects rising edge events. */ + G_GPIOD_LINE_EDGE_FALLING, + /**< Line detects falling edge events. */ + G_GPIOD_LINE_EDGE_BOTH, + /**< Line detects both rising and falling edge events. */ +} GPIODLineEdge; + +/** + * @brief Internal bias settings. + */ +typedef enum { + G_GPIOD_LINE_BIAS_AS_IS = 1, + /**< Don't change the bias setting when applying line config. */ + G_GPIOD_LINE_BIAS_UNKNOWN, + /**< The internal bias state is unknown. */ + G_GPIOD_LINE_BIAS_DISABLED, + /**< The internal bias is disabled. */ + G_GPIOD_LINE_BIAS_PULL_UP, + /**< The internal pull-up bias is enabled. */ + G_GPIOD_LINE_BIAS_PULL_DOWN, + /**< The internal pull-down bias is enabled. */ +} GPIODLineBias; + +/** + * @brief Drive settings. + */ +typedef enum { + G_GPIOD_LINE_DRIVE_PUSH_PULL = 1, + /**< Drive setting is push-pull. */ + G_GPIOD_LINE_DRIVE_OPEN_DRAIN, + /**< Line output is open-drain. */ + G_GPIOD_LINE_DRIVE_OPEN_SOURCE, + /**< Line output is open-source. */ +} GPIODLineDrive; + +/** + * @brief Clock settings. + */ +typedef enum { + G_GPIOD_LINE_CLOCK_MONOTONIC = 1, + /**< Line uses the monotonic clock for edge event timestamps. */ + G_GPIOD_LINE_CLOCK_REALTIME, + /**< Line uses the realtime clock for edge event timestamps. */ + G_GPIOD_LINE_CLOCK_HTE, + /**< Line uses the hardware timestamp engine for event timestamps. */ +} GPIODLineClock; + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_LINE_H__ */ diff --git a/bindings/glib/gpiod-glib/misc.h b/bindings/glib/gpiod-glib/misc.h new file mode 100644 index 0000000..d555aae --- /dev/null +++ b/bindings/glib/gpiod-glib/misc.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +/** + * @file misc.h + */ + +#ifndef __GPIOD_GLIB_MISC_H__ +#define __GPIOD_GLIB_MISC_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * @ingroup gpiod_glib + * @{ + * + * @defgroup gpiod_glib_misc Misc functions. + * @{ + */ + +/** + * @brief Check if the file pointed to by path is a GPIO chip character device. + * @param path Path to check. + * @return TRUE if the file exists and is either a GPIO chip character device + * or a symbolic link to one, FALSE otherwise. + */ +gboolean g_gpiod_is_gpiochip_device(const gchar *path); + +/** + * @brief Get the API version of the library as a human-readable string. + * @return A valid pointer to a human-readable string containing the library + * version. The pointer is valid for the lifetime of the program and + * must not be freed by the caller. + */ +const gchar *g_gpiod_api_version(void); + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_MISC_H__ */ diff --git a/bindings/glib/gpiod-glib/request-config.h b/bindings/glib/gpiod-glib/request-config.h new file mode 100644 index 0000000..c413f0f --- /dev/null +++ b/bindings/glib/gpiod-glib/request-config.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2023 Bartosz Golaszewski */ + +/** + * @file request-config.h + */ + +#ifndef __GPIOD_GLIB_REQUEST_CONFIG_H__ +#define __GPIOD_GLIB_REQUEST_CONFIG_H__ + +#if !defined (__GPIOD_GLIB_INSIDE__) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +/** + * @cond + */ + +G_DECLARE_FINAL_TYPE(GPIODRequestConfig, g_gpiod_request_config, + G_GPIOD, REQUEST_CONFIG, GObject); + +/** + * @endcond + * + * @ingroup gpiod_glib + * @{ + * + * @defgroup gobject_request_config GPIO request-config GObject + * @{ + * + * Request config objects are used to pass a set of options to the kernel at + * the time of the line request. + */ + +/** + * @brief Get the GObject type for the GPIO request-config. + */ +#define G_GPIOD_REQUEST_CONFIG_TYPE (g_gpiod_request_config_get_type()) + +/** + * @brief Cast a GObject to a GPIO request-config concrete GObject. + */ +#define G_GPIOD_REQUEST_CONFIG_OBJ(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), G_GPIOD_REQUEST_CONFIG_TYPE, \ + GPIODRequestConfig)) + +/** + * @brief Create a new request config object. + * @param first_prop Name of the first property to set. + * @return New request config object. + * + * The constructor allows to set object's properties when it's first created + * instead of having to build an empty object and then call mutators separately. + * + * Currently supported properties are: `consumer` and `event-buffer-size`. + */ +GPIODRequestConfig *g_gpiod_request_config_new(const gchar *first_prop, ...); + +/** + * @brief Set the consumer name for the request. + * @param self GPIO request config object to manipulate. + * @param consumer Consumer name. + * @note If the consumer string is too long, it will be truncated to the max + * accepted length. + */ +void g_gpiod_request_config_set_consumer(GPIODRequestConfig *self, + const gchar *consumer); + +/** + * @brief Get the consumer name configured in the request config. + * @param self GPIO request config object to manipulate. + * @return Consumer name stored in the request config. + */ +const gchar *g_gpiod_request_config_get_consumer(GPIODRequestConfig *self); + +/** + * @brief Set the size of the kernel event buffer for the request. + * @param self GPIO request config object to manipulate. + * @param event_buffer_size New event buffer size. + * @note The kernel may adjust the value if it's too high. If set to 0, the + * default value will be used. + */ +void g_gpiod_request_config_set_event_buffer_size(GPIODRequestConfig *self, + guint event_buffer_size); + + +/** + * @brief Get the edge event buffer size for the request config. + * @param self GPIO request config object to manipulate. + * @return Edge event buffer size setting from the request config. + */ +guint g_gpiod_request_config_get_event_buffer_size(GPIODRequestConfig *self); + +/** + * @} + * + * @} + */ + +G_END_DECLS + +#endif /* __GPIOD_GLIB_REQUEST_CONFIG_H__ */ From patchwork Fri Jun 28 18:58:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808532 Received: from mail-wr1-f41.google.com (mail-wr1-f41.google.com [209.85.221.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D4ACA57CAC for ; Fri, 28 Jun 2024 18:59:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601149; cv=none; b=APK5TtBH4wkUc8FsDVYRqzAc36qija6y/EA1I/ehBmSa4nCgLzRSRb823ySbU9C3Z9UkVnnO6JKCy6F+goFk1y28a7QfGw9Oan10d6c9cVFX/plXRQ0enl+FN8sbvUNoqq7Z2RO5GSNS+twd+QhsTV8MgWffljGtogTxRSDjka8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601149; c=relaxed/simple; bh=mdjjl2IMNF3Ssf4wGz9H2i6AZ0zD2QLj2X7GbvaPJ1c=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=AX6YHG7qEoSW6/mZnywdCyxKPvITTqlNxMZopnNYEBb+uH4eg0Ush7dvVBHSs/kFE3EDvR6yFwtOf/lTiNYCw09HLbWrNfvdYl+aW6MG2gx7QMPYyOBHTkN8N6dgTTFuosYZKIAAm2ROeG5HPhV7xfnHAouie0WBwnqmWqOtg4M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=zE8wxzoD; arc=none smtp.client-ip=209.85.221.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="zE8wxzoD" Received: by mail-wr1-f41.google.com with SMTP id ffacd0b85a97d-364a39824baso557570f8f.1 for ; Fri, 28 Jun 2024 11:59:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601143; x=1720205943; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=kb2Yln08TMSFxQEfIChyrOakYjsGVTl77vIFUgXk0fg=; b=zE8wxzoDVUXzzISxuUxBCio+w/Y0KttyFRQS8fRUdrer4WXY2/AAvxDkwFSyjbqrvO G/Ls0oslCSI2jcsObhYWRRaP0errlFsoOHp4bSKaznzozuq0arxCQtXDnvwq9Ce1w/M2 GxoOUcAyyJWwjm6Yaj+VqRzSZHoBxLqttqEWmjCpyJzD/h5oP7YQjxZxPDFJ8tdJWA0J D+mM6P8wY3qvCXn590eFYkIy24OZYNoq92OG+vSP2OAStyO8N5xz/EbVDXNyS+XjktmZ zn+o0Ge6hCpNhQUGakrvnBtT8imI8pojQXRAvAWy3nvrsSKRo079Ag4OX8LmuXA3CXBf Lu/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601143; x=1720205943; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kb2Yln08TMSFxQEfIChyrOakYjsGVTl77vIFUgXk0fg=; b=cWntIGcWvk6bd+3oK0CxKOIH7wnLnsfQXQAvdbWb0MdbHbpVRuU4k4ZPDzTK5BnMvi YkBM1TZBOoUzoYTvZyyRO5eRfpJtPv2aQ3WX1MNYYZNXdOBZ0iPt0E7Lx4btD4mIhpYG xmZLRAIPVEmYCZzA6/kvZLLCCu9lL4tqDNdwppHu68hmmbhQ3fMjRDCisNWy0Qa/77FU k9cL6IfMhNkn00b1ci+uJ2Ilu5cVgiK0gQ6p1d4pgGtLRXhMBB+U9dqaDKNYvokILLrA F6g3PtmpmfMT/TUuvWhLQyGUXDpxGvRrgaIfRGUL4lujp1zxEJjZDtnGry65L6e13MG5 YlAg== X-Forwarded-Encrypted: i=1; AJvYcCWC2jEYJrYBlWAJQEsTxxOQeqt47XImrjP1EIuXp2RddGE7AqBLUGWCXWQaVyDnmwBXqJQbas+kl+r7DpfNcl2Jb252hgvTZg3ZEA== X-Gm-Message-State: AOJu0Yx8jbhu99HV+Dft7Yq9xfSOiBrmbiFyHa0Gr95+8xyQ0sISxdJR jyLJeDCaF9A970vrSUQDSgKFcWmU3co9mTp7wOKpn/CYzfeKdHdcNRJLP7U+bH0= X-Google-Smtp-Source: AGHT+IHg+PLBcwMXs2JaOVHX5cjPY5UADYYWRj2+xFY8vepXxvU7YIUDDy1KJw6UXezV/q7HDrqQJA== X-Received: by 2002:adf:e742:0:b0:367:43ce:9c9a with SMTP id ffacd0b85a97d-36743ce9f84mr3679830f8f.37.1719601142973; Fri, 28 Jun 2024 11:59:02 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:02 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:24 +0200 Subject: [PATCH RESEND libgpiod v2 05/18] bindings: glib: add core code Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-5-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=84166; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=EPRdoubmRD3R0jNUbrrU9qS18PAMcHhZTpuMMM6JC2w=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfqpRloYX+zJ2vY2cSq4bxnr13HLi726zkPB LXjJlIubmWJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H6gAKCRARpy6gFHHX clecEADNFMSftpkVjfVaSMKcg+MenCxWHW4e8SC6fpGO61YvzuuMv/2I65oKbJYM5OfzyK6vYDC inLY8gLV/E+/65RN4L1X02tZelUvS0NJap4W+RDbN9gwA3WCIVnxn78ncU9GGzlD4wv5GQHvzKy w2+pvNMVItwwtHBkr8GrVIAJ/NmekmAzjGUGdXjOFNZtaB37ehpdZ0a8HLjaRAUW69G/14+b4+x MHdNm61AUD2SVZha2i8JJEOFP4ST4NUakXZGtq1omsyKNrRK4NI3tOXIdxc8EnQePRHc5Cl/FWg DwUlPMY8JXK1CnSEokWDgCAgYfy/tIO1db8dPFQvgzHJlGxALDbjf6yqYpB9PgbsPJ9TP71TqgW DUB//d1s4KMF9hCOPRFGtgTcDjO1F2i1gXjpqDovXoow+oykE+3kMhm59I8O0mfR//YV3M5klNt 9/rgH95h6eFYLlh2Nl59B/6LnqXGQDqTseGYDMlSXZcq9a2HZLe055M5v59hGHFq41mBKayJyOK uXOVmxWbps3Av1aR3+XIPyI7Pbjy8jGYhXIiTk0KXmE/DU+y2vcOAGZYJUIkr+pcF572OtdPPIv EEsS4wbRobjCCTYS/IYbVYjJHdfBcClMRzotrn9ThGOcmVsnXUoWoy1wiurjq2QnBEdIedmTwXs QcMMG76yCkQgJPQ== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add the files implementing the public API of the GLib bindings to libgpiod. Signed-off-by: Bartosz Golaszewski --- bindings/glib/chip-info.c | 118 +++++++++ bindings/glib/chip.c | 396 ++++++++++++++++++++++++++++ bindings/glib/edge-event.c | 158 +++++++++++ bindings/glib/error.c | 67 +++++ bindings/glib/generated-enums.c.template | 43 +++ bindings/glib/info-event.c | 150 +++++++++++ bindings/glib/internal.c | 334 ++++++++++++++++++++++++ bindings/glib/internal.h | 54 ++++ bindings/glib/line-config.c | 186 +++++++++++++ bindings/glib/line-info.c | 274 +++++++++++++++++++ bindings/glib/line-request.c | 434 +++++++++++++++++++++++++++++++ bindings/glib/line-settings.c | 359 +++++++++++++++++++++++++ bindings/glib/misc.c | 17 ++ bindings/glib/request-config.c | 155 +++++++++++ 14 files changed, 2745 insertions(+) diff --git a/bindings/glib/chip-info.c b/bindings/glib/chip-info.c new file mode 100644 index 0000000..a2038c6 --- /dev/null +++ b/bindings/glib/chip-info.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include +#include + +#include "internal.h" + +struct _GPIODChipInfo { + GObject parent_instance; + struct gpiod_chip_info *handle; +}; + +enum { + G_GPIOD_CHIP_INFO_PROP_HANDLE = 1, + G_GPIOD_CHIP_INFO_PROP_NAME, + G_GPIOD_CHIP_INFO_PROP_LABEL, + G_GPIOD_CHIP_INFO_PROP_NUM_LINES, +}; + +G_DEFINE_TYPE(GPIODChipInfo, g_gpiod_chip_info, G_TYPE_OBJECT); + +static void g_gpiod_chip_info_get_property(GObject *obj, guint prop_id, + GValue *val, GParamSpec *pspec) +{ + GPIODChipInfo *self = G_GPIOD_CHIP_INFO_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_CHIP_INFO_PROP_NAME: + g_value_set_static_string(val, + gpiod_chip_info_get_name(self->handle)); + break; + case G_GPIOD_CHIP_INFO_PROP_LABEL: + g_value_set_static_string(val, + gpiod_chip_info_get_label(self->handle)); + break; + case G_GPIOD_CHIP_INFO_PROP_NUM_LINES: + g_value_set_uint(val, + gpiod_chip_info_get_num_lines(self->handle)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_chip_info_set_property(GObject *obj, guint prop_id, + const GValue *val, GParamSpec *pspec) +{ + GPIODChipInfo *self = G_GPIOD_CHIP_INFO_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_CHIP_INFO_PROP_HANDLE: + self->handle = g_value_get_pointer(val); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_chip_info_finalize(GObject *obj) +{ + GPIODChipInfo *self = G_GPIOD_CHIP_INFO_OBJ(obj); + + g_clear_pointer(&self->handle, gpiod_chip_info_free); + + G_OBJECT_CLASS(g_gpiod_chip_info_parent_class)->finalize(obj); +} + +static void g_gpiod_chip_info_class_init(GPIODChipInfoClass *chip_info_class) +{ + GObjectClass *class = G_OBJECT_CLASS(chip_info_class); + + class->set_property = g_gpiod_chip_info_set_property; + class->get_property = g_gpiod_chip_info_get_property; + class->finalize = g_gpiod_chip_info_finalize; + + g_object_class_install_property(class, G_GPIOD_CHIP_INFO_PROP_HANDLE, + g_param_spec_pointer("handle", "Handle", + "GPIO Chip information object.", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); + + g_object_class_install_property(class, G_GPIOD_CHIP_INFO_PROP_NAME, + g_param_spec_string("name", "Name", + "Name of this GPIO chip device.", NULL, + G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOD_CHIP_INFO_PROP_LABEL, + g_param_spec_string("label", "Label", + "Label of this GPIO chip device.", NULL, + G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOD_CHIP_INFO_PROP_NUM_LINES, + g_param_spec_uint("num-lines", "NumLines", + "Number of GPIO lines exposed by this chip.", + 1, G_MAXUINT, 1, + G_PARAM_READABLE)); +} + +static void g_gpiod_chip_info_init(GPIODChipInfo *self) +{ + self->handle = NULL; +} + +const gchar *g_gpiod_chip_info_get_name(GPIODChipInfo *self) +{ + return g_gpiod_get_prop_string(G_OBJECT(self), "name"); +} + +const gchar *g_gpiod_chip_info_get_label(GPIODChipInfo *self) +{ + return g_gpiod_get_prop_string(G_OBJECT(self), "label"); +} + +guint g_gpiod_chip_info_get_num_lines(GPIODChipInfo *self) +{ + return g_gpiod_get_prop_uint(G_OBJECT(self), "num-lines"); +} diff --git a/bindings/glib/chip.c b/bindings/glib/chip.c new file mode 100644 index 0000000..bd8495b --- /dev/null +++ b/bindings/glib/chip.c @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include +#include + +#include "internal.h" + +struct _GPIODChip { + GObject parent_instance; + GString *path; + GError *construct_err; + struct gpiod_chip *handle; + GSource *info_event_src; + guint info_event_src_id; +}; + +enum { + G_GPIOD_CHIP_PROP_PATH = 1, + G_GPIOD_CHIP_PROP_HANDLE, +}; + +enum { + G_GPIOD_CHIP_SIGNAL_INFO_EVENT, + G_GPIOD_CHIP_SIGNAL_LAST, +}; + +static guint signals[G_GPIOD_CHIP_SIGNAL_LAST]; + +static void g_string_free_complete(GString *str) +{ + g_string_free(str, TRUE); +} + +static gboolean g_gpiod_chip_on_info_event(GIOChannel *source G_GNUC_UNUSED, + GIOCondition condition G_GNUC_UNUSED, + gpointer data) +{ + g_autoptr(GPIODInfoEvent) event = NULL; + struct gpiod_info_event *event_handle; + GPIODChip *self = data; + + event_handle = gpiod_chip_read_info_event(self->handle); + if (!event_handle) + return TRUE; + + event = G_GPIOD_INFO_EVENT_OBJ(g_object_new(G_GPIOD_INFO_EVENT_TYPE, + "handle", event_handle, + NULL)); + + g_signal_emit(self, + signals[G_GPIOD_CHIP_SIGNAL_INFO_EVENT], + 0, + event); + + return TRUE; +} + +static gboolean +g_gpiod_chip_initable_init(GInitable *initable, + GCancellable *cancellable G_GNUC_UNUSED, + GError **err) +{ + GPIODChip *self = G_GPIOD_CHIP_OBJ(initable); + + if (self->construct_err) { + g_propagate_error(err, self->construct_err); + self->construct_err = NULL; + return FALSE; + } + + return TRUE; +} + +static void g_gpiod_chip_initable_iface_init(GInitableIface *iface) +{ + iface->init = g_gpiod_chip_initable_init; +} + +G_DEFINE_TYPE_WITH_CODE(GPIODChip, g_gpiod_chip, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE( + G_TYPE_INITABLE, + g_gpiod_chip_initable_iface_init)); + +static void g_gpiod_chip_constructed(GObject *obj) +{ + GPIODChip *self = G_GPIOD_CHIP_OBJ(obj); + g_autoptr(GIOChannel) channel = NULL; + + g_assert(!self->handle); + g_assert(self->path); + + self->handle = gpiod_chip_open(self->path->str); + if (!self->handle) { + g_gpiod_set_error_from_errno(&self->construct_err, + "unable to open GPIO chip '%s'", + self->path->str); + return; + } + + channel = g_io_channel_unix_new(gpiod_chip_get_fd(self->handle)); + self->info_event_src = g_io_create_watch(channel, G_IO_IN); + g_source_set_callback(self->info_event_src, + G_SOURCE_FUNC(g_gpiod_chip_on_info_event), + self, NULL); + self->info_event_src_id = g_source_attach(self->info_event_src, NULL); + + G_OBJECT_CLASS(g_gpiod_chip_parent_class)->constructed(obj); +} + +static void g_gpiod_chip_get_property(GObject *obj, guint prop_id, + GValue *val, GParamSpec *pspec) +{ + GPIODChip *self = G_GPIOD_CHIP_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_CHIP_PROP_PATH: + g_value_set_static_string(val, self->path->str); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_chip_set_property(GObject *obj, guint prop_id, + const GValue *val, GParamSpec *pspec) +{ + GPIODChip *self = G_GPIOD_CHIP_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_CHIP_PROP_PATH: + self->path = g_string_new(g_value_get_string(val)); + break; + case G_GPIOD_CHIP_PROP_HANDLE: + self->handle = g_value_get_pointer(val); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +void g_gpiod_chip_close(GPIODChip *self) +{ + g_clear_pointer(&self->info_event_src, g_source_unref); + g_clear_pointer(&self->handle, gpiod_chip_close); +} + +static void g_gpiod_chip_dispose(GObject *obj) +{ + GPIODChip *self = G_GPIOD_CHIP_OBJ(obj); + + if (self->info_event_src_id) + g_source_remove(self->info_event_src_id); + + g_gpiod_chip_close(self); + + G_OBJECT_CLASS(g_gpiod_chip_parent_class)->dispose(obj); +} + +static void g_gpiod_chip_finalize(GObject *obj) +{ + GPIODChip *self = G_GPIOD_CHIP_OBJ(obj); + + g_clear_error(&self->construct_err); + g_clear_pointer(&self->path, g_string_free_complete); + + G_OBJECT_CLASS(g_gpiod_chip_parent_class)->finalize(obj); +} + +static void g_gpiod_chip_class_init(GPIODChipClass *chip_class) +{ + GObjectClass *class = G_OBJECT_CLASS(chip_class); + + class->constructed = g_gpiod_chip_constructed; + class->get_property = g_gpiod_chip_get_property; + class->set_property = g_gpiod_chip_set_property; + class->dispose = g_gpiod_chip_dispose; + class->finalize = g_gpiod_chip_finalize; + + g_object_class_install_property(class, G_GPIOD_CHIP_PROP_PATH, + g_param_spec_string("path", "Path", + "Path to the GPIO chip device used to create this chip.", + NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(class, G_GPIOD_CHIP_PROP_HANDLE, + g_param_spec_pointer("handle", "Handle", + "Open GPIO chip handle as returned by gpiod_chip_open().", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); + + signals[G_GPIOD_CHIP_SIGNAL_INFO_EVENT] = + g_signal_new("info-event", + G_TYPE_FROM_CLASS(chip_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, + G_GPIOD_INFO_EVENT_TYPE); +} + +static void g_gpiod_chip_init(GPIODChip *self) +{ + self->path = NULL; + self->construct_err = NULL; + self->handle = NULL; + self->info_event_src = NULL; + self->info_event_src_id = 0; +} + +GPIODChip *g_gpiod_chip_new(const gchar *path, GError **err) +{ + return G_GPIOD_CHIP_OBJ(g_initable_new(G_GPIOD_CHIP_TYPE, NULL, err, + "path", path, NULL)); +} + +gboolean g_gpiod_chip_is_closed(GPIODChip *self) +{ + return !self->handle; +} + +const gchar *g_gpiod_chip_get_path(GPIODChip *self) +{ + return g_gpiod_get_prop_string(G_OBJECT(self), "path"); +} + +static void set_err_chip_closed(GError **err) +{ + g_set_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_CHIP_CLOSED, + "Chip was closed and cannot be used"); +} + +GPIODChipInfo *g_gpiod_chip_get_info(GPIODChip *self, GError **err) +{ + struct gpiod_chip_info *info; + + g_assert(self); + + if (g_gpiod_chip_is_closed(self)) { + set_err_chip_closed(err); + return NULL; + } + + info = gpiod_chip_get_info(self->handle); + if (!info) { + g_gpiod_set_error_from_errno(err, + "unable to retrieve GPIO chip information"); + return NULL; + } + + return G_GPIOD_CHIP_INFO_OBJ(g_object_new(G_GPIOD_CHIP_INFO_TYPE, + "handle", info, NULL)); +} + +static GPIODLineInfo * +g_gpiod_chip_do_get_line_info(GPIODChip *self, guint offset, GError **err, + struct gpiod_line_info *(*func)(struct gpiod_chip *, + unsigned int), + const gchar *err_action) +{ + struct gpiod_line_info *info; + + g_assert(self); + + if (g_gpiod_chip_is_closed(self)) { + set_err_chip_closed(err); + return NULL; + } + + info = func(self->handle, offset); + if (!info) { + g_gpiod_set_error_from_errno(err, "unable to %s for offset %u", + err_action, offset); + return NULL; + } + + return G_GPIOD_LINE_INFO_OBJ(g_object_new(G_GPIOD_LINE_INFO_TYPE, + "handle", info, NULL)); + +} + +GPIODLineInfo * +g_gpiod_chip_get_line_info(GPIODChip *self, guint offset, GError **err) +{ + return g_gpiod_chip_do_get_line_info(self, offset, err, + gpiod_chip_get_line_info, + "retrieve GPIO line-info"); +} + +GPIODLineInfo * +g_gpiod_chip_watch_line_info(GPIODChip *self, guint offset, GError **err) +{ + return g_gpiod_chip_do_get_line_info(self, offset, err, + gpiod_chip_watch_line_info, + "setup a line-info watch"); +} + +gboolean +g_gpiod_chip_unwatch_line_info(GPIODChip *self, guint offset, GError **err) +{ + int ret; + + g_assert(self); + + if (g_gpiod_chip_is_closed(self)) { + set_err_chip_closed(err); + return FALSE; + } + + ret = gpiod_chip_unwatch_line_info(self->handle, offset); + if (ret) { + g_gpiod_set_error_from_errno(err, + "unable to unwatch line-info events for offset %u", + offset); + return FALSE; + } + + return TRUE; +} + +gboolean +g_gpiod_chip_get_line_offset_from_name(GPIODChip *self, const gchar *name, + guint *offset, GError **err) +{ + gint ret; + + g_assert(self); + + if (g_gpiod_chip_is_closed(self)) { + set_err_chip_closed(err); + return FALSE; + } + + if (!name) { + g_set_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL, + "name must not be NULL"); + return FALSE; + } + + ret = gpiod_chip_get_line_offset_from_name(self->handle, name); + if (ret < 0) { + if (errno != ENOENT) + g_gpiod_set_error_from_errno(err, + "failed to map line name to offset"); + else + errno = 0; + + return FALSE; + } + + if (offset) + *offset = ret; + + return TRUE; +} + +GPIODLineRequest *g_gpiod_chip_request_lines(GPIODChip *self, + GPIODRequestConfig *req_cfg, + GPIODLineConfig *line_cfg, + GError **err) +{ + struct gpiod_request_config *req_cfg_handle; + struct gpiod_line_config *line_cfg_handle; + struct gpiod_line_request *req; + + g_assert(self); + + if (g_gpiod_chip_is_closed(self)) { + set_err_chip_closed(err); + return NULL; + } + + if (!line_cfg) { + g_set_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL, + "line-config is required for request"); + return NULL; + } + + req_cfg_handle = req_cfg ? + g_gpiod_get_prop_pointer(G_OBJECT(req_cfg), "handle") : NULL; + line_cfg_handle = g_gpiod_get_prop_pointer(G_OBJECT(line_cfg), + "handle"); + + req = gpiod_chip_request_lines(self->handle, + req_cfg_handle, line_cfg_handle); + if (!req) { + g_gpiod_set_error_from_errno(err, + "failed to request GPIO lines"); + return NULL; + } + + return G_GPIOD_LINE_REQUEST_OBJ(g_object_new(G_GPIOD_LINE_REQUEST_TYPE, + "handle", req, NULL)); +} diff --git a/bindings/glib/edge-event.c b/bindings/glib/edge-event.c new file mode 100644 index 0000000..c732138 --- /dev/null +++ b/bindings/glib/edge-event.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include + +#include "internal.h" + +struct _GPIODEdgeEvent { + GObject parent_instance; + struct gpiod_edge_event *handle; +}; + +enum { + G_GPIOD_EDGE_EVENT_PROP_HANDLE = 1, + G_GPIOD_EDGE_EVENT_PROP_EVENT_TYPE, + G_GPIOD_EDGE_EVENT_PROP_TIMESTAMP_NS, + G_GPIOD_EDGE_EVENT_PROP_LINE_OFFSET, + G_GPIOD_EDGE_EVENT_PROP_GLOBAL_SEQNO, + G_GPIOD_EDGE_EVENT_PROP_LINE_SEQNO, +}; + +G_DEFINE_TYPE(GPIODEdgeEvent, g_gpiod_edge_event, G_TYPE_OBJECT); + +static void g_gpiod_edge_event_get_property(GObject *obj, guint prop_id, + GValue *val, GParamSpec *pspec) +{ + GPIODEdgeEvent *self = G_GPIOD_EDGE_EVENT_OBJ(obj); + GPIODEdgeEventType type; + + switch (prop_id) { + case G_GPIOD_EDGE_EVENT_PROP_EVENT_TYPE: + type = g_gpiod_edge_event_type_from_library( + gpiod_edge_event_get_event_type(self->handle)); + g_value_set_enum(val, type); + break; + case G_GPIOD_EDGE_EVENT_PROP_TIMESTAMP_NS: + g_value_set_uint64(val, + gpiod_edge_event_get_timestamp_ns(self->handle)); + break; + case G_GPIOD_EDGE_EVENT_PROP_LINE_OFFSET: + g_value_set_uint(val, + gpiod_edge_event_get_line_offset(self->handle)); + break; + case G_GPIOD_EDGE_EVENT_PROP_GLOBAL_SEQNO: + g_value_set_ulong(val, + gpiod_edge_event_get_global_seqno(self->handle)); + break; + case G_GPIOD_EDGE_EVENT_PROP_LINE_SEQNO: + g_value_set_ulong(val, + gpiod_edge_event_get_line_seqno(self->handle)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_edge_event_set_property(GObject *obj, guint prop_id, + const GValue *val, + GParamSpec *pspec) +{ + GPIODEdgeEvent *self = G_GPIOD_EDGE_EVENT_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_EDGE_EVENT_PROP_HANDLE: + self->handle = g_value_get_pointer(val); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_edge_event_finalize(GObject *obj) +{ + GPIODEdgeEvent *self = G_GPIOD_EDGE_EVENT_OBJ(obj); + + g_clear_pointer(&self->handle, gpiod_edge_event_free); + + G_OBJECT_CLASS(g_gpiod_edge_event_parent_class)->finalize(obj); +} + +static void g_gpiod_edge_event_class_init(GPIODEdgeEventClass *edge_event_class) +{ + GObjectClass *class = G_OBJECT_CLASS(edge_event_class); + + class->set_property = g_gpiod_edge_event_set_property; + class->get_property = g_gpiod_edge_event_get_property; + class->finalize = g_gpiod_edge_event_finalize; + + g_object_class_install_property(class, G_GPIOD_EDGE_EVENT_PROP_HANDLE, + g_param_spec_pointer("handle", "Handle", + "GPIO info event object.", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); + + g_object_class_install_property(class, + G_GPIOD_EDGE_EVENT_PROP_EVENT_TYPE, + g_param_spec_enum("event-type", "Event Type", + "Type of the edge event.", + G_GPIOD_EDGE_EVENT_TYPE_TYPE, + G_GPIOD_EDGE_EVENT_RISING_EDGE, + G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_EDGE_EVENT_PROP_TIMESTAMP_NS, + g_param_spec_uint64("timestamp-ns", + "Timestamp (in nanoseconds)", + "Timestamp of the edge event expressed in nanoseconds.", + 0, G_MAXUINT64, 0, G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_EDGE_EVENT_PROP_LINE_OFFSET, + g_param_spec_uint("line-offset", "Line Offset", + "Offset of the line on which this event was registered.", + 0, G_MAXUINT, 0, G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_EDGE_EVENT_PROP_GLOBAL_SEQNO, + g_param_spec_ulong("global-seqno", "Global Sequence Number", + "Global sequence number of this event", + 0, G_MAXULONG, 0, G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_EDGE_EVENT_PROP_LINE_SEQNO, + g_param_spec_ulong("line-seqno", "Line Sequence Number", + "Event sequence number specific to the line.", + 0, G_MAXULONG, 0, G_PARAM_READABLE)); +} + +static void g_gpiod_edge_event_init(GPIODEdgeEvent *self) +{ + self->handle = NULL; +} + +GPIODEdgeEventType g_gpiod_edge_event_get_event_type(GPIODEdgeEvent *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "event-type"); +} + +guint64 g_gpiod_edge_event_get_timestamp_ns(GPIODEdgeEvent *self) +{ + return g_gpiod_get_prop_uint64(G_OBJECT(self), "timestamp-ns"); +} + +guint g_gpiod_edge_event_get_line_offset(GPIODEdgeEvent *self) +{ + return g_gpiod_get_prop_uint(G_OBJECT(self), "line-offset"); +} + +gulong g_gpiod_edge_event_get_global_seqno(GPIODEdgeEvent *self) +{ + return g_gpiod_get_prop_ulong(G_OBJECT(self), "global-seqno"); +} + +gulong g_gpiod_edge_event_get_line_seqno(GPIODEdgeEvent *self) +{ + return g_gpiod_get_prop_ulong(G_OBJECT(self), "line-seqno"); +} diff --git a/bindings/glib/error.c b/bindings/glib/error.c new file mode 100644 index 0000000..6a1dc00 --- /dev/null +++ b/bindings/glib/error.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include +#include +#include + +G_DEFINE_QUARK(g-gpiod-error, g_gpiod_error) + +static GPIODError error_from_errno(void) +{ + switch (errno) { + case EPERM: + return G_GPIOD_ERR_PERM; + case ENOENT: + return G_GPIOD_ERR_NOENT; + case EINTR: + return G_GPIOD_ERR_INTR; + case EIO: + return G_GPIOD_ERR_IO; + case ENXIO: + return G_GPIOD_ERR_NXIO; + case E2BIG: + return G_GPIOD_ERR_E2BIG; + case EBADFD: + return G_GPIOD_ERR_BADFD; + case ECHILD: + return G_GPIOD_ERR_CHILD; + case EAGAIN: + return G_GPIOD_ERR_AGAIN; + case ENOMEM: + /* Special case - as a convention GLib just aborts on ENOMEM. */ + g_error("out of memory"); + case EACCES: + return G_GPIOD_ERR_ACCES; + case EFAULT: + return G_GPIOD_ERR_FAULT; + case EBUSY: + return G_GPIOD_ERR_BUSY; + case EEXIST: + return G_GPIOD_ERR_EXIST; + case ENODEV: + return G_GPIOD_ERR_NODEV; + case EINVAL: + return G_GPIOD_ERR_INVAL; + case ENOTTY: + return G_GPIOD_ERR_NOTTY; + case EPIPE: + return G_GPIOD_ERR_PIPE; + default: + return G_GPIOD_ERR_FAILED; + } +} + +void g_gpiod_set_error_from_errno(GError **err, const gchar *fmt, ...) +{ + g_autofree gchar *msg = NULL; + va_list va; + + va_start(va, fmt); + msg = g_strdup_vprintf(fmt, va); + va_end(va); + + g_set_error(err, G_GPIOD_ERROR, error_from_errno(), + "%s: %s", msg, g_strerror(errno)); +} diff --git a/bindings/glib/generated-enums.c.template b/bindings/glib/generated-enums.c.template new file mode 100644 index 0000000..c124eb7 --- /dev/null +++ b/bindings/glib/generated-enums.c.template @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/*** BEGIN file-header ***/ + +#include + +/*** END file-header ***/ + +/*** BEGIN file-production ***/ + +/* enumerations from "@basename@" */ + +/*** END file-production ***/ + +/*** BEGIN value-header ***/ + +GType @enum_name@_get_type(void) +{ + static gsize static_g_@type@_type_id; + + if (g_once_init_enter(&static_g_@type@_type_id)) { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + {@VALUENAME@, "@VALUENAME@", "@valuenick@"}, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + + GType g_@type@_type_id = g_@type@_register_static( + g_intern_static_string("@EnumName@"), values); + + g_once_init_leave (&static_g_@type@_type_id, g_@type@_type_id); + } + + return static_g_@type@_type_id; +} + +/*** END value-tail ***/ diff --git a/bindings/glib/info-event.c b/bindings/glib/info-event.c new file mode 100644 index 0000000..4abaee3 --- /dev/null +++ b/bindings/glib/info-event.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include + +#include "internal.h" + +struct _GPIODInfoEvent { + GObject parent_instance; + struct gpiod_info_event *handle; + GPIODLineInfo *info; +}; + +enum { + G_GPIOD_INFO_EVENT_PROP_HANDLE = 1, + G_GPIOD_INFO_EVENT_PROP_EVENT_TYPE, + G_GPIOD_INFO_EVENT_PROP_TIMESTAMP, + G_GPIOD_INFO_EVENT_PROP_LINE_INFO, +}; + +G_DEFINE_TYPE(GPIODInfoEvent, g_gpiod_info_event, G_TYPE_OBJECT); + +static void g_gpiod_info_event_get_property(GObject *obj, guint prop_id, + GValue *val, GParamSpec *pspec) +{ + GPIODInfoEvent *self = G_GPIOD_INFO_EVENT_OBJ(obj); + struct gpiod_line_info *info, *cpy; + GPIODInfoEventType type; + + switch (prop_id) { + case G_GPIOD_INFO_EVENT_PROP_EVENT_TYPE: + type = g_gpiod_info_event_type_from_library( + gpiod_info_event_get_event_type(self->handle)); + g_value_set_enum(val, type); + break; + case G_GPIOD_INFO_EVENT_PROP_TIMESTAMP: + g_value_set_uint64(val, + gpiod_info_event_get_timestamp_ns(self->handle)); + break; + case G_GPIOD_INFO_EVENT_PROP_LINE_INFO: + if (!self->info) { + info = gpiod_info_event_get_line_info(self->handle); + cpy = gpiod_line_info_copy(info); + if (!cpy) + g_error("Failed to allocate memory for line-info object"); + + self->info = G_GPIOD_LINE_INFO_OBJ( + g_object_new(G_GPIOD_LINE_INFO_TYPE, + "handle", cpy, NULL)); + } + + g_value_set_object(val, g_object_ref(self->info)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_info_event_set_property(GObject *obj, guint prop_id, + const GValue *val, + GParamSpec *pspec) +{ + GPIODInfoEvent *self = G_GPIOD_INFO_EVENT_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_INFO_EVENT_PROP_HANDLE: + self->handle = g_value_get_pointer(val); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_info_event_dispose(GObject *obj) +{ + GPIODInfoEvent *self = G_GPIOD_INFO_EVENT_OBJ(obj); + + g_clear_object(&self->info); + + G_OBJECT_CLASS(g_gpiod_info_event_parent_class)->dispose(obj); +} + +static void g_gpiod_info_event_finalize(GObject *obj) +{ + GPIODInfoEvent *self = G_GPIOD_INFO_EVENT_OBJ(obj); + + g_clear_pointer(&self->handle, gpiod_info_event_free); + + G_OBJECT_CLASS(g_gpiod_info_event_parent_class)->finalize(obj); +} + +static void g_gpiod_info_event_class_init(GPIODInfoEventClass *info_event_class) +{ + GObjectClass *class = G_OBJECT_CLASS(info_event_class); + + class->set_property = g_gpiod_info_event_set_property; + class->get_property = g_gpiod_info_event_get_property; + class->dispose = g_gpiod_info_event_dispose; + class->finalize = g_gpiod_info_event_finalize; + + g_object_class_install_property(class, G_GPIOD_INFO_EVENT_PROP_HANDLE, + g_param_spec_pointer("handle", "Handle", + "GPIO info event object.", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); + + g_object_class_install_property(class, + G_GPIOD_INFO_EVENT_PROP_EVENT_TYPE, + g_param_spec_enum("event-type", "Event Type", + "Type of the info event.", + G_GPIOD_INFO_EVENT_TYPE_TYPE, + G_GPIOD_INFO_EVENT_LINE_REQUESTED, + G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_INFO_EVENT_PROP_TIMESTAMP, + g_param_spec_uint64("timestamp-ns", + "Timestamp (in nanoseconds)", + "Timestamp of the info event expressed in nanoseconds.", + 0, G_MAXUINT64, 0, G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_INFO_EVENT_PROP_LINE_INFO, + g_param_spec_object("line-info", "Line Info", + "New line-info snapshot associated with this info event.", + G_GPIOD_LINE_INFO_TYPE, G_PARAM_READABLE)); +} + +static void g_gpiod_info_event_init(GPIODInfoEvent *self) +{ + self->handle = NULL; + self->info = NULL; +} + +GPIODInfoEventType g_gpiod_info_event_get_event_type(GPIODInfoEvent *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "event-type"); +} + +guint64 g_gpiod_info_event_get_timestamp_ns(GPIODInfoEvent *self) +{ + return g_gpiod_get_prop_uint64(G_OBJECT(self), "timestamp-ns"); +} + +GPIODLineInfo *g_gpiod_info_event_get_line_info(GPIODInfoEvent *self) +{ + return G_GPIOD_LINE_INFO_OBJ( + g_gpiod_get_prop_object(G_OBJECT(self), "line-info")); +} diff --git a/bindings/glib/internal.c b/bindings/glib/internal.c new file mode 100644 index 0000000..40192a4 --- /dev/null +++ b/bindings/glib/internal.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include "internal.h" + +#define get_prop(_obj, _prop, _type, _vtype, _get_func) \ + ({ \ + g_auto(GValue) _val = G_VALUE_INIT; \ + _type _ret; \ + g_value_init(&_val, _vtype); \ + g_object_get_property(_obj, _prop, &_val); \ + _ret = _get_func(&_val); \ + _ret; \ + }) + +G_GNUC_INTERNAL const gchar * +g_gpiod_get_prop_string(GObject *obj, const gchar *prop) +{ + return get_prop(obj, prop, const gchar *, G_TYPE_STRING, + g_value_get_string); +} + +G_GNUC_INTERNAL gboolean g_gpiod_get_prop_bool(GObject *obj, const gchar *prop) +{ + return get_prop(obj, prop, gboolean, G_TYPE_BOOLEAN, + g_value_get_boolean); +} + +G_GNUC_INTERNAL gint g_gpiod_get_prop_enum(GObject *obj, const gchar *prop) +{ + return get_prop(obj, prop, gint, G_TYPE_ENUM, g_value_get_enum); +} + +G_GNUC_INTERNAL guint g_gpiod_get_prop_uint(GObject *obj, const gchar *prop) +{ + return get_prop(obj, prop, guint, G_TYPE_UINT, g_value_get_uint); +} + +G_GNUC_INTERNAL guint64 g_gpiod_get_prop_uint64(GObject *obj, const gchar *prop) +{ + return get_prop(obj, prop, guint64, G_TYPE_UINT64, g_value_get_uint64); +} + +G_GNUC_INTERNAL gulong g_gpiod_get_prop_ulong(GObject *obj, const gchar *prop) +{ + return get_prop(obj, prop, gulong, G_TYPE_ULONG, g_value_get_ulong); +} + +G_GNUC_INTERNAL GTimeSpan +g_gpiod_get_prop_timespan(GObject *obj, const gchar *prop) +{ + return get_prop(obj, prop, GTimeSpan, G_TYPE_INT64, g_value_get_int64); +} + +G_GNUC_INTERNAL GObject * +g_gpiod_get_prop_object(GObject *obj, const gchar *prop) +{ + return G_OBJECT(get_prop(obj, prop, gpointer, G_TYPE_OBJECT, + g_value_get_object)); +} + +G_GNUC_INTERNAL gpointer +g_gpiod_get_prop_pointer(GObject *obj, const gchar *prop) +{ + return get_prop(obj, prop, gpointer, G_TYPE_POINTER, + g_value_get_pointer); +} + +G_GNUC_INTERNAL gpointer +g_gpiod_get_prop_boxed_array(GObject *obj, const gchar *prop) +{ + return get_prop(obj, prop, gpointer, G_TYPE_ARRAY, g_value_get_boxed); +} + +#define set_prop(_obj, _prop, _set_func, _vtype, _val) \ + do { \ + g_auto(GValue) _gval = G_VALUE_INIT; \ + g_value_init(&_gval, _vtype); \ + _set_func(&_gval, _val); \ + g_object_set_property(_obj, _prop, &_gval); \ + } while (0) + +G_GNUC_INTERNAL void +g_gpiod_set_prop_uint(GObject *obj, const gchar *prop, guint val) +{ + set_prop(obj, prop, g_value_set_uint, G_TYPE_UINT, val); +} + +G_GNUC_INTERNAL void +g_gpiod_set_prop_string(GObject *obj, const gchar *prop, const gchar *val) +{ + set_prop(obj, prop, g_value_set_string, G_TYPE_STRING, val); +} + +G_GNUC_INTERNAL void +g_gpiod_set_prop_enum(GObject *obj, const gchar *prop, gint val) +{ + set_prop(obj, prop, g_value_set_enum, G_TYPE_ENUM, val); +} + +G_GNUC_INTERNAL void +g_gpiod_set_prop_bool(GObject *obj, const gchar *prop, gboolean val) +{ + set_prop(obj, prop, g_value_set_boolean, G_TYPE_BOOLEAN, val); +} + +G_GNUC_INTERNAL void +g_gpiod_set_prop_timespan(GObject *obj, const gchar *prop, GTimeSpan val) +{ + set_prop(obj, prop, g_value_set_int64, G_TYPE_INT64, val); +} + +G_GNUC_INTERNAL GPIODLineDirection +g_gpiod_line_direction_from_library(enum gpiod_line_direction direction, + gboolean allow_as_is) +{ + switch (direction) { + case GPIOD_LINE_DIRECTION_AS_IS: + if (allow_as_is) + return G_GPIOD_LINE_DIRECTION_AS_IS; + break; + case GPIOD_LINE_DIRECTION_INPUT: + return G_GPIOD_LINE_DIRECTION_INPUT; + case GPIOD_LINE_DIRECTION_OUTPUT: + return G_GPIOD_LINE_DIRECTION_OUTPUT; + } + + g_error("invalid line direction value returned by libgpiod"); +} + +G_GNUC_INTERNAL GPIODLineEdge +g_gpiod_line_edge_from_library(enum gpiod_line_edge edge) +{ + switch (edge) { + case GPIOD_LINE_EDGE_NONE: + return G_GPIOD_LINE_EDGE_NONE; + case GPIOD_LINE_EDGE_RISING: + return G_GPIOD_LINE_EDGE_RISING; + case GPIOD_LINE_EDGE_FALLING: + return G_GPIOD_LINE_EDGE_FALLING; + case GPIOD_LINE_EDGE_BOTH: + return G_GPIOD_LINE_EDGE_BOTH; + } + + g_error("invalid line edge value returned by libgpiod"); +} + +G_GNUC_INTERNAL GPIODLineBias +g_gpiod_line_bias_from_library(enum gpiod_line_bias bias, gboolean allow_as_is) +{ + switch (bias) { + case GPIOD_LINE_BIAS_AS_IS: + if (allow_as_is) + return G_GPIOD_LINE_BIAS_AS_IS; + break; + case GPIOD_LINE_BIAS_UNKNOWN: + return G_GPIOD_LINE_BIAS_UNKNOWN; + case GPIOD_LINE_BIAS_DISABLED: + return G_GPIOD_LINE_BIAS_DISABLED; + case GPIOD_LINE_BIAS_PULL_UP: + return G_GPIOD_LINE_BIAS_PULL_UP; + case GPIOD_LINE_BIAS_PULL_DOWN: + return G_GPIOD_LINE_BIAS_PULL_DOWN; + } + + g_error("invalid line bias value returned by libgpiod"); +} + +G_GNUC_INTERNAL GPIODLineDrive +g_gpiod_line_drive_from_library(enum gpiod_line_drive drive) +{ + switch (drive) { + case GPIOD_LINE_DRIVE_PUSH_PULL: + return G_GPIOD_LINE_DRIVE_PUSH_PULL; + case GPIOD_LINE_DRIVE_OPEN_DRAIN: + return G_GPIOD_LINE_DRIVE_OPEN_DRAIN; + case GPIOD_LINE_DRIVE_OPEN_SOURCE: + return G_GPIOD_LINE_DRIVE_OPEN_SOURCE; + } + + g_error("invalid line drive value returned by libgpiod"); +} + +G_GNUC_INTERNAL GPIODLineClock +g_gpiod_line_clock_from_library(enum gpiod_line_clock event_clock) +{ + switch (event_clock) { + case GPIOD_LINE_CLOCK_MONOTONIC: + return G_GPIOD_LINE_CLOCK_MONOTONIC; + case GPIOD_LINE_CLOCK_REALTIME: + return G_GPIOD_LINE_CLOCK_REALTIME; + case GPIOD_LINE_CLOCK_HTE: + return G_GPIOD_LINE_CLOCK_HTE; + } + + g_error("invalid line event clock value returned by libgpiod"); +} + +G_GNUC_INTERNAL GPIODLineValue +g_gpiod_line_value_from_library(enum gpiod_line_value value) +{ + switch (value) { + case GPIOD_LINE_VALUE_INACTIVE: + return G_GPIOD_LINE_VALUE_INACTIVE; + case GPIOD_LINE_VALUE_ACTIVE: + return G_GPIOD_LINE_VALUE_ACTIVE; + default: + break; + } + + g_error("invalid line value returned by libgpiod"); +} + +G_GNUC_INTERNAL GPIODInfoEventType +g_gpiod_info_event_type_from_library(enum gpiod_info_event_type type) +{ + switch (type) { + case GPIOD_INFO_EVENT_LINE_REQUESTED: + return G_GPIOD_INFO_EVENT_LINE_REQUESTED; + case GPIOD_INFO_EVENT_LINE_RELEASED: + return G_GPIOD_INFO_EVENT_LINE_RELEASED; + case GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED: + return G_GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED; + } + + g_error("invalid info-event type returned by libgpiod"); +} + +G_GNUC_INTERNAL GPIODEdgeEventType +g_gpiod_edge_event_type_from_library(enum gpiod_edge_event_type type) +{ + switch (type) { + case GPIOD_EDGE_EVENT_RISING_EDGE: + return G_GPIOD_EDGE_EVENT_RISING_EDGE; + case GPIOD_EDGE_EVENT_FALLING_EDGE: + return G_GPIOD_EDGE_EVENT_FALLING_EDGE; + } + + g_error("invalid edge-event type returned by libgpiod"); +} + +G_GNUC_INTERNAL enum gpiod_line_direction +g_gpiod_line_direction_to_library(GPIODLineDirection direction) +{ + switch (direction) { + case G_GPIOD_LINE_DIRECTION_AS_IS: + return GPIOD_LINE_DIRECTION_AS_IS; + case G_GPIOD_LINE_DIRECTION_INPUT: + return GPIOD_LINE_DIRECTION_INPUT; + case G_GPIOD_LINE_DIRECTION_OUTPUT: + return GPIOD_LINE_DIRECTION_OUTPUT; + } + + g_error("invalid line direction value"); +} + +G_GNUC_INTERNAL enum gpiod_line_edge +g_gpiod_line_edge_to_library(GPIODLineEdge edge) +{ + switch (edge) { + case G_GPIOD_LINE_EDGE_NONE: + return GPIOD_LINE_EDGE_NONE; + case G_GPIOD_LINE_EDGE_RISING: + return GPIOD_LINE_EDGE_RISING; + case G_GPIOD_LINE_EDGE_FALLING: + return GPIOD_LINE_EDGE_FALLING; + case G_GPIOD_LINE_EDGE_BOTH: + return GPIOD_LINE_EDGE_BOTH; + } + + g_error("invalid line edge value"); +} + +G_GNUC_INTERNAL enum gpiod_line_bias +g_gpiod_line_bias_to_library(GPIODLineBias bias) +{ + switch (bias) { + case G_GPIOD_LINE_BIAS_AS_IS: + return GPIOD_LINE_BIAS_AS_IS; + case G_GPIOD_LINE_BIAS_DISABLED: + return GPIOD_LINE_BIAS_DISABLED; + case G_GPIOD_LINE_BIAS_PULL_UP: + return GPIOD_LINE_BIAS_PULL_UP; + case G_GPIOD_LINE_BIAS_PULL_DOWN: + return GPIOD_LINE_BIAS_PULL_DOWN; + default: + break; + } + + g_error("invalid line bias value"); +} + +G_GNUC_INTERNAL enum gpiod_line_drive +g_gpiod_line_drive_to_library(GPIODLineDrive drive) +{ + switch (drive) { + case G_GPIOD_LINE_DRIVE_PUSH_PULL: + return GPIOD_LINE_DRIVE_PUSH_PULL; + case G_GPIOD_LINE_DRIVE_OPEN_SOURCE: + return GPIOD_LINE_DRIVE_OPEN_SOURCE; + case G_GPIOD_LINE_DRIVE_OPEN_DRAIN: + return GPIOD_LINE_DRIVE_OPEN_DRAIN; + } + + g_error("invalid line drive value"); +} + +G_GNUC_INTERNAL enum gpiod_line_clock +g_gpiod_line_clock_to_library(GPIODLineClock event_clock) +{ + switch (event_clock) { + case G_GPIOD_LINE_CLOCK_MONOTONIC: + return GPIOD_LINE_CLOCK_MONOTONIC; + case G_GPIOD_LINE_CLOCK_REALTIME: + return GPIOD_LINE_CLOCK_REALTIME; + case G_GPIOD_LINE_CLOCK_HTE: + return GPIOD_LINE_CLOCK_HTE; + } + + g_error("invalid line clock value"); +} + +G_GNUC_INTERNAL enum gpiod_line_value +g_gpiod_line_value_to_library(GPIODLineValue value) +{ + switch (value) { + case G_GPIOD_LINE_VALUE_INACTIVE: + return GPIOD_LINE_VALUE_INACTIVE; + case G_GPIOD_LINE_VALUE_ACTIVE: + return GPIOD_LINE_VALUE_ACTIVE; + } + + g_error("invalid line value"); +} diff --git a/bindings/glib/internal.h b/bindings/glib/internal.h new file mode 100644 index 0000000..a1fa516 --- /dev/null +++ b/bindings/glib/internal.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +#ifndef __GPIOD_GLIB_INTERNAL_H__ +#define __GPIOD_GLIB_INTERNAL_H__ + +#include +#include +#include +#include + +void g_gpiod_set_error_from_errno(GError **err, const gchar *fmt, ...); + +const gchar *g_gpiod_get_prop_string(GObject *obj, const gchar *prop); +gboolean g_gpiod_get_prop_bool(GObject *obj, const gchar *prop); +gint g_gpiod_get_prop_enum(GObject *obj, const gchar *prop); +guint g_gpiod_get_prop_uint(GObject *obj, const gchar *prop); +guint64 g_gpiod_get_prop_uint64(GObject *obj, const gchar *prop); +gulong g_gpiod_get_prop_ulong(GObject *obj, const gchar *prop); +GTimeSpan g_gpiod_get_prop_timespan(GObject *obj, const gchar *prop); +GObject *g_gpiod_get_prop_object(GObject *obj, const gchar *prop); +gpointer g_gpiod_get_prop_pointer(GObject *obj, const gchar *prop); +gpointer g_gpiod_get_prop_boxed_array(GObject *obj, const gchar *prop); + +void g_gpiod_set_prop_uint(GObject *obj, const gchar *prop, guint val); +void g_gpiod_set_prop_string(GObject *obj, const gchar *prop, const gchar *val); +void g_gpiod_set_prop_enum(GObject *obj, const gchar *prop, gint val); +void g_gpiod_set_prop_bool(GObject *obj, const gchar *prop, gboolean val); +void g_gpiod_set_prop_timespan(GObject *obj, const gchar *prop, GTimeSpan val); + +GPIODLineDirection +g_gpiod_line_direction_from_library(enum gpiod_line_direction direction, + gboolean allow_as_is); +GPIODLineEdge g_gpiod_line_edge_from_library(enum gpiod_line_edge edge); +GPIODLineBias g_gpiod_line_bias_from_library(enum gpiod_line_bias bias, + gboolean allow_as_is); +GPIODLineDrive g_gpiod_line_drive_from_library(enum gpiod_line_drive drive); +GPIODLineClock +g_gpiod_line_clock_from_library(enum gpiod_line_clock event_clock); +GPIODLineValue g_gpiod_line_value_from_library(enum gpiod_line_value value); +GPIODInfoEventType +g_gpiod_info_event_type_from_library(enum gpiod_info_event_type type); +GPIODEdgeEventType +g_gpiod_edge_event_type_from_library(enum gpiod_edge_event_type type); + +enum gpiod_line_direction +g_gpiod_line_direction_to_library(GPIODLineDirection direction); +enum gpiod_line_edge g_gpiod_line_edge_to_library(GPIODLineEdge edge); +enum gpiod_line_bias g_gpiod_line_bias_to_library(GPIODLineBias bias); +enum gpiod_line_drive g_gpiod_line_drive_to_library(GPIODLineDrive drive); +enum gpiod_line_clock g_gpiod_line_clock_to_library(GPIODLineClock event_clock); +enum gpiod_line_value g_gpiod_line_value_to_library(GPIODLineValue value); + +#endif /* __GPIOD_GLIB_INTERNAL_H__ */ diff --git a/bindings/glib/line-config.c b/bindings/glib/line-config.c new file mode 100644 index 0000000..4fc1585 --- /dev/null +++ b/bindings/glib/line-config.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include + +#include "internal.h" + +struct _GPIODLineConfig { + GObject parent_instance; + struct gpiod_line_config *handle; +}; + +enum { + G_GPIOD_LINE_CONFIG_PROP_HANDLE = 1, + G_GPIOD_LINE_CONFIG_PROP_CONFIGURED_OFFSETS, +}; + +G_DEFINE_TYPE(GPIODLineConfig, g_gpiod_line_config, G_TYPE_OBJECT); + +static void g_gpiod_line_config_get_property(GObject *obj, guint prop_id, + GValue *val, GParamSpec *pspec) +{ + GPIODLineConfig *self = G_GPIOD_LINE_CONFIG_OBJ(obj); + g_autofree guint *offsets = NULL; + gsize num_offsets, i; + GArray *boxed; + + switch (prop_id) { + case G_GPIOD_LINE_CONFIG_PROP_HANDLE: + g_value_set_pointer(val, self->handle); + break; + case G_GPIOD_LINE_CONFIG_PROP_CONFIGURED_OFFSETS: + num_offsets = gpiod_line_config_get_num_configured_offsets( + self->handle); + offsets = g_malloc0(num_offsets * sizeof(guint)); + gpiod_line_config_get_configured_offsets(self->handle, offsets, + num_offsets); + + boxed = g_array_new(FALSE, TRUE, sizeof(guint)); + for (i = 0; i < num_offsets; i++) + g_array_append_val(boxed, offsets[i]); + + g_value_set_boxed(val, boxed); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_line_config_finalize(GObject *obj) +{ + GPIODLineConfig *self = G_GPIOD_LINE_CONFIG_OBJ(obj); + + g_clear_pointer(&self->handle, gpiod_line_config_free); + + G_OBJECT_CLASS(g_gpiod_line_config_parent_class)->finalize(obj); +} + +static void +g_gpiod_line_config_class_init(GPIODLineConfigClass *line_config_class) +{ + GObjectClass *class = G_OBJECT_CLASS(line_config_class); + + class->get_property = g_gpiod_line_config_get_property; + class->finalize = g_gpiod_line_config_finalize; + + g_object_class_install_property(class, G_GPIOD_LINE_CONFIG_PROP_HANDLE, + g_param_spec_pointer("handle", "Handle", + "GPIO line config object.", + G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_CONFIG_PROP_CONFIGURED_OFFSETS, + g_param_spec_boxed("configured-offsets", "Configured Offsets", + "Array of offsets for which line settings have been set.", + G_TYPE_ARRAY, + G_PARAM_READABLE)); +} + +static void g_gpiod_line_config_init(GPIODLineConfig *self) +{ + self->handle = gpiod_line_config_new(); + if (!self->handle) + /* The only possible error is ENOMEM. */ + g_error("Failed to allocate memory for the request-config object."); +} + +GPIODLineConfig *g_gpiod_line_config_new(void) +{ + return G_GPIOD_LINE_CONFIG_OBJ( + g_object_new(G_GPIOD_LINE_CONFIG_TYPE, NULL)); +} + +void g_gpiod_line_config_reset(GPIODLineConfig *self) +{ + g_assert(self); + + gpiod_line_config_reset(self->handle); +} + +gboolean g_gpiod_line_config_add_line_settings(GPIODLineConfig *self, + const GArray *offsets, + GPIODLineSettings *settings, + GError **err) +{ + struct gpiod_line_settings *settings_handle; + int ret; + + g_assert(self); + + if (!offsets || !offsets->len) { + g_set_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL, + "at least one offset must be specified when adding line settings"); + return FALSE; + } + + settings_handle = settings ? + g_gpiod_get_prop_pointer(G_OBJECT(settings), "handle") : NULL; + ret = gpiod_line_config_add_line_settings(self->handle, + (unsigned int *)offsets->data, + offsets->len, + settings_handle); + if (ret) { + g_gpiod_set_error_from_errno(err, + "failed to add line settings to line config"); + return FALSE; + } + + return TRUE; +} + +GPIODLineSettings * +g_gpiod_line_config_get_line_settings(GPIODLineConfig *self, guint offset) +{ + struct gpiod_line_settings *settings; + + g_assert(self); + + settings = gpiod_line_config_get_line_settings(self->handle, offset); + if (!settings) { + if (errno == ENOENT) + return NULL; + + /* Let's bail-out on ENOMEM/ */ + g_error("failed to retrieve line settings for offset %u: %s", + offset, g_strerror(errno)); + } + + return G_GPIOD_LINE_SETTINGS_OBJ( + g_object_new(G_GPIOD_LINE_SETTINGS_TYPE, + "handle", settings, NULL)); +} + +gboolean g_gpiod_line_config_set_output_values(GPIODLineConfig *self, + const GArray *values, + GError **err) +{ + g_autofree enum gpiod_line_value *vals = NULL; + gint ret; + guint i; + + g_assert(self); + + vals = g_malloc0(sizeof(*vals) * values->len); + for (i = 0; i < values->len; i++) + vals[i] = g_gpiod_line_value_to_library( + g_array_index(values, GPIODLineValue, i)); + + ret = gpiod_line_config_set_output_values(self->handle, vals, + values->len); + if (ret) { + g_gpiod_set_error_from_errno(err, + "unable to set output values"); + return FALSE; + } + + return TRUE; +} + +GArray *g_gpiod_line_config_get_configured_offsets(GPIODLineConfig *self) +{ + return g_gpiod_get_prop_boxed_array(G_OBJECT(self), + "configured-offsets"); +} diff --git a/bindings/glib/line-info.c b/bindings/glib/line-info.c new file mode 100644 index 0000000..38b332f --- /dev/null +++ b/bindings/glib/line-info.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include + +#include "internal.h" + +struct _GPIODLineInfo { + GObject parent_instance; + struct gpiod_line_info *handle; +}; + +enum { + G_GPIOD_LINE_INFO_PROP_HANDLE = 1, + G_GPIOD_LINE_INFO_PROP_OFFSET, + G_GPIOD_LINE_INFO_PROP_NAME, + G_GPIOD_LINE_INFO_PROP_USED, + G_GPIOD_LINE_INFO_PROP_CONSUMER, + G_GPIOD_LINE_INFO_PROP_DIRECTION, + G_GPIOD_LINE_INFO_PROP_EDGE_DETECTION, + G_GPIOD_LINE_INFO_PROP_BIAS, + G_GPIOD_LINE_INFO_PROP_DRIVE, + G_GPIOD_LINE_INFO_PROP_ACTIVE_LOW, + G_GPIOD_LINE_INFO_PROP_DEBOUNCED, + G_GPIOD_LINE_INFO_PROP_DEBOUNCE_PERIOD, + G_GPIOD_LINE_INFO_PROP_EVENT_CLOCK, +}; + +G_DEFINE_TYPE(GPIODLineInfo, g_gpiod_line_info, G_TYPE_OBJECT); + +static void g_gpiod_line_info_get_property(GObject *obj, guint prop_id, + GValue *val, GParamSpec *pspec) +{ + GPIODLineInfo *self = G_GPIOD_LINE_INFO_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_LINE_INFO_PROP_OFFSET: + g_value_set_uint(val, gpiod_line_info_get_offset(self->handle)); + break; + case G_GPIOD_LINE_INFO_PROP_NAME: + g_value_set_static_string(val, + gpiod_line_info_get_name(self->handle)); + break; + case G_GPIOD_LINE_INFO_PROP_USED: + g_value_set_boolean(val, gpiod_line_info_is_used(self->handle)); + break; + case G_GPIOD_LINE_INFO_PROP_CONSUMER: + g_value_set_static_string(val, + gpiod_line_info_get_consumer(self->handle)); + break; + case G_GPIOD_LINE_INFO_PROP_DIRECTION: + g_value_set_enum(val, + g_gpiod_line_direction_from_library( + gpiod_line_info_get_direction(self->handle), + FALSE)); + break; + case G_GPIOD_LINE_INFO_PROP_EDGE_DETECTION: + g_value_set_enum(val, + g_gpiod_line_edge_from_library( + gpiod_line_info_get_edge_detection( + self->handle))); + break; + case G_GPIOD_LINE_INFO_PROP_BIAS: + g_value_set_enum(val, + g_gpiod_line_bias_from_library( + gpiod_line_info_get_bias(self->handle), + FALSE)); + break; + case G_GPIOD_LINE_INFO_PROP_DRIVE: + g_value_set_enum(val, + g_gpiod_line_drive_from_library( + gpiod_line_info_get_drive(self->handle))); + break; + case G_GPIOD_LINE_INFO_PROP_ACTIVE_LOW: + g_value_set_boolean(val, + gpiod_line_info_is_active_low(self->handle)); + break; + case G_GPIOD_LINE_INFO_PROP_DEBOUNCED: + g_value_set_boolean(val, + gpiod_line_info_is_debounced(self->handle)); + break; + case G_GPIOD_LINE_INFO_PROP_DEBOUNCE_PERIOD: + g_value_set_int64(val, + gpiod_line_info_get_debounce_period_us(self->handle)); + break; + case G_GPIOD_LINE_INFO_PROP_EVENT_CLOCK: + g_value_set_enum(val, + g_gpiod_line_clock_from_library( + gpiod_line_info_get_event_clock(self->handle))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_line_info_set_property(GObject *obj, guint prop_id, + const GValue *val, GParamSpec *pspec) +{ + GPIODLineInfo *self = G_GPIOD_LINE_INFO_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_LINE_INFO_PROP_HANDLE: + self->handle = g_value_get_pointer(val); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_line_info_finalize(GObject *obj) +{ + GPIODLineInfo *self = G_GPIOD_LINE_INFO_OBJ(obj); + + g_clear_pointer(&self->handle, gpiod_line_info_free); + + G_OBJECT_CLASS(g_gpiod_line_info_parent_class)->finalize(obj); +} + +static void g_gpiod_line_info_class_init(GPIODLineInfoClass *line_info_class) +{ + GObjectClass *class = G_OBJECT_CLASS(line_info_class); + + class->set_property = g_gpiod_line_info_set_property; + class->get_property = g_gpiod_line_info_get_property; + class->finalize = g_gpiod_line_info_finalize; + + g_object_class_install_property(class, G_GPIOD_LINE_INFO_PROP_HANDLE, + g_param_spec_pointer("handle", "Handle", + "GPIO line information object.", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); + + g_object_class_install_property(class, G_GPIOD_LINE_INFO_PROP_OFFSET, + g_param_spec_uint("offset", "Offset", + "Offset of the GPIO line.", + 0, G_MAXUINT, 0, G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOD_LINE_INFO_PROP_NAME, + g_param_spec_string("name", "Name", + "Name of the GPIO line, if named.", + NULL, G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOD_LINE_INFO_PROP_USED, + g_param_spec_boolean("used", "Is Used", + "Indicates whether the GPIO line is requested for exclusive usage", + FALSE, G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOD_LINE_INFO_PROP_CONSUMER, + g_param_spec_string("consumer", "Consumer", + "Name of the consumer of the GPIO line, if requested.", + NULL, G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOD_LINE_INFO_PROP_DIRECTION, + g_param_spec_enum("direction", "Direction", + "Direction of the GPIO line.", + G_GPIOD_LINE_DIRECTION_TYPE, + G_GPIOD_LINE_DIRECTION_INPUT, + G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_INFO_PROP_EDGE_DETECTION, + g_param_spec_enum("edge-detection", "Edge Detection", + "Edge detection setting of the GPIO line.", + G_GPIOD_LINE_EDGE_TYPE, + G_GPIOD_LINE_EDGE_NONE, + G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOD_LINE_INFO_PROP_BIAS, + g_param_spec_enum("bias", "Bias", + "Bias setting of the GPIO line.", + G_GPIOD_LINE_BIAS_TYPE, + G_GPIOD_LINE_BIAS_UNKNOWN, + G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOD_LINE_INFO_PROP_DRIVE, + g_param_spec_enum("drive", "Drive", + "Drive setting of the GPIO line.", + G_GPIOD_LINE_DRIVE_TYPE, + G_GPIOD_LINE_DRIVE_PUSH_PULL, + G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_INFO_PROP_ACTIVE_LOW, + g_param_spec_boolean("active-low", "Is Active-Low", + "Indicates whether the signal of the line is inverted.", + FALSE, G_PARAM_READABLE)); + + g_object_class_install_property(class, G_GPIOD_LINE_INFO_PROP_DEBOUNCED, + g_param_spec_boolean("debounced", "Is Debounced", + "Indicates whether the line is debounced (by hardware or by the kernel software debouncer).", + FALSE, G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_INFO_PROP_DEBOUNCE_PERIOD, + g_param_spec_int64("debounce-period-us", + "Debounce Period (in microseconds)", + "Debounce period of the line (expressed in microseconds).", + 0, G_MAXINT64, 0, + G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_INFO_PROP_EVENT_CLOCK, + g_param_spec_enum("event-clock", "Event Clock", + "Event clock used to timestamp the edge events of the line.", + G_GPIOD_LINE_CLOCK_TYPE, + G_GPIOD_LINE_CLOCK_MONOTONIC, + G_PARAM_READABLE)); +} + +static void g_gpiod_line_info_init(GPIODLineInfo *self) +{ + self->handle = NULL; +} + +guint g_gpiod_line_info_get_offset(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_uint(G_OBJECT(self), "offset"); +} + +const gchar *g_gpiod_line_info_get_name(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_string(G_OBJECT(self), "name"); +} + +gboolean g_gpiod_line_info_is_used(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_bool(G_OBJECT(self), "used"); +} + +const gchar *g_gpiod_line_info_get_consumer(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_string(G_OBJECT(self), "consumer"); +} + +GPIODLineDirection g_gpiod_line_info_get_direction(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "direction"); +} + +GPIODLineEdge g_gpiod_line_info_get_edge_detection(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "edge-detection"); +} + +GPIODLineBias g_gpiod_line_info_get_bias(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "bias"); +} + +GPIODLineDrive g_gpiod_line_info_get_drive(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "drive"); +} + +gboolean g_gpiod_line_info_is_active_low(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_bool(G_OBJECT(self), "active-low"); +} + +gboolean g_gpiod_line_info_is_debounced(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_bool(G_OBJECT(self), "debounced"); +} + +GTimeSpan g_gpiod_line_info_get_debounce_period_us(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_timespan(G_OBJECT(self), "debounce-period-us"); +} + +GPIODLineClock g_gpiod_line_info_get_event_clock(GPIODLineInfo *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "event-clock"); +} diff --git a/bindings/glib/line-request.c b/bindings/glib/line-request.c new file mode 100644 index 0000000..d26dd9c --- /dev/null +++ b/bindings/glib/line-request.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include + +#include "internal.h" + +static const gsize event_buf_size = 64; + +struct _GPIODLineRequest { + GObject parent_instance; + struct gpiod_line_request *handle; + struct gpiod_edge_event_buffer *event_buf; + GSource *edge_event_src; + guint edge_event_src_id; + enum gpiod_line_value *val_buf; +}; + +enum { + G_GPIOD_LINE_REQUEST_PROP_HANDLE = 1, + G_GPIOD_LINE_REQUEST_PROP_CHIP_NAME, + G_GPIOD_LINE_REQUEST_PROP_REQUESTED_OFFSETS, +}; + +enum { + G_GPIOD_LINE_REQUEST_SIGNAL_EDGE_EVENT, + G_GPIOD_LINE_REQUEST_SIGNAL_LAST, +}; + +static guint signals[G_GPIOD_LINE_REQUEST_SIGNAL_LAST]; + +G_DEFINE_TYPE(GPIODLineRequest, g_gpiod_line_request, G_TYPE_OBJECT); + +static gboolean +g_gpiod_line_request_on_edge_event(GIOChannel *source G_GNUC_UNUSED, + GIOCondition condition G_GNUC_UNUSED, + gpointer data) +{ + struct gpiod_edge_event *event_handle, *event_copy; + GPIODLineRequest *self = data; + gint ret, i; + + ret = gpiod_line_request_read_edge_events(self->handle, + self->event_buf, + event_buf_size); + if (ret < 0) + return TRUE; + + for (i = 0; i < ret; i++) { + g_autoptr(GPIODEdgeEvent) event = NULL; + + event_handle = gpiod_edge_event_buffer_get_event( + self->event_buf, i); + event_copy = gpiod_edge_event_copy(event_handle); + if (!event_copy) + g_error("failed to copy the edge event"); + + event = G_GPIOD_EDGE_EVENT_OBJ( + g_object_new(G_GPIOD_EDGE_EVENT_TYPE, + "handle", event_copy, NULL)); + + g_signal_emit(self, + signals[G_GPIOD_LINE_REQUEST_SIGNAL_EDGE_EVENT], + 0, + event); + } + + return TRUE; +} + +static void g_gpiod_line_request_constructed(GObject *obj) +{ + GPIODLineRequest *self = G_GPIOD_LINE_REQUEST_OBJ(obj); + g_autoptr(GIOChannel) channel = NULL; + gsize num_lines; + + self->event_buf = gpiod_edge_event_buffer_new(event_buf_size); + if (!self->event_buf) + g_error("failed to allocate the edge event buffer"); + + channel = g_io_channel_unix_new( + gpiod_line_request_get_fd(self->handle)); + self->edge_event_src = g_io_create_watch(channel, G_IO_IN); + g_source_set_callback(self->edge_event_src, + G_SOURCE_FUNC(g_gpiod_line_request_on_edge_event), + self, NULL); + self->edge_event_src_id = g_source_attach(self->edge_event_src, NULL); + + num_lines = gpiod_line_request_get_num_requested_lines(self->handle); + self->val_buf = g_malloc0(sizeof(enum gpiod_line_value) * num_lines); + + G_OBJECT_CLASS(g_gpiod_line_request_parent_class)->constructed(obj); +} + +static void g_gpiod_line_request_get_property(GObject *obj, guint prop_id, + GValue *val, GParamSpec *pspec) +{ + GPIODLineRequest *self = G_GPIOD_LINE_REQUEST_OBJ(obj); + g_autofree guint *offsets = NULL; + gsize num_offsets; + GArray *boxed; + + switch (prop_id) { + case G_GPIOD_LINE_REQUEST_PROP_CHIP_NAME: + g_value_set_static_string(val, + gpiod_line_request_get_chip_name(self->handle)); + break; + case G_GPIOD_LINE_REQUEST_PROP_REQUESTED_OFFSETS: + boxed = g_array_new(FALSE, TRUE, sizeof(guint)); + + if (!g_gpiod_line_request_is_released(self)) { + num_offsets = + gpiod_line_request_get_num_requested_lines( + self->handle); + offsets = g_malloc0(num_offsets * sizeof(guint)); + gpiod_line_request_get_requested_offsets(self->handle, + offsets, + num_offsets); + g_array_append_vals(boxed, offsets, num_offsets); + } + + g_value_set_boxed(val, boxed); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_line_request_set_property(GObject *obj, guint prop_id, + const GValue *val, GParamSpec *pspec) +{ + GPIODLineRequest *self = G_GPIOD_LINE_REQUEST_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_LINE_REQUEST_PROP_HANDLE: + self->handle = g_value_get_pointer(val); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_line_request_dispose(GObject *obj) +{ + GPIODLineRequest *self = G_GPIOD_LINE_REQUEST_OBJ(obj); + + if (self->edge_event_src_id) + g_source_remove(self->edge_event_src_id); + + G_OBJECT_CLASS(g_gpiod_line_request_parent_class)->dispose(obj); +} + +static void g_gpiod_line_request_finalize(GObject *obj) +{ + GPIODLineRequest *self = G_GPIOD_LINE_REQUEST_OBJ(obj); + + g_gpiod_line_request_release(self); + g_clear_pointer(&self->event_buf, gpiod_edge_event_buffer_free); + g_clear_pointer(&self->val_buf, g_free); + + G_OBJECT_CLASS(g_gpiod_line_request_parent_class)->finalize(obj); +} + +static void g_gpiod_line_request_class_init(GPIODLineRequestClass *line_request_class) +{ + GObjectClass *class = G_OBJECT_CLASS(line_request_class); + + class->constructed = g_gpiod_line_request_constructed; + class->set_property = g_gpiod_line_request_set_property; + class->get_property = g_gpiod_line_request_get_property; + class->dispose = g_gpiod_line_request_dispose; + class->finalize = g_gpiod_line_request_finalize; + + g_object_class_install_property(class, G_GPIOD_LINE_REQUEST_PROP_HANDLE, + g_param_spec_pointer("handle", "Handle", + "GPIO line request object.", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_REQUEST_PROP_CHIP_NAME, + g_param_spec_string("chip-name", "Chip Name", + "Name of the GPIO chip this request was made on.", + NULL, G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_REQUEST_PROP_REQUESTED_OFFSETS, + g_param_spec_boxed("requested-offsets", "Requested offsets", + "Array of requested offsets.", + G_TYPE_ARRAY, + G_PARAM_READABLE)); + + signals[G_GPIOD_LINE_REQUEST_SIGNAL_EDGE_EVENT] = + g_signal_new("edge-event", + G_TYPE_FROM_CLASS(line_request_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 1, + G_GPIOD_EDGE_EVENT_TYPE); +} + +static void g_gpiod_line_request_init(GPIODLineRequest *self) +{ + self->handle = NULL; + self->event_buf = NULL; + self->edge_event_src = NULL; +} + +void g_gpiod_line_request_release(GPIODLineRequest *self) +{ + g_assert(self); + + g_clear_pointer(&self->edge_event_src, g_source_unref); + g_clear_pointer(&self->handle, gpiod_line_request_release); +} + +gboolean g_gpiod_line_request_is_released(GPIODLineRequest *self) +{ + g_assert(self); + + return !self->handle; +} + +static void set_err_request_released(GError **err) +{ + g_set_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_REQUEST_RELEASED, + "line request was released and cannot be used"); +} + +const gchar *g_gpiod_line_request_get_chip_name(GPIODLineRequest *self) +{ + return g_gpiod_get_prop_string(G_OBJECT(self), "chip-name"); +} + +GArray *g_gpiod_line_request_get_requested_offsets(GPIODLineRequest *self) +{ + return g_gpiod_get_prop_boxed_array(G_OBJECT(self), + "requested-offsets"); +} + +gboolean g_gpiod_line_request_reconfigure_lines(GPIODLineRequest *self, + GPIODLineConfig *config, + GError **err) +{ + struct gpiod_line_config *config_handle; + gint ret; + + g_assert(self); + + if (g_gpiod_line_request_is_released(self)) { + set_err_request_released(err); + return FALSE; + } + + if (!config) { + g_set_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL, + "line-config is required to reconfigure lines"); + return FALSE; + } + + config_handle = g_gpiod_get_prop_pointer(G_OBJECT(config), "handle"); + + ret = gpiod_line_request_reconfigure_lines(self->handle, config_handle); + if (ret) { + g_gpiod_set_error_from_errno(err, + "failed to reconfigure lines"); + return FALSE; + } + + return TRUE; +} + +gboolean +g_gpiod_line_request_get_value(GPIODLineRequest *self, guint offset, + GPIODLineValue *value, GError **err) +{ + enum gpiod_line_value val; + + g_assert(self); + + if (g_gpiod_line_request_is_released(self)) { + set_err_request_released(err); + return FALSE; + } + + val = gpiod_line_request_get_value(self->handle, offset); + if (val == GPIOD_LINE_VALUE_ERROR) { + g_gpiod_set_error_from_errno(err, + "failed to get line value for offset %u", offset); + return FALSE; + } + + *value = g_gpiod_line_value_from_library(val); + return TRUE; +} + +gboolean g_gpiod_line_request_get_values_subset(GPIODLineRequest *self, + const GArray *offsets, + GArray **values, + GError **err) +{ + guint i; + int ret; + + g_assert(self); + + if (g_gpiod_line_request_is_released(self)) { + set_err_request_released(err); + return FALSE; + } + + if (!offsets || !values) { + g_set_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL, + "offsets and values must not be NULL"); + return FALSE; + } + + ret = gpiod_line_request_get_values_subset(self->handle, offsets->len, + (const unsigned int *)offsets->data, + self->val_buf); + if (ret) { + g_gpiod_set_error_from_errno(err, "failed to read line values"); + return FALSE; + } + + if (!(*values)) { + *values = g_array_sized_new(FALSE, TRUE, + sizeof(GPIODLineValue), + offsets->len); + } + + g_array_set_size(*values, offsets->len); + + for (i = 0; i < offsets->len; i++) { + GPIODLineValue *val = &g_array_index(*values, GPIODLineValue, i); + *val = g_gpiod_line_value_from_library(self->val_buf[i]); + } + + return TRUE; +} + +gboolean g_gpiod_line_request_get_values(GPIODLineRequest *self, + GArray **values, GError **err) +{ + g_autoptr(GArray) offsets = NULL; + + offsets = g_gpiod_line_request_get_requested_offsets(self); + + return g_gpiod_line_request_get_values_subset(self, offsets, + values, err); +} + +gboolean g_gpiod_line_request_set_value(GPIODLineRequest *self, guint offset, + GPIODLineValue value, GError **err) +{ + int ret; + + g_assert(self); + + if (g_gpiod_line_request_is_released(self)) { + set_err_request_released(err); + return FALSE; + } + + ret = gpiod_line_request_set_value(self->handle, offset, + g_gpiod_line_value_to_library(value)); + if (ret) { + g_gpiod_set_error_from_errno(err, + "failed to set line value for offset: %u", offset); + return FALSE; + } + + return TRUE; +} + +gboolean g_gpiod_line_request_set_values_subset(GPIODLineRequest *self, + const GArray *offsets, + const GArray *values, + GError **err) +{ + guint i; + int ret; + + g_assert(self); + + if (g_gpiod_line_request_is_released(self)) { + set_err_request_released(err); + return FALSE; + } + + if (!offsets || !values) { + g_set_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL, + "offsets and values must not be NULL"); + return FALSE; + } + + if (offsets->len != values->len) { + g_set_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL, + "offsets and values must have the sme size"); + return FALSE; + } + + for (i = 0; i < values->len; i++) + self->val_buf[i] = g_gpiod_line_value_to_library( + g_array_index(values, + GPIODLineValue, i)); + + ret = gpiod_line_request_set_values_subset(self->handle, + offsets->len, + (unsigned int *)offsets->data, + self->val_buf); + if (ret) { + g_gpiod_set_error_from_errno(err, "failed to set line values"); + return FALSE; + } + + return TRUE; +} + +gboolean g_gpiod_line_request_set_values(GPIODLineRequest *self, + GArray *values, GError **err) +{ + g_autoptr(GArray) offsets = NULL; + + offsets = g_gpiod_line_request_get_requested_offsets(self); + + return g_gpiod_line_request_set_values_subset(self, offsets, + values, err); +} diff --git a/bindings/glib/line-settings.c b/bindings/glib/line-settings.c new file mode 100644 index 0000000..612a17e --- /dev/null +++ b/bindings/glib/line-settings.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include +#include + +#include "internal.h" + +struct _GPIODLineSettings { + GObject parent_instance; + struct gpiod_line_settings *handle; +}; + +enum { + G_GPIOD_LINE_SETTINGS_PROP_HANDLE = 1, + G_GPIOD_LINE_SETTINGS_PROP_DIRECTION, + G_GPIOD_LINE_SETTINGS_PROP_EDGE_DETECTION, + G_GPIOD_LINE_SETTINGS_PROP_BIAS, + G_GPIOD_LINE_SETTINGS_PROP_DRIVE, + G_GPIOD_LINE_SETTINGS_PROP_ACTIVE_LOW, + G_GPIOD_LINE_SETTINGS_PROP_DEBOUNCE_PERIOD_US, + G_GPIOD_LINE_SETTINGS_PROP_EVENT_CLOCK, + G_GPIOD_LINE_SETTINGS_PROP_OUTPUT_VALUE, +}; + +G_DEFINE_TYPE(GPIODLineSettings, g_gpiod_line_settings, G_TYPE_OBJECT); + +static void g_gpiod_line_settings_constructed(GObject *obj) +{ + GPIODLineSettings *self = G_GPIOD_LINE_SETTINGS_OBJ(obj); + + /* + * If we still haven't created the handle at this point, do it now. + * This is normal if called did g_gpiod_line_settings_new(NULL). + */ + if (!self->handle) { + self->handle = gpiod_line_settings_new(); + if (!self->handle) + g_error("failed to allocate line settings"); + } + + G_OBJECT_CLASS(g_gpiod_line_settings_parent_class)->constructed(obj); +} + +static void g_gpiod_line_settings_get_property(GObject *obj, guint prop_id, + GValue *val, GParamSpec *pspec) +{ + GPIODLineSettings *self = G_GPIOD_LINE_SETTINGS_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_LINE_SETTINGS_PROP_HANDLE: + g_value_set_pointer(val, self->handle); + break; + case G_GPIOD_LINE_SETTINGS_PROP_DIRECTION: + g_value_set_enum(val, + g_gpiod_line_direction_from_library( + gpiod_line_settings_get_direction( + self->handle), TRUE)); + break; + case G_GPIOD_LINE_SETTINGS_PROP_EDGE_DETECTION: + g_value_set_enum(val, + g_gpiod_line_edge_from_library( + gpiod_line_settings_get_edge_detection( + self->handle))); + break; + case G_GPIOD_LINE_SETTINGS_PROP_BIAS: + g_value_set_enum(val, + g_gpiod_line_bias_from_library( + gpiod_line_settings_get_bias(self->handle), + TRUE)); + break; + case G_GPIOD_LINE_SETTINGS_PROP_DRIVE: + g_value_set_enum(val, + g_gpiod_line_drive_from_library( + gpiod_line_settings_get_drive(self->handle))); + break; + case G_GPIOD_LINE_SETTINGS_PROP_ACTIVE_LOW: + g_value_set_boolean(val, + gpiod_line_settings_get_active_low(self->handle)); + break; + case G_GPIOD_LINE_SETTINGS_PROP_DEBOUNCE_PERIOD_US: + g_value_set_int64(val, + gpiod_line_settings_get_debounce_period_us( + self->handle)); + break; + case G_GPIOD_LINE_SETTINGS_PROP_EVENT_CLOCK: + g_value_set_enum(val, + g_gpiod_line_clock_from_library( + gpiod_line_settings_get_event_clock( + self->handle))); + break; + case G_GPIOD_LINE_SETTINGS_PROP_OUTPUT_VALUE: + g_value_set_enum(val, + g_gpiod_line_value_from_library( + gpiod_line_settings_get_output_value( + self->handle))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_line_settings_set_property(GObject *obj, guint prop_id, + const GValue *val, + GParamSpec *pspec) +{ + GPIODLineSettings *self = G_GPIOD_LINE_SETTINGS_OBJ(obj); + + if (!self->handle && prop_id != G_GPIOD_LINE_SETTINGS_PROP_HANDLE) { + self->handle = gpiod_line_settings_new(); + if (!self->handle) + /* The only possible error is ENOMEM. */ + g_error("Failed to allocate memory for the line-settings object."); + } + + switch (prop_id) { + case G_GPIOD_LINE_SETTINGS_PROP_HANDLE: + self->handle = g_value_get_pointer(val); + break; + case G_GPIOD_LINE_SETTINGS_PROP_DIRECTION: + gpiod_line_settings_set_direction(self->handle, + g_gpiod_line_direction_to_library( + g_value_get_enum(val))); + break; + case G_GPIOD_LINE_SETTINGS_PROP_EDGE_DETECTION: + gpiod_line_settings_set_edge_detection(self->handle, + g_gpiod_line_edge_to_library(g_value_get_enum(val))); + break; + case G_GPIOD_LINE_SETTINGS_PROP_BIAS: + gpiod_line_settings_set_bias(self->handle, + g_gpiod_line_bias_to_library(g_value_get_enum(val))); + break; + case G_GPIOD_LINE_SETTINGS_PROP_DRIVE: + gpiod_line_settings_set_drive(self->handle, + g_gpiod_line_drive_to_library(g_value_get_enum(val))); + break; + case G_GPIOD_LINE_SETTINGS_PROP_ACTIVE_LOW: + gpiod_line_settings_set_active_low(self->handle, + g_value_get_boolean(val)); + break; + case G_GPIOD_LINE_SETTINGS_PROP_DEBOUNCE_PERIOD_US: + gpiod_line_settings_set_debounce_period_us(self->handle, + g_value_get_int64(val)); + break; + case G_GPIOD_LINE_SETTINGS_PROP_EVENT_CLOCK: + gpiod_line_settings_set_event_clock(self->handle, + g_gpiod_line_clock_to_library(g_value_get_enum(val))); + break; + case G_GPIOD_LINE_SETTINGS_PROP_OUTPUT_VALUE: + gpiod_line_settings_set_output_value(self->handle, + g_gpiod_line_value_to_library(g_value_get_enum(val))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_line_settings_finalize(GObject *obj) +{ + GPIODLineSettings *self = G_GPIOD_LINE_SETTINGS_OBJ(obj); + + g_clear_pointer(&self->handle, gpiod_line_settings_free); + + G_OBJECT_CLASS(g_gpiod_line_settings_parent_class)->finalize(obj); +} + +static void +g_gpiod_line_settings_class_init(GPIODLineSettingsClass *line_settings_class) +{ + GObjectClass *class = G_OBJECT_CLASS(line_settings_class); + + class->constructed = g_gpiod_line_settings_constructed; + class->set_property = g_gpiod_line_settings_set_property; + class->get_property = g_gpiod_line_settings_get_property; + class->finalize = g_gpiod_line_settings_finalize; + + g_object_class_install_property(class, + G_GPIOD_LINE_SETTINGS_PROP_HANDLE, + g_param_spec_pointer("handle", "Handle", + "GPIO line settings object.", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_SETTINGS_PROP_DIRECTION, + g_param_spec_enum("direction", "Direction", + "Line direction setting.", + G_GPIOD_LINE_DIRECTION_TYPE, + G_GPIOD_LINE_DIRECTION_AS_IS, + G_PARAM_READWRITE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_SETTINGS_PROP_EDGE_DETECTION, + g_param_spec_enum("edge-detection", "Edge Detection", + "Line edge detection setting.", + G_GPIOD_LINE_EDGE_TYPE, + G_GPIOD_LINE_EDGE_NONE, + G_PARAM_READWRITE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_SETTINGS_PROP_BIAS, + g_param_spec_enum("bias", "Bias", + "Line bias setting.", + G_GPIOD_LINE_BIAS_TYPE, + G_GPIOD_LINE_BIAS_AS_IS, + G_PARAM_READWRITE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_SETTINGS_PROP_DRIVE, + g_param_spec_enum("drive", "Drive", + "Line drive setting.", + G_GPIOD_LINE_DRIVE_TYPE, + G_GPIOD_LINE_DRIVE_PUSH_PULL, + G_PARAM_READWRITE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_SETTINGS_PROP_ACTIVE_LOW, + g_param_spec_boolean("active-low", "Active-Low", + "Line active-low settings.", + FALSE, G_PARAM_READWRITE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_SETTINGS_PROP_DEBOUNCE_PERIOD_US, + g_param_spec_int64("debounce-period-us", + "Debounce Period (in microseconds)", + "Line debounce period (expressed in microseconds).", + 0, G_MAXINT64, 0, G_PARAM_READWRITE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_SETTINGS_PROP_EVENT_CLOCK, + g_param_spec_enum("event-clock", "Event Clock", + "Clock used to timestamp edge events.", + G_GPIOD_LINE_CLOCK_TYPE, + G_GPIOD_LINE_CLOCK_MONOTONIC, + G_PARAM_READWRITE)); + + g_object_class_install_property(class, + G_GPIOD_LINE_SETTINGS_PROP_OUTPUT_VALUE, + g_param_spec_enum("output-value", "Output Value", + "Line output value.", + G_GPIOD_LINE_VALUE_TYPE, + G_GPIOD_LINE_VALUE_INACTIVE, + G_PARAM_READWRITE)); +} + +static void g_gpiod_line_settings_init(GPIODLineSettings *self) +{ + self->handle = NULL; +} + +GPIODLineSettings *g_gpiod_line_settings_new(const gchar *first_prop, ...) +{ + GPIODLineSettings *settings; + va_list va; + + va_start(va, first_prop); + settings = G_GPIOD_LINE_SETTINGS_OBJ( + g_object_new_valist(G_GPIOD_LINE_SETTINGS_TYPE, + first_prop, va)); + va_end(va); + + return settings; +} + +void g_gpiod_line_settings_reset(GPIODLineSettings *self) +{ + g_assert(self); + + gpiod_line_settings_reset(self->handle); +} + +void g_gpiod_line_settings_set_direction(GPIODLineSettings *self, + GPIODLineDirection direction) +{ + g_gpiod_set_prop_enum(G_OBJECT(self), "direction", direction); +} + +GPIODLineDirection g_gpiod_line_settings_get_direction(GPIODLineSettings *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "direction"); +} + +void g_gpiod_line_settings_set_edge_detection(GPIODLineSettings *self, + GPIODLineEdge edge) +{ + g_gpiod_set_prop_enum(G_OBJECT(self), "edge-detection", edge); +} + +GPIODLineEdge g_gpiod_line_settings_get_edge_detection(GPIODLineSettings *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "edge-detection"); +} + +void g_gpiod_line_settings_set_bias(GPIODLineSettings *self, GPIODLineBias bias) +{ + g_gpiod_set_prop_enum(G_OBJECT(self), "bias", bias); +} + +GPIODLineBias g_gpiod_line_settings_get_bias(GPIODLineSettings *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "bias"); +} + +void g_gpiod_line_settings_set_drive(GPIODLineSettings *self, + GPIODLineDrive drive) +{ + g_gpiod_set_prop_enum(G_OBJECT(self), "drive", drive); +} + +GPIODLineDrive g_gpiod_line_settings_get_drive(GPIODLineSettings *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "drive"); +} + +void g_gpiod_line_settings_set_active_low(GPIODLineSettings *self, + gboolean active_low) +{ + g_gpiod_set_prop_bool(G_OBJECT(self), "active-low", active_low); +} + +gboolean g_gpiod_line_settings_get_active_low(GPIODLineSettings *self) +{ + return g_gpiod_get_prop_bool(G_OBJECT(self), "active-low"); +} + +void g_gpiod_line_settings_set_debounce_period_us(GPIODLineSettings *self, + GTimeSpan period) +{ + g_gpiod_set_prop_timespan(G_OBJECT(self), + "debounce-period-us", period); +} + +GTimeSpan g_gpiod_line_settings_get_debounce_period_us(GPIODLineSettings *self) +{ + return g_gpiod_get_prop_timespan(G_OBJECT(self), "debounce-period-us"); +} + +void g_gpiod_line_settings_set_event_clock(GPIODLineSettings *self, + GPIODLineClock event_clock) +{ + g_gpiod_set_prop_enum(G_OBJECT(self), "event-clock", event_clock); +} + +GPIODLineClock g_gpiod_line_settings_get_event_clock(GPIODLineSettings *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "event-clock"); +} + +void g_gpiod_line_settings_set_output_value(GPIODLineSettings *self, + GPIODLineValue value) +{ + g_gpiod_set_prop_enum(G_OBJECT(self), "output-value", value); +} + +GPIODLineValue g_gpiod_line_settings_get_output_value(GPIODLineSettings *self) +{ + return g_gpiod_get_prop_enum(G_OBJECT(self), "output-value"); +} diff --git a/bindings/glib/misc.c b/bindings/glib/misc.c new file mode 100644 index 0000000..139efed --- /dev/null +++ b/bindings/glib/misc.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include + +gboolean g_gpiod_is_gpiochip_device(const gchar *path) +{ + g_assert(path); + + return gpiod_is_gpiochip_device(path); +} + +const gchar *g_gpiod_api_version(void) +{ + return gpiod_api_version(); +} diff --git a/bindings/glib/request-config.c b/bindings/glib/request-config.c new file mode 100644 index 0000000..05c903b --- /dev/null +++ b/bindings/glib/request-config.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include +#include + +#include "internal.h" + +struct _GPIODRequestConfig { + GObject parent_instance; + struct gpiod_request_config *handle; +}; + +enum { + G_GPIOD_REQUEST_CONFIG_PROP_HANDLE = 1, + G_GPIOD_REQUEST_CONFIG_PROP_CONSUMER, + G_GPIOD_REQUEST_CONFIG_PROP_EVENT_BUFFER_SIZE, +}; + +G_DEFINE_TYPE(GPIODRequestConfig, g_gpiod_request_config, G_TYPE_OBJECT); + +static void g_gpiod_request_config_get_property(GObject *obj, guint prop_id, + GValue *val, GParamSpec *pspec) +{ + GPIODRequestConfig *self = G_GPIOD_REQUEST_CONFIG_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_REQUEST_CONFIG_PROP_HANDLE: + g_value_set_pointer(val, self->handle); + break; + case G_GPIOD_REQUEST_CONFIG_PROP_CONSUMER: + g_value_set_static_string(val, + gpiod_request_config_get_consumer(self->handle)); + break; + case G_GPIOD_REQUEST_CONFIG_PROP_EVENT_BUFFER_SIZE: + g_value_set_uint(val, + gpiod_request_config_get_event_buffer_size( + self->handle)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_request_config_set_property(GObject *obj, guint prop_id, + const GValue *val, + GParamSpec *pspec) +{ + GPIODRequestConfig *self = G_GPIOD_REQUEST_CONFIG_OBJ(obj); + + switch (prop_id) { + case G_GPIOD_REQUEST_CONFIG_PROP_CONSUMER: + gpiod_request_config_set_consumer(self->handle, + g_value_get_string(val)); + break; + case G_GPIOD_REQUEST_CONFIG_PROP_EVENT_BUFFER_SIZE: + gpiod_request_config_set_event_buffer_size(self->handle, + g_value_get_uint(val)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); + } +} + +static void g_gpiod_request_config_finalize(GObject *obj) +{ + GPIODRequestConfig *self = G_GPIOD_REQUEST_CONFIG_OBJ(obj); + + g_clear_pointer(&self->handle, gpiod_request_config_free); + + G_OBJECT_CLASS(g_gpiod_request_config_parent_class)->finalize(obj); +} + +static void +g_gpiod_request_config_class_init(GPIODRequestConfigClass *request_config_class) +{ + GObjectClass *class = G_OBJECT_CLASS(request_config_class); + + class->set_property = g_gpiod_request_config_set_property; + class->get_property = g_gpiod_request_config_get_property; + class->finalize = g_gpiod_request_config_finalize; + + g_object_class_install_property(class, + G_GPIOD_REQUEST_CONFIG_PROP_HANDLE, + g_param_spec_pointer("handle", "Handle", + "GPIO request config object.", + G_PARAM_READABLE)); + + g_object_class_install_property(class, + G_GPIOD_REQUEST_CONFIG_PROP_CONSUMER, + g_param_spec_string("consumer", "Consumer", + "Name of the request consumer.", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property(class, + G_GPIOD_REQUEST_CONFIG_PROP_EVENT_BUFFER_SIZE, + g_param_spec_uint("event-buffer-size", "Event Buffer Size", + "Size of the kernel event buffer size of the request.", + 0, G_MAXUINT, 64, G_PARAM_READWRITE)); +} + +static void g_gpiod_request_config_init(GPIODRequestConfig *self) +{ + self->handle = gpiod_request_config_new(); + if (!self->handle) + /* The only possible error is ENOMEM. */ + g_error("Failed to allocate memory for the request-config object."); +} + +GPIODRequestConfig *g_gpiod_request_config_new(const gchar *first_prop, ...) +{ + GPIODRequestConfig *settings; + va_list va; + + va_start(va, first_prop); + settings = G_GPIOD_REQUEST_CONFIG_OBJ( + g_object_new_valist(G_GPIOD_REQUEST_CONFIG_TYPE, + first_prop, va)); + va_end(va); + + return settings; +} + +void g_gpiod_request_config_set_consumer(GPIODRequestConfig *self, + const gchar *consumer) +{ + g_assert(self); + + g_gpiod_set_prop_string(G_OBJECT(self), "consumer", consumer); +} + +const gchar *g_gpiod_request_config_get_consumer(GPIODRequestConfig *self) +{ + g_assert(self); + + return g_gpiod_get_prop_string(G_OBJECT(self), "consumer"); +} + +void g_gpiod_request_config_set_event_buffer_size(GPIODRequestConfig *self, + guint event_buffer_size) +{ + g_assert(self); + + g_gpiod_set_prop_uint(G_OBJECT(self), "event-buffer-size", + event_buffer_size); +} + +guint g_gpiod_request_config_get_event_buffer_size(GPIODRequestConfig *self) +{ + g_assert(self); + + return g_gpiod_get_prop_uint(G_OBJECT(self), "event-buffer-size"); +} From patchwork Fri Jun 28 18:58:25 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808381 Received: from mail-wm1-f50.google.com (mail-wm1-f50.google.com [209.85.128.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 83CBB55887 for ; Fri, 28 Jun 2024 18:59:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601149; cv=none; b=KwzzrcsYXUJGbJq5E88EJ0H1uQW5AwiI7kZAfQd2V5/AXDZq3kjzL3RJNsoP9KS9wa9m1tDR0tLSs8MIScWrydWpfTFKrUXTttXChSlpKlgm4SQjvlysfQ9MVbk8gvslRy4rDcCQvF5pwAzwvshaYroP/dVovRnkbCYY9aVJZMg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601149; c=relaxed/simple; bh=SlzY/eobVZbAqEQnhELAbpVvIf4+tFvg5l6GcSa0cPU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=pzL2NNBYwRkFjbZ0pzKqFhpuKeTIr6+cDeTIrfw4WRrcuXVkfhmP1Bj1mMKYdRer08ZAejsuhR5ZZxmkH7Yn+iO9R92/m4vhPlOXHvJ0iWZnc3ORm9g2nCZxvkf26T5XadhCrqEYgJeuY200bTpUEXNGpMte3AwZjdULdJESy0Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=pV/q0HzU; arc=none smtp.client-ip=209.85.128.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="pV/q0HzU" Received: by mail-wm1-f50.google.com with SMTP id 5b1f17b1804b1-425680b1d3aso6583465e9.2 for ; Fri, 28 Jun 2024 11:59:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601145; x=1720205945; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=5TxxKDw6lFxix32iMlWY5Xj3wDy40LO1W4xF7xjBoQs=; b=pV/q0HzUncLa/HDIv8plXtGFhBbV+5CgnZvcfMcIK/oHlzJj2qHigtBIfkqn58PIKx A3o9OiGdyoJixkOhAJRdRfmWkFqpPsQYD//cGzCra4SEp/5P+X0RctghM66nCDMpwtCM zqGJj6gPIvqDxMU1tK5VJY7L1dGfebRGtUzmL2HJb2dNgYGw+A2nvEpDZWPofKlaqoTD BqgnYeDhUd3mC91J6NKgjCAVmP5x4JkvCaLcRngvAyJQHVzrixM957xsAoka6F6SGKJD +KnRsuw0nWVdaxUA1qH7cPbbiURlDZreb718YEs1OMVjC7tpyI1GqzOKz7kWwT/X8fZM u9ZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601145; x=1720205945; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5TxxKDw6lFxix32iMlWY5Xj3wDy40LO1W4xF7xjBoQs=; b=Aztnd3FHIGkXHIvPG4BcwpROdKnUxYx+fZoITXDJ+B0kynIQeOukTQub1D6rgZNYe9 u1sv2sb731+J3Z74fJGdxzMXaMxAU2Nvxz8j06O6e9Tch/khtvH094jCSVu6+nJttGv2 RfFCrBYmuBpcCUlM0NPGsiJzX7pNMr5zcLZ7g6MWLhFEFjUBB4O3EujrHqDOGXgNV7Nw uzZFRLLvczofmlKNn0QJO+U41ODoClX63iGE/3qO46SK/VQEB4HPkgLtqChUKJR2g90f ICV3KhxLGHeAFQrS+X5AWwMSUtBCW6XnZp4a/tK55F0SYqjW3994YK/mk4sQTboM3Vfq GUeg== X-Forwarded-Encrypted: i=1; AJvYcCWN9klb4VDRP7xbCO231M1VUxJkJOtRFH1kWGp/Ri5IUFn0K85/G4ZSguo1iOjbUK5X9ykHQuXJmZ+EmJJZiDHmV82FYGFzMX58+Q== X-Gm-Message-State: AOJu0YzT8a8NW7ZWVuy44JHbvrkbm2IwuGshyGFNrEOk7XoFZfZfCKt6 PzcwCCoUkDg/Dtvna+Njz18qzo+zn4BBv1lL6JroXgiKGZ7mEr6UuBac8JDbWBo= X-Google-Smtp-Source: AGHT+IEubA+6EKVJwOoYNecPKFmEPUqCHTRQdzvA7WehmxQBzPQtKf7Ot7A1lFqRaRzKGKiyZeH0ng== X-Received: by 2002:adf:f782:0:b0:35f:2725:7ae2 with SMTP id ffacd0b85a97d-366e9628e80mr12292776f8f.57.1719601144693; Fri, 28 Jun 2024 11:59:04 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:03 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:25 +0200 Subject: [PATCH RESEND libgpiod v2 06/18] bindings: glib: add examples Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-6-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=30641; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=LIJC546j181NYtljHCaPFJ+KtgQfVmiCJZfnYLabPyY=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfre+HHgYbww0p3+i+Hr0uqnOZHeqSLbQleb I1t1bTwXlqJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H6wAKCRARpy6gFHHX cs7qEADQb1rL9gvvQxijDLGNTaas/7iprK4TkN1NrTolBptKxgd0JwcwG3J7JRQhvIuNkTUHcxf nFoWrsqiR6TJFAHSLZ1hnLTB0OMgyhd1DeojL60dYqG2sqlnrgwn//nDiWUVmzROpoo5Eal9cM6 L0cJPBtz3YBE96Z+aHFiTi4AHRM9FmqJzZxQDKOaHWNKIcA27jrge9vx+qp38pl9C5jWkEXPBOU NqY/seHGgZXZUMnsQ5ccUldAuY43GR88E4N/iRduXOZ6JH7jjVzOi9ctRjIRrB4nFDijqJSmIH4 ER3DcVwqr0qF+ZL0N7VS2z55tLZumuUux+Utr5jWXqHEYG1mKDo1xT//C0bnEDQsS32u3DHuTcx 1bVi5OGeW8p/u4SksTU/rjbo/wNHrRrbpsNbZZx7rYbZKZCByVgsshphjW7azcWaCJAo0okXd3K gaEntxAzfL4PKeZM/4rPbB5ZvrFb1Q/12CEn+cgEE9tu1pIFQGMmrTnob8si4M43MHIqKccNNBq NyljzQBY8bfgNjWqbbVLEea9f5dW6J49asY5aeKxQ54HB+zAoD1N4dT5aClohiIhNqGb++tnpAv nrGJpgw9XbpwiLJcHW0H4ui3+3LzOvU0ypntUa76cybaTeu040QTPc675uMWvirZYeFaMwjNbcj +nVo2PVOBvK/gIg== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add example programs showcasing the usage of GLib bindings to libgpiod. Signed-off-by: Bartosz Golaszewski --- bindings/glib/examples/find_line_by_name_glib.c | 68 +++++++++++ bindings/glib/examples/get_chip_info_glib.c | 39 ++++++ bindings/glib/examples/get_line_info_glib.c | 79 ++++++++++++ bindings/glib/examples/get_line_value_glib.c | 67 +++++++++++ .../glib/examples/get_multiple_line_values_glib.c | 72 +++++++++++ .../examples/reconfigure_input_to_output_glib.c | 103 ++++++++++++++++ bindings/glib/examples/toggle_line_value_glib.c | 99 ++++++++++++++++ .../examples/toggle_multiple_line_values_glib.c | 132 +++++++++++++++++++++ bindings/glib/examples/watch_line_info_glib.c | 63 ++++++++++ bindings/glib/examples/watch_line_value_glib.c | 91 ++++++++++++++ .../examples/watch_multiple_edge_rising_glib.c | 95 +++++++++++++++ 11 files changed, 908 insertions(+) diff --git a/bindings/glib/examples/find_line_by_name_glib.c b/bindings/glib/examples/find_line_by_name_glib.c new file mode 100644 index 0000000..46a193e --- /dev/null +++ b/bindings/glib/examples/find_line_by_name_glib.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/* Minimal example of finding a line with the given name. */ + +#include +#include +#include + +int main(void) +{ + /* Example configuration - customize to suit your situation. */ + static const gchar *const line_name = "GPIO0"; + + g_autoptr(GPIODChipInfo) info = NULL; + g_autoptr(GError) err = NULL; + g_autoptr(GDir) dir = NULL; + const gchar *filename; + gboolean ret; + guint offset; + + dir = g_dir_open("/dev", 0, &err); + if (err) { + g_printerr("Unable to open /dev: %s\n", err->message); + return EXIT_FAILURE; + } + + /* + * Names are not guaranteed unique, so this finds the first line with + * the given name. + */ + while ((filename = g_dir_read_name(dir))) { + g_autoptr(GPIODChip) chip = NULL; + g_autofree gchar *path = NULL; + + path = g_strdup_printf("/dev/%s", filename); + if (!g_gpiod_is_gpiochip_device(path)) + continue; + + chip = g_gpiod_chip_new(path, &err); + if (err) { + g_printerr("Failed to open the GPIO chip at '%s': %s\n", + path, err->message); + return EXIT_FAILURE; + } + + ret = g_gpiod_chip_get_line_offset_from_name(chip, line_name, + &offset, &err); + if (!ret) { + g_printerr("Failed to map the line name '%s' to offset: %s\n", + line_name, err->message); + return EXIT_FAILURE; + } + + info = g_gpiod_chip_get_info(chip, &err); + if (!info) { + g_printerr("Failed to get chip info: %s\n", + err->message); + return EXIT_FAILURE; + } + + g_print("%s %u\n", g_gpiod_chip_info_get_name(info), offset); + } + + g_print("line '%s' not found\n", line_name); + + return EXIT_SUCCESS; +} diff --git a/bindings/glib/examples/get_chip_info_glib.c b/bindings/glib/examples/get_chip_info_glib.c new file mode 100644 index 0000000..1f1811a --- /dev/null +++ b/bindings/glib/examples/get_chip_info_glib.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/* Minimal example of reading the info for a chip. */ + +#include +#include +#include + +int main(void) +{ + /* Example configuration - customize to suit your situation. */ + static const gchar *const chip_path = "/dev/gpiochip0"; + + g_autoptr(GPIODChipInfo) info = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + + chip = g_gpiod_chip_new(chip_path, &err); + if (err) { + g_printerr("Failed to open the GPIO chip at '%s': %s\n", + chip_path, err->message); + return EXIT_FAILURE; + } + + info = g_gpiod_chip_get_info(chip, &err); + if (err) { + g_printerr("Failed to retrieve GPIO chip info: %s\n", + err->message); + return EXIT_FAILURE; + } + + g_print("%s [%s] (%u lines)\n", + g_gpiod_chip_info_get_name(info), + g_gpiod_chip_info_get_label(info), + g_gpiod_chip_info_get_num_lines(info)); + + return EXIT_SUCCESS; +} diff --git a/bindings/glib/examples/get_line_info_glib.c b/bindings/glib/examples/get_line_info_glib.c new file mode 100644 index 0000000..f3da267 --- /dev/null +++ b/bindings/glib/examples/get_line_info_glib.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +/* Minimal example of reading the info for a line. */ + +#include +#include +#include + +static GString *make_flags(GPIODLineInfo *info) +{ + g_autofree gchar *drive_str = NULL; + g_autofree gchar *edge_str = NULL; + g_autofree gchar *bias_str = NULL; + GPIODLineDrive drive; + GPIODLineEdge edge; + GPIODLineBias bias; + GString *ret; + + edge = g_gpiod_line_info_get_edge_detection(info); + bias = g_gpiod_line_info_get_bias(info); + drive = g_gpiod_line_info_get_drive(info); + + edge_str = g_enum_to_string(G_GPIOD_LINE_EDGE_TYPE, edge); + bias_str = g_enum_to_string(G_GPIOD_LINE_BIAS_TYPE, bias); + drive_str = g_enum_to_string(G_GPIOD_LINE_DRIVE_TYPE, drive); + + ret = g_string_new(NULL); + g_string_printf(ret, "%s, %s, %s", edge_str, bias_str, drive_str); + g_string_replace(ret, "G_GPIOD_LINE_", "", 0); + + return ret; +} + +int main(void) +{ + /* Example configuration - customize to suit your situation. */ + static const gchar *const chip_path = "/dev/gpiochip0"; + static const guint line_offset = 4; + + g_autoptr(GPIODLineInfo) info = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GString) flags = NULL; + const gchar *name, *consumer; + GPIODLineDirection direction; + g_autoptr(GError) err = NULL; + gboolean active_low; + + chip = g_gpiod_chip_new(chip_path, &err); + if (err) { + g_printerr("Failed to open the GPIO chip at '%s': %s\n", + chip_path, err->message); + return EXIT_FAILURE; + } + + info = g_gpiod_chip_get_line_info(chip, line_offset, &err); + if (err) { + g_printerr("Failed to retrieve GPIO line info: %s\n", + err->message); + return EXIT_FAILURE; + } + + name = g_gpiod_line_info_get_name(info); + consumer = g_gpiod_line_info_get_consumer(info); + direction = g_gpiod_line_info_get_direction(info); + active_low = g_gpiod_line_info_is_active_low(info); + flags = make_flags(info); + + g_print("\tline: %u %s %s %s %s [%s]\n", + line_offset, + name ?: "unnamed", + consumer ?: "unused", + direction == G_GPIOD_LINE_DIRECTION_INPUT ? + "input" : "output", + active_low ? "active-low" : "active-high", + flags->str); + + return EXIT_SUCCESS; +} diff --git a/bindings/glib/examples/get_line_value_glib.c b/bindings/glib/examples/get_line_value_glib.c new file mode 100644 index 0000000..614390e --- /dev/null +++ b/bindings/glib/examples/get_line_value_glib.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/* Minimal example of reading a single line. */ + +#include +#include +#include + +int main(void) +{ + /* Example configuration - customize to suit your situation. */ + static const gchar *const chip_path = "/dev/gpiochip1"; + static const guint line_offset = 5; + + g_autoptr(GPIODRequestConfig) req_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + guint offset; + gboolean ret; + + chip = g_gpiod_chip_new(chip_path, &err); + if (!chip) { + g_printerr("unable to open %s: %s\n", chip_path, err->message); + return EXIT_FAILURE; + } + + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + g_array_append_val(offsets, line_offset); + + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, + NULL); + + line_cfg = g_gpiod_line_config_new(); + ret = g_gpiod_line_config_add_line_settings(line_cfg, offsets, + settings, &err); + if (!ret) { + g_printerr("failed to add line settings to line config: %s\n", + err->message); + return EXIT_FAILURE; + } + + req_cfg = g_gpiod_request_config_new("consumer", "get-line-value-glib", + NULL); + + request = g_gpiod_chip_request_lines(chip, req_cfg, line_cfg, &err); + if (!request) { + g_printerr("failed to request lines: %s\n", err->message); + return EXIT_FAILURE; + } + + ret = g_gpiod_line_request_get_value(request, line_offset, + &offset, &err); + if (!ret) { + g_printerr("failed to read line values: %s\n", err->message); + return EXIT_FAILURE; + } + + g_print("%u\n", offset); + + return EXIT_SUCCESS; +} diff --git a/bindings/glib/examples/get_multiple_line_values_glib.c b/bindings/glib/examples/get_multiple_line_values_glib.c new file mode 100644 index 0000000..c65d45e --- /dev/null +++ b/bindings/glib/examples/get_multiple_line_values_glib.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/* Minimal example of reading multiple lines. */ + +#include +#include +#include + +int main(void) +{ + /* Example configuration - customize to suit your situation. */ + static const gchar *const chip_path = "/dev/gpiochip1"; + static const guint line_offsets[] = { 5, 3, 7 }; + static const gsize num_lines = 3; + + g_autoptr(GPIODRequestConfig) req_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + guint i, j; + + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + for (i = 0; i < num_lines; i++) + g_array_append_val(offsets, line_offsets[i]); + + chip = g_gpiod_chip_new(chip_path, &err); + if (!chip) { + g_printerr("unable to open %s: %s\n", chip_path, err->message); + return EXIT_FAILURE; + } + + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, + NULL); + + line_cfg = g_gpiod_line_config_new(); + ret = g_gpiod_line_config_add_line_settings(line_cfg, offsets, + settings, &err); + if (!ret) { + g_printerr("failed to add line settings to line config: %s\n", + err->message); + return EXIT_FAILURE; + } + + req_cfg = g_gpiod_request_config_new( + "consumer", "get-multiple-line-values", NULL); + + request = g_gpiod_chip_request_lines(chip, req_cfg, line_cfg, &err); + if (!request) { + g_printerr("failed to request lines: %s\n", err->message); + return EXIT_FAILURE; + } + + ret = g_gpiod_line_request_get_values_subset(request, offsets, + &values, &err); + if (!ret) { + g_printerr("failed to read line values: %s\n", err->message); + return EXIT_FAILURE; + } + + for (j = 0; j < values->len; j++) + g_print("%d ", g_array_index(values, GPIODLineValue, j)); + g_print("\n"); + + return EXIT_SUCCESS; +} diff --git a/bindings/glib/examples/reconfigure_input_to_output_glib.c b/bindings/glib/examples/reconfigure_input_to_output_glib.c new file mode 100644 index 0000000..896b19f --- /dev/null +++ b/bindings/glib/examples/reconfigure_input_to_output_glib.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/* + * Example of a bi-directional line requested as input and then switched + * to output. + */ + +#include +#include +#include + +int main(void) +{ + /* Example configuration - customize to suit your situation */ + static const gchar *const chip_path = "/dev/gpiochip1"; + static const guint line_offset = 5; + + g_autoptr(GPIODRequestConfig) req_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + GPIODLineValue value; + gboolean ret; + + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + g_array_append_val(offsets, line_offset); + + chip = g_gpiod_chip_new(chip_path, &err); + if (!chip) { + g_printerr("unable to open %s: %s\n", chip_path, err->message); + return EXIT_FAILURE; + } + + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, + NULL); + + line_cfg = g_gpiod_line_config_new(); + ret = g_gpiod_line_config_add_line_settings(line_cfg, offsets, + settings, &err); + if (!ret) { + g_printerr("failed to add line settings to line config: %s\n", + err->message); + return EXIT_FAILURE; + } + + req_cfg = g_gpiod_request_config_new( + "consumer", "reconfigure-input-to-output", NULL); + + request = g_gpiod_chip_request_lines(chip, req_cfg, line_cfg, &err); + if (!request) { + g_printerr("failed to request lines: %s\n", err->message); + return EXIT_FAILURE; + } + + /* Read the current line value. */ + ret = g_gpiod_line_request_get_value(request, line_offset, + &value, &err); + if (!ret) { + g_printerr("failed to read line value: %s\n", err->message); + return EXIT_FAILURE; + } + + g_print("%s (input)\n", + value == G_GPIOD_LINE_VALUE_ACTIVE ? "Active" : "Inactive"); + + /* Switch the line to an output and drive it high. */ + g_gpiod_line_settings_set_direction(settings, + G_GPIOD_LINE_DIRECTION_OUTPUT); + g_gpiod_line_settings_set_output_value(settings, + G_GPIOD_LINE_VALUE_ACTIVE); + ret = g_gpiod_line_config_add_line_settings(line_cfg, offsets, + settings, &err); + if (!ret) { + g_printerr("failed to add line settings to line config: %s\n", + err->message); + return EXIT_FAILURE; + } + + /* Reconfigure lines. */ + ret = g_gpiod_line_request_reconfigure_lines(request, line_cfg, &err); + if (!ret) { + g_printerr("failed to reconfigure lines: %s\n", err->message); + return EXIT_FAILURE; + } + + /* Report the current driven value. */ + ret = g_gpiod_line_request_get_value(request, line_offset, + &value, &err); + if (!ret) { + g_printerr("failed to read line value: %s\n", err->message); + return EXIT_FAILURE; + } + + g_print("%s (output)\n", + value == G_GPIOD_LINE_VALUE_ACTIVE ? "Active" : "Inactive"); + + return EXIT_SUCCESS; +} diff --git a/bindings/glib/examples/toggle_line_value_glib.c b/bindings/glib/examples/toggle_line_value_glib.c new file mode 100644 index 0000000..f11f121 --- /dev/null +++ b/bindings/glib/examples/toggle_line_value_glib.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/* Minimal example of periodically toggling a single line. */ + +#include +#include +#include + +typedef struct { + GPIODLineRequest *request; + guint line_offset; + GPIODLineValue value; +} ToggleData; + +static gboolean toggle_line(gpointer user_data) +{ + ToggleData *data = user_data; + g_autoptr(GError) err = NULL; + gboolean ret; + + data->value = data->value == G_GPIOD_LINE_VALUE_ACTIVE ? + G_GPIOD_LINE_VALUE_INACTIVE : + G_GPIOD_LINE_VALUE_ACTIVE; + + ret = g_gpiod_line_request_set_value(data->request, data->line_offset, + data->value, &err); + if (!ret) { + g_printerr("failed to set line value: %s\n", err->message); + exit(EXIT_FAILURE); + } + + g_print("%u=%s\n", + data->line_offset, + data->value == G_GPIOD_LINE_VALUE_ACTIVE ? + "active" : "inactive"); + + return G_SOURCE_CONTINUE; +} + +int main(void) +{ + /* Example configuration - customize to suit your situation. */ + static const gchar *const chip_path = "/dev/gpiochip1"; + static const guint line_offset = 5; + + g_autoptr(GPIODRequestConfig) req_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GMainLoop) loop = NULL; + g_autoptr(GError) err = NULL; + ToggleData data; + gboolean ret; + + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + g_array_append_val(offsets, line_offset); + + chip = g_gpiod_chip_new(chip_path, &err); + if (!chip) { + g_printerr("unable to open %s: %s\n", chip_path, err->message); + return EXIT_FAILURE; + } + + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_OUTPUT, + NULL); + line_cfg = g_gpiod_line_config_new(); + ret = g_gpiod_line_config_add_line_settings(line_cfg, offsets, + settings, &err); + if (!ret) { + g_printerr("failed to add line settings to line config: %s\n", + err->message); + return EXIT_FAILURE; + } + + req_cfg = g_gpiod_request_config_new("consumer", "toggle-line-value", + NULL); + + request = g_gpiod_chip_request_lines(chip, req_cfg, line_cfg, &err); + if (!request) { + g_printerr("failed to request lines: %s\n", err->message); + return EXIT_FAILURE; + } + + data.request = request; + data.line_offset = line_offset; + data.value = G_GPIOD_LINE_VALUE_INACTIVE; + + loop = g_main_loop_new(NULL, FALSE); + /* Do the GLib way: add a callback to be invoked from the main loop. */ + g_timeout_add_seconds(1, toggle_line, &data); + + g_main_loop_run(loop); + + return EXIT_SUCCESS; +} diff --git a/bindings/glib/examples/toggle_multiple_line_values_glib.c b/bindings/glib/examples/toggle_multiple_line_values_glib.c new file mode 100644 index 0000000..24ee7fa --- /dev/null +++ b/bindings/glib/examples/toggle_multiple_line_values_glib.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/* Minimal example of periodically toggling multiple lines. */ + +#include +#include +#include + +typedef struct { + GPIODLineRequest *request; + GArray *offsets; + GArray *values; +} ToggleData; + +static void toggle_values(GArray *values) +{ + GPIODLineValue *value; + guint i; + + for (i = 0; i < values->len; i++) { + value = &g_array_index(values, GPIODLineValue, i); + *value = *value == G_GPIOD_LINE_VALUE_ACTIVE ? + G_GPIOD_LINE_VALUE_INACTIVE : + G_GPIOD_LINE_VALUE_ACTIVE; + } +} + +static gboolean toggle_lines(gpointer user_data) +{ + ToggleData *data = user_data; + g_autoptr(GError) err = NULL; + gboolean ret; + guint i; + + toggle_values(data->values); + + ret = g_gpiod_line_request_set_values_subset(data->request, + data->offsets, + data->values, &err); + if (!ret) { + g_printerr("failed to set line values: %s\n", err->message); + exit(EXIT_FAILURE); + } + + for (i = 0; i < data->offsets->len; i++) + g_print("%u=%s ", + g_array_index(data->offsets, guint, i), + g_array_index(data->values, + GPIODLineValue, + i) == G_GPIOD_LINE_VALUE_ACTIVE ? + "active" : "inactive"); + g_print("\n"); + + return G_SOURCE_CONTINUE; +} + +int main(void) +{ + /* Example configuration - customize to suit your situation. */ + static const gchar *const chip_path = "/dev/gpiochip1"; + static const guint line_offsets[] = { 5, 3, 7 }; + static const GPIODLineValue line_values[] = { + G_GPIOD_LINE_VALUE_ACTIVE, + G_GPIOD_LINE_VALUE_ACTIVE, + G_GPIOD_LINE_VALUE_INACTIVE + }; + static const gsize num_lines = 3; + + g_autoptr(GPIODRequestConfig) req_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GMainLoop) loop = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + ToggleData data; + gboolean ret; + + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + g_array_append_vals(offsets, line_offsets, num_lines); + + values = g_array_new(FALSE, TRUE, sizeof(GPIODLineValue)); + g_array_append_vals(values, line_values, num_lines); + + chip = g_gpiod_chip_new(chip_path, &err); + if (!chip) { + g_printerr("unable to open %s: %s\n", chip_path, err->message); + return EXIT_FAILURE; + } + + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_OUTPUT, + NULL); + line_cfg = g_gpiod_line_config_new(); + ret = g_gpiod_line_config_add_line_settings(line_cfg, offsets, + settings, &err); + if (!ret) { + g_printerr("failed to add line settings to line config: %s\n", + err->message); + return EXIT_FAILURE; + } + + ret = g_gpiod_line_config_set_output_values(line_cfg, values, &err); + if (!ret) { + g_printerr("failed to set output values: %s\n", err->message); + return EXIT_FAILURE; + } + + req_cfg = g_gpiod_request_config_new("consumer", "toggle-line-value", + NULL); + + request = g_gpiod_chip_request_lines(chip, req_cfg, line_cfg, &err); + if (!request) { + g_printerr("failed to request lines: %s\n", err->message); + return EXIT_FAILURE; + } + + data.request = request; + data.offsets = offsets; + data.values = values; + + loop = g_main_loop_new(NULL, FALSE); + /* Do the GLib way: add a callback to be invoked from the main loop. */ + g_timeout_add_seconds(1, toggle_lines, &data); + + g_main_loop_run(loop); + + return EXIT_SUCCESS; +} diff --git a/bindings/glib/examples/watch_line_info_glib.c b/bindings/glib/examples/watch_line_info_glib.c new file mode 100644 index 0000000..e05d020 --- /dev/null +++ b/bindings/glib/examples/watch_line_info_glib.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/* Minimal example of watching for requests on particular lines. */ + +#include +#include +#include + +static void on_info_event(GPIODChip *chip G_GNUC_UNUSED, + GPIODInfoEvent *event, + gpointer data G_GNUC_UNUSED) +{ + g_autoptr(GPIODLineInfo) info = NULL; + g_autoptr(GString) event_name = NULL; + guint offset; + + event_name = g_string_new( + g_enum_to_string(G_GPIOD_INFO_EVENT_TYPE_TYPE, + g_gpiod_info_event_get_event_type(event))); + g_string_replace(event_name, "G_GPIOD_INFO_EVENT_LINE_", "", 0); + info = g_gpiod_info_event_get_line_info(event); + offset = g_gpiod_line_info_get_offset(info); + + g_print("%s %u\n", event_name->str, offset); +} + +int main(void) +{ + static const gchar *const chip_path = "/dev/gpiochip1"; + static const guint line_offsets[] = { 5, 3, 7 }; + static const gsize num_lines = 3; + + g_autoptr(GMainLoop) loop = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + guint i; + + chip = g_gpiod_chip_new(chip_path, &err); + if (!chip) { + g_printerr("unable to open %s: %s\n", chip_path, err->message); + return EXIT_FAILURE; + } + + for (i = 0; i < num_lines; i++) { + g_autoptr(GPIODLineInfo) info = + g_gpiod_chip_watch_line_info(chip, line_offsets[i], + &err); + if (!info) { + g_printerr("unable to watch line info for offset %u: %s", + line_offsets[1], err->message); + return EXIT_FAILURE; + } + } + + loop = g_main_loop_new(NULL, FALSE); + + g_signal_connect(chip, "info-event", G_CALLBACK(on_info_event), NULL); + + g_main_loop_run(loop); + + return EXIT_SUCCESS; +} diff --git a/bindings/glib/examples/watch_line_value_glib.c b/bindings/glib/examples/watch_line_value_glib.c new file mode 100644 index 0000000..0f3933e --- /dev/null +++ b/bindings/glib/examples/watch_line_value_glib.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/* Minimal example of asynchronously watching for edges on a single line. */ + +#include +#include +#include + +static void on_edge_event(GPIODLineRequest *request G_GNUC_UNUSED, + GPIODEdgeEvent *event, + gpointer data G_GNUC_UNUSED) +{ + g_autoptr(GString) event_name = NULL; + guint64 timestamp; + guint offset; + + event_name = g_string_new( + g_enum_to_string(G_GPIOD_EDGE_EVENT_TYPE_TYPE, + g_gpiod_edge_event_get_event_type(event))); + g_string_replace(event_name, "G_GPIOD_EDGE_EVENT_", "", 0); + timestamp = g_gpiod_edge_event_get_timestamp_ns(event); + offset = g_gpiod_edge_event_get_line_offset(event); + + g_print("%s %lu %u\n", event_name->str, timestamp, offset); +} + +int main(void) +{ + /* Example configuration - customize to suit your situation. */ + static const gchar *const chip_path = "/dev/gpiochip1"; + static const guint line_offset = 5; + + g_autoptr(GPIODRequestConfig) req_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GMainLoop) loop = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + g_array_append_val(offsets, line_offset); + + chip = g_gpiod_chip_new(chip_path, &err); + if (!chip) { + g_printerr("unable to open %s: %s\n", chip_path, err->message); + return EXIT_FAILURE; + } + + /* + * Assume a button connecting the pin to ground, so pull it up and + * provide some debounce. + */ + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, + "edge-detection", G_GPIOD_LINE_EDGE_BOTH, + "bias", G_GPIOD_LINE_BIAS_PULL_UP, + "debounce-period-us", 1000, + NULL); + + line_cfg = g_gpiod_line_config_new(); + ret = g_gpiod_line_config_add_line_settings(line_cfg, offsets, + settings, &err); + if (!ret) { + g_printerr("failed to add line settings to line config: %s", + err->message); + return EXIT_FAILURE; + } + + req_cfg = g_gpiod_request_config_new("consumer", "watch-line-value", + NULL); + + request = g_gpiod_chip_request_lines(chip, req_cfg, line_cfg, &err); + if (!request) { + g_printerr("failed to request lines: %s", err->message); + return EXIT_FAILURE; + } + + loop = g_main_loop_new(NULL, FALSE); + + /* Connect to the edge-event signal on the line-request. */ + g_signal_connect(request, "edge-event", + G_CALLBACK(on_edge_event), NULL); + + g_main_loop_run(loop); + + return EXIT_SUCCESS; +} diff --git a/bindings/glib/examples/watch_multiple_edge_rising_glib.c b/bindings/glib/examples/watch_multiple_edge_rising_glib.c new file mode 100644 index 0000000..e99b58a --- /dev/null +++ b/bindings/glib/examples/watch_multiple_edge_rising_glib.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +/* + * Minimal example of asynchronously watching for rising edges on multiple + * lines. + */ + +#include +#include +#include + +static void on_edge_event(GPIODLineRequest *request G_GNUC_UNUSED, + GPIODEdgeEvent *event, + gpointer data G_GNUC_UNUSED) +{ + g_autoptr(GString) event_name = NULL; + guint64 timestamp; + guint offset; + + event_name = g_string_new( + g_enum_to_string(G_GPIOD_EDGE_EVENT_TYPE_TYPE, + g_gpiod_edge_event_get_event_type(event))); + g_string_replace(event_name, "G_GPIOD_EDGE_EVENT_", "", 0); + timestamp = g_gpiod_edge_event_get_timestamp_ns(event); + offset = g_gpiod_edge_event_get_line_offset(event); + + g_print("%s %lu %u\n", event_name->str, timestamp, offset); +} + +int main(void) +{ + /* Example configuration - customize to suit your situation. */ + static const gchar *const chip_path = "/dev/gpiochip1"; + static const guint line_offsets[] = { 5, 3, 7 }; + static const gsize num_lines = 3; + + g_autoptr(GPIODRequestConfig) req_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GMainLoop) loop = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + g_array_append_vals(offsets, line_offsets, num_lines); + + chip = g_gpiod_chip_new(chip_path, &err); + if (!chip) { + g_printerr("unable to open %s: %s\n", chip_path, err->message); + return EXIT_FAILURE; + } + + /* + * Assume a button connecting the pin to ground, so pull it up and + * provide some debounce. + */ + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, + "edge-detection", G_GPIOD_LINE_EDGE_RISING, + "bias", G_GPIOD_LINE_BIAS_PULL_UP, + "debounce-period-us", 1000, + NULL); + + line_cfg = g_gpiod_line_config_new(); + ret = g_gpiod_line_config_add_line_settings(line_cfg, offsets, + settings, &err); + if (!ret) { + g_printerr("failed to add line settings to line config: %s", + err->message); + return EXIT_FAILURE; + } + + req_cfg = g_gpiod_request_config_new(NULL); + g_gpiod_request_config_set_consumer(req_cfg, "watch-multiline-value"); + + request = g_gpiod_chip_request_lines(chip, req_cfg, line_cfg, &err); + if (!request) { + g_printerr("failed to request lines: %s", err->message); + return EXIT_FAILURE; + } + + loop = g_main_loop_new(NULL, FALSE); + + /* Connect to the edge-event signal on the line-request. */ + g_signal_connect(request, "edge-event", + G_CALLBACK(on_edge_event), NULL); + + g_main_loop_run(loop); + + return EXIT_SUCCESS; +} From patchwork Fri Jun 28 18:58:26 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808530 Received: from mail-wr1-f49.google.com (mail-wr1-f49.google.com [209.85.221.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 53A94558B7 for ; Fri, 28 Jun 2024 18:59:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601152; cv=none; b=cDgVpPZeCG9b4qdXl+RhX61j8OC9LAMovWx7bxIvsr1cEHt1R4NTCG+UMQEXx1b/wJ+ibzAIH8TmLZyWuAsDY6un3lViGXM/5irl9mrJsrtmG9omzRVffsao6axDztSfhCuUr3OL6Sx8e1TVpc2sbufKTgpQcZcbdOhj3IB0zfw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601152; c=relaxed/simple; bh=GuhTB/3KTzCmSyJpKU3o2obXm/jl63IYW6Msbognq8o=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=AwFBZNVwZG6ecGK0dos3iYNrEFzOR8xRD7SPHpE4azn/ZpT9CLkD8b0iti69gDFFfR4iqGuBGxn/N3yfQBwotDxSAGSGUVGBuZh4JKPKJvLVePnX6mBksOQsmeEQflXRxUxhS9CYqHHXfzvDDWMrwr8c4oSuecHvrYFJz1+w7t8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=JK8a0q+c; arc=none smtp.client-ip=209.85.221.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="JK8a0q+c" Received: by mail-wr1-f49.google.com with SMTP id ffacd0b85a97d-36279cf6414so568554f8f.3 for ; Fri, 28 Jun 2024 11:59:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601146; x=1720205946; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=5Dv7BoiOnC/L/YFNzBSd09xy/6EmCsqspK46sKjijCI=; b=JK8a0q+czAihdaYDlCaqPE4di/cyJXPdECNadFL6jms4ceaLj+FeJ+jW0BJN5nonIW j3427BsmHhFwV14vUgHh9TqkZviRsxpRA5LblLDgE00ZEznSfbt+1k9DMdyWGflFiAGb 6H53i+fVlALPqj/EyiCf+mT+uEeZlZTSD7MnDGwQm4k9GydeO+gUsbJL6zMkrLZo5qEq kl024Qo7+9pcUbndJAXBobS/uE2UcbSjDJTfVXl6vAYq3UDh5YhLi1zJK/xclfBoFa5N 4izsdh8U9RsDwyLgsKE2RFRkiHLRfgWMjMl8zOIjurHWxT4cQCoDM75ltxaHbhO/j/QD aStQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601146; x=1720205946; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=5Dv7BoiOnC/L/YFNzBSd09xy/6EmCsqspK46sKjijCI=; b=i7TNhYhOzsJVoo79d89nF4tCZoOfsW214QoOojLxC9lav3ODJ4hweNGty1QTQ3j/uw GW4ErU1WtRWxH1r9aUDCpolw2ATzxz3o/o9NCNnmvap+UlQpnNz23xsDWtMAuHql/BSw mj4NwLsuej8uAS/D625QI+BGFz/7Y5gYn4UJp7jyDJ3F4GVYrj7IPvIIN5WFOYw1b7C3 yWuhFCyHiXp9CU0sl21ExmQ5whyGbHEkumQLZhLpOXUNjDQjs2lvFf+QJutYEdwbEfry MR34/4Iv3nL7+4TKoeIFkSAU7ZS3zyrerNkvtCuT7pYYhslINldteFhtjuWVa6AQOqfH kNkA== X-Forwarded-Encrypted: i=1; AJvYcCVw4CCUV1c+VclHQCRQVKXK3NydcytD+ks7r9Pq2/mJIYY0NIUdJHrEwV+iBZDUr5nLH1M2uLGNNiCzRO/mizgcIDh2T2NOzOpRVw== X-Gm-Message-State: AOJu0Yw+8AdOGj+BqsNSDao4uMQo10u8Ff4/2pWc6Nsf91V4n8geVbXz GVpb4PUS8Cq7OcvZKspeZajHlcX/OlU9pjT8U+U59BCKA/9BPNNoCugCb70ko/4= X-Google-Smtp-Source: AGHT+IGD+fYh5O9NVjLG7lyh9QyyIX/KhXwkywXLwzUcSHbXIQ/ScpEHekqaenSYHOTsG02naRyseg== X-Received: by 2002:a5d:4405:0:b0:367:4dd7:28a0 with SMTP id ffacd0b85a97d-3674dd72a2cmr4081340f8f.37.1719601145627; Fri, 28 Jun 2024 11:59:05 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:05 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:26 +0200 Subject: [PATCH RESEND libgpiod v2 07/18] bindings: glib: add tests Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-7-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=77510; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=7KjS9OCtohvbg1fQ/qG4jZL13JQM0DyG6puHfOGrzRA=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfrdRISFN140siZ54Z/t7A4et6oKJHwTClVH 84o7+yBGceJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H6wAKCRARpy6gFHHX cvtQEACpxzOht7IAZMxKgkUfdgPpIkDwgKH5y5q3CXj8+GFMln1hZChjevQAoPlFYC/zncvMMo0 bqJQ0kRHuLkC9i4usaau1fHmUXuYLpPmWj1ssKYnfSu6wwWK3Ox/U7I7BF3RWYeDuRCrgp+zQsz kZkMePxkAm1VzzGetlKBuHWyZQDKpLsYjClAMZ3yk2XpVB2/TnlaiLxHI1Iwo6HMUu6l41IBZ8c oKetG92gXgKw6Jb4hm7xRAiWLLSRp6Jd2Dn5zaPuAjy6gM8xZajeZVAGxwGmVUjqZ+Ecl6eBfWS Ti3PbzY4kDGaDYyR9iAOBJGrAEvl8DNVBd2X2VC4B6Vidup45qwc8cFrm95VzIOdikk67dFPJyw dBiaR6B8Q8NLGtR1aqLcUZLmbjuUFtVhYHqhmD+7G11QXZzjDYNMa9x2MTDeLMSOR948ZNZHjNK u2Y+C1z53o2MfMJQsUcXNYJb9rv5ihmm3/QlhnD9CqHkVlxJ6BGJwMIzhZc+6u/OB9Zmx1DegZw bsmXftFlffWzAbj1C+nRnBJE2YhBb2dvanwFPq/QmkP1WsPe/VAlOqBmyFnNtYJg6ankOHLEqPv GUbBfXICc0GHqgy9kRLeqxUU4z/RWbYSRz021QnNXxjlq5VyxdqXEuk5Q9En48x8t0oPu+bEHGe VEONo9jk7PU1aDg== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add a comprehensive set of test-cases for GLib bindings to libgpiod reusing the core test framework based around the gpio-sim module. Signed-off-by: Bartosz Golaszewski --- bindings/glib/tests/helpers.c | 12 + bindings/glib/tests/helpers.h | 139 ++++++ bindings/glib/tests/tests-chip-info.c | 55 +++ bindings/glib/tests/tests-chip.c | 183 ++++++++ bindings/glib/tests/tests-edge-event.c | 226 +++++++++ bindings/glib/tests/tests-info-event.c | 321 +++++++++++++ bindings/glib/tests/tests-line-config.c | 186 ++++++++ bindings/glib/tests/tests-line-info.c | 98 ++++ bindings/glib/tests/tests-line-request.c | 704 +++++++++++++++++++++++++++++ bindings/glib/tests/tests-line-settings.c | 252 +++++++++++ bindings/glib/tests/tests-misc.c | 88 ++++ bindings/glib/tests/tests-request-config.c | 58 +++ 12 files changed, 2322 insertions(+) diff --git a/bindings/glib/tests/helpers.c b/bindings/glib/tests/helpers.c new file mode 100644 index 0000000..d77f2b8 --- /dev/null +++ b/bindings/glib/tests/helpers.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include "helpers.h" + +GArray *gpiod_glib_test_array_from_const(gconstpointer data, gsize len, + gsize elem_size) +{ + GArray *arr = g_array_new(FALSE, TRUE, elem_size); + + return g_array_append_vals(arr, data, len); +} diff --git a/bindings/glib/tests/helpers.h b/bindings/glib/tests/helpers.h new file mode 100644 index 0000000..2eda6a7 --- /dev/null +++ b/bindings/glib/tests/helpers.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +#ifndef __GPIOD_GLIB_TEST_HELPERS_H__ +#define __GPIOD_GLIB_TEST_HELPERS_H__ + +#include +#include + +#define gpiod_glib_test_new_chip_or_fail(_path) \ + ({ \ + g_autoptr(GError) _err = NULL; \ + GPIODChip *_chip = g_gpiod_chip_new(_path, &_err); \ + g_assert_nonnull(_chip); \ + g_assert_no_error(_err); \ + gpiod_test_return_if_failed(); \ + _chip; \ + }) + +#define gpiod_glib_test_chip_get_info_or_fail(_chip) \ + ({ \ + g_autoptr(GError) _err = NULL; \ + GPIODChipInfo *_info = g_gpiod_chip_get_info(_chip, &_err); \ + g_assert_nonnull(_info); \ + g_assert_no_error(_err); \ + gpiod_test_return_if_failed(); \ + _info; \ + }) + +#define gpiod_glib_test_chip_get_line_info_or_fail(_chip, _offset) \ + ({ \ + g_autoptr(GError) _err = NULL; \ + GPIODLineInfo *_info = g_gpiod_chip_get_line_info(_chip, \ + _offset, \ + &_err); \ + g_assert_nonnull(_info); \ + g_assert_no_error(_err); \ + gpiod_test_return_if_failed(); \ + _info; \ + }) + +#define gpiod_glib_test_chip_watch_line_info_or_fail(_chip, _offset) \ + ({ \ + g_autoptr(GError) _err = NULL; \ + GPIODLineInfo *_info = g_gpiod_chip_watch_line_info(_chip, \ + _offset, \ + &_err); \ + g_assert_nonnull(_info); \ + g_assert_no_error(_err); \ + gpiod_test_return_if_failed(); \ + _info; \ + }) + +#define gpiod_glib_test_chip_unwatch_line_info_or_fail(_chip, _offset) \ + do { \ + g_autoptr(GError) _err = NULL; \ + gboolean ret = \ + g_gpiod_chip_unwatch_line_info(_chip, _offset, &_err); \ + g_assert_true(ret); \ + g_assert_no_error(_err); \ + gpiod_test_return_if_failed(); \ + } while (0) + +#define gpiod_glib_test_line_config_add_line_settings_or_fail(_config, \ + _offsets, \ + _settings) \ + do { \ + g_autoptr(GError) _err = NULL; \ + gboolean _ret = \ + g_gpiod_line_config_add_line_settings(_config, \ + _offsets,\ + _settings, \ + &_err); \ + g_assert_true(_ret); \ + g_assert_no_error(_err); \ + gpiod_test_return_if_failed(); \ + } while (0) + +#define gpiod_glib_test_line_config_get_line_settings_or_fail(_config, \ + _offset) \ + ({ \ + GPIODLineSettings *_settings = \ + g_gpiod_line_config_get_line_settings(_config, \ + _offset); \ + g_assert_nonnull(_settings); \ + gpiod_test_return_if_failed(); \ + _settings; \ + }) + +#define gpiod_glib_test_line_config_set_output_values_or_fail(_config, \ + _values) \ + do { \ + g_autoptr(GError) _err = NULL; \ + gboolean _ret = \ + g_gpiod_line_config_set_output_values(_config, \ + _values, \ + &_err); \ + g_assert_true(_ret); \ + g_assert_no_error(_err); \ + gpiod_test_return_if_failed(); \ + } while (0) + +#define gpiod_glib_test_chip_request_lines_or_fail(_chip, _req_cfg, _line_cfg) \ + ({ \ + g_autoptr(GError) _err = NULL; \ + GPIODLineRequest *_req = \ + g_gpiod_chip_request_lines(_chip, _req_cfg, \ + _line_cfg, &_err); \ + g_assert_nonnull(_req); \ + g_assert_no_error(_err); \ + gpiod_test_return_if_failed(); \ + _req; \ + }) + +#define gpiod_glib_test_request_lines_or_fail(_path, _req_cfg, _line_cfg) \ + ({ \ + g_autoptr(GPIODChip) _chip = \ + gpiod_glib_test_new_chip_or_fail(_path); \ + GPIODLineRequest *_req = \ + gpiod_glib_test_chip_request_lines_or_fail(_chip, \ + _req_cfg, \ + _line_cfg); \ + _req; \ + }) + +#define gpiod_glib_test_check_error_or_fail(_err, _domain, _code) \ + do { \ + g_assert_nonnull(_err); \ + gpiod_test_return_if_failed(); \ + g_assert_cmpint(_domain, ==, (_err)->domain); \ + g_assert_cmpint(_code, ==, (_err)->code); \ + gpiod_test_return_if_failed(); \ + } while (0) + +GArray *gpiod_glib_test_array_from_const(const gconstpointer data, gsize len, + gsize elem_size); + +#endif /* __GPIOD_GLIB_TEST_HELPERS_H__ */ + diff --git a/bindings/glib/tests/tests-chip-info.c b/bindings/glib/tests/tests-chip-info.c new file mode 100644 index 0000000..7741ccb --- /dev/null +++ b/bindings/glib/tests/tests-chip-info.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include +#include +#include + +#include "helpers.h" + +#define GPIOD_TEST_GROUP "glib/chip-info" + +GPIOD_TEST_CASE(get_name) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODChipInfo) info = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + info = gpiod_glib_test_chip_get_info_or_fail(chip); + + g_assert_cmpstr(g_gpiod_chip_info_get_name(info), ==, + g_gpiosim_chip_get_name(sim)); +} + +GPIOD_TEST_CASE(get_label) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("label", "foobar", + NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODChipInfo) info = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + info = gpiod_glib_test_chip_get_info_or_fail(chip); + + g_assert_cmpstr(g_gpiod_chip_info_get_label(info), ==, "foobar"); +} + +GPIOD_TEST_CASE(get_num_lines) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 16, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODChipInfo) info = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + info = gpiod_glib_test_chip_get_info_or_fail(chip); + + g_assert_cmpuint(g_gpiod_chip_info_get_num_lines(info), ==, 16); +} diff --git a/bindings/glib/tests/tests-chip.c b/bindings/glib/tests/tests-chip.c new file mode 100644 index 0000000..b7c5aff --- /dev/null +++ b/bindings/glib/tests/tests-chip.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include +#include +#include +#include + +#include "helpers.h" + +#define GPIOD_TEST_GROUP "glib/chip" + +GPIOD_TEST_CASE(open_chip_good) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + + chip = g_gpiod_chip_new(g_gpiosim_chip_get_dev_path(sim), &err); + g_assert_nonnull(chip); + g_assert_null(err); +} + +GPIOD_TEST_CASE(open_chip_nonexistent) +{ + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + + chip = g_gpiod_chip_new("/dev/nonexistent", &err); + g_assert_null(chip); + gpiod_glib_test_check_error_or_fail(err, G_GPIOD_ERROR, + G_GPIOD_ERR_NOENT); +} + +GPIOD_TEST_CASE(open_chip_not_a_character_device) +{ + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + + chip = g_gpiod_chip_new("/tmp", &err); + g_assert_null(chip); + gpiod_glib_test_check_error_or_fail(err, G_GPIOD_ERROR, + G_GPIOD_ERR_NOTTY); +} + +GPIOD_TEST_CASE(open_chip_not_a_gpio_device) +{ + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + + chip = g_gpiod_chip_new("/dev/null", &err); + g_assert_null(chip); + gpiod_glib_test_check_error_or_fail(err, G_GPIOD_ERROR, + G_GPIOD_ERR_NODEV); +} + +GPIOD_TEST_CASE(get_chip_path) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autoptr(GPIODChip) chip = NULL; + const gchar *path = g_gpiosim_chip_get_dev_path(sim); + + chip = gpiod_glib_test_new_chip_or_fail(path); + + g_assert_cmpstr(g_gpiod_chip_get_path(chip), ==, path); +} + +GPIOD_TEST_CASE(closed_chip) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + g_autoptr(GPIODChipInfo) info = NULL; + const gchar *path = g_gpiosim_chip_get_dev_path(sim); + + chip = gpiod_glib_test_new_chip_or_fail(path); + + g_gpiod_chip_close(chip); + + info = g_gpiod_chip_get_info(chip, &err); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_CHIP_CLOSED); + + /* Properties still work. */ + g_assert_cmpstr(g_gpiod_chip_get_path(chip), ==, path); +} + +GPIOD_TEST_CASE(find_line_bad) +{ + static const GPIOSimLineName names[] = { + { .offset = 1, .name = "foo", }, + { .offset = 2, .name = "bar", }, + { .offset = 4, .name = "baz", }, + { .offset = 5, .name = "xyz", }, + { } + }; + + g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names); + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, + "line-names", vnames, + NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + guint offset; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + g_assert_false(g_gpiod_chip_get_line_offset_from_name(chip, + "nonexistent", + &offset, &err)); + g_assert_no_error(err); +} + +GPIOD_TEST_CASE(find_line_good) +{ + static const GPIOSimLineName names[] = { + { .offset = 1, .name = "foo", }, + { .offset = 2, .name = "bar", }, + { .offset = 4, .name = "baz", }, + { .offset = 5, .name = "xyz", }, + { } + }; + + g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names); + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, + "line-names", vnames, + NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + guint offset; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + g_assert_true(g_gpiod_chip_get_line_offset_from_name(chip, "baz", + &offset, &err)); + g_assert_no_error(err); + g_assert_cmpuint(offset, ==, 4); +} + +/* Verify that for duplicated line names, the first one is returned. */ +GPIOD_TEST_CASE(find_line_duplicate) +{ + static const GPIOSimLineName names[] = { + { .offset = 1, .name = "foo", }, + { .offset = 2, .name = "baz", }, + { .offset = 4, .name = "baz", }, + { .offset = 5, .name = "xyz", }, + { } + }; + + g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names); + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, + "line-names", vnames, + NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + guint offset; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + g_assert_true(g_gpiod_chip_get_line_offset_from_name(chip, "baz", + &offset, &err)); + g_assert_no_error(err); + g_assert_cmpuint(offset, ==, 2); +} + +GPIOD_TEST_CASE(find_line_null_name) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + guint offset; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + g_assert_false(g_gpiod_chip_get_line_offset_from_name(chip, NULL, + &offset, &err)); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} diff --git a/bindings/glib/tests/tests-edge-event.c b/bindings/glib/tests/tests-edge-event.c new file mode 100644 index 0000000..fbebcd3 --- /dev/null +++ b/bindings/glib/tests/tests-edge-event.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include +#include + +#include "helpers.h" + +#define GPIOD_TEST_GROUP "glib/edge-event" + +static gpointer falling_and_rising_edge_events(gpointer data) +{ + GPIOSimChip *sim = data; + + g_usleep(1000); + + g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_UP); + + g_usleep(1000); + + g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_DOWN); + + return NULL; +} + +typedef struct { + gboolean rising; + gboolean falling; + gboolean failed; + guint64 falling_ts; + guint64 rising_ts; + guint falling_offset; + guint rising_offset; +} EdgeEventCallbackData; + +static void on_edge_event(GPIODLineRequest *request G_GNUC_UNUSED, + GPIODEdgeEvent *event, gpointer data) +{ + EdgeEventCallbackData *cb_data = data; + + if (g_gpiod_edge_event_get_event_type(event) == + G_GPIOD_EDGE_EVENT_FALLING_EDGE) { + cb_data->falling = TRUE; + cb_data->falling_ts = + g_gpiod_edge_event_get_timestamp_ns(event); + cb_data->falling_offset = + g_gpiod_edge_event_get_line_offset(event); + } else { + cb_data->rising = TRUE; + cb_data->rising_ts = + g_gpiod_edge_event_get_timestamp_ns(event); + cb_data->rising_offset = + g_gpiod_edge_event_get_line_offset(event); + } +} + +static gboolean on_timeout(gpointer data) +{ + EdgeEventCallbackData *cb_data = data; + + g_test_fail_printf("timeout while waiting for edge events"); + cb_data->failed = TRUE; + + return G_SOURCE_CONTINUE; +} + +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(GPIODChip) chip = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GThread) thread = NULL; + EdgeEventCallbackData cb_data = { }; + guint timeout_id; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, + "edge-detection", G_GPIOD_LINE_EDGE_BOTH, NULL); + config = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(config, offsets, + settings); + + request = gpiod_glib_test_chip_request_lines_or_fail(chip, NULL, + config); + + g_signal_connect(request, "edge-event", + G_CALLBACK(on_edge_event), &cb_data); + timeout_id = g_timeout_add_seconds(5, on_timeout, &cb_data); + + thread = g_thread_new("rising-falling-edge-events", + falling_and_rising_edge_events, sim); + g_thread_ref(thread); + + while (!cb_data.failed && (!cb_data.falling || !cb_data.rising)) + g_main_context_iteration(NULL, TRUE); + + g_source_remove(timeout_id); + g_thread_join(thread); + + g_assert_cmpuint(cb_data.falling_ts, >, cb_data.rising_ts); + g_assert_cmpuint(cb_data.falling_offset, ==, offset); + g_assert_cmpuint(cb_data.rising_offset, ==, offset); +} + +typedef struct { + gboolean failed; + gboolean first; + gboolean second; + guint first_offset; + guint second_offset; + gulong first_line_seqno; + gulong second_line_seqno; + gulong first_global_seqno; + gulong second_global_seqno; +} SeqnoCallbackData; + +static void on_seqno_edge_event(GPIODLineRequest *request G_GNUC_UNUSED, + GPIODEdgeEvent *event, gpointer data) +{ + SeqnoCallbackData *cb_data = data; + + if (!cb_data->first) { + cb_data->first_offset = + g_gpiod_edge_event_get_line_offset(event); + cb_data->first_line_seqno = + g_gpiod_edge_event_get_line_seqno(event); + cb_data->first_global_seqno = + g_gpiod_edge_event_get_global_seqno(event); + cb_data->first = TRUE; + } else { + cb_data->second_offset = + g_gpiod_edge_event_get_line_offset(event); + cb_data->second_line_seqno = + g_gpiod_edge_event_get_line_seqno(event); + cb_data->second_global_seqno = + g_gpiod_edge_event_get_global_seqno(event); + cb_data->second = TRUE; + } +} + +static gpointer rising_edge_events_on_two_offsets(gpointer data) +{ + GPIOSimChip *sim = data; + + g_usleep(1000); + + g_gpiosim_chip_set_pull(sim, 2, G_GPIOSIM_PULL_UP); + + g_usleep(1000); + + g_gpiosim_chip_set_pull(sim, 3, G_GPIOSIM_PULL_UP); + + return NULL; +} + +static gboolean on_seqno_timeout(gpointer data) +{ + SeqnoCallbackData *cb_data = data; + + g_test_fail_printf("timeout while waiting for edge events"); + cb_data->failed = TRUE; + + return G_SOURCE_CONTINUE; +} + +GPIOD_TEST_CASE(seqno) +{ + static const guint offset_vals[] = { 2, 3 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GThread) thread = NULL; + SeqnoCallbackData cb_data = { }; + guint timeout_id; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, + "edge-detection", G_GPIOD_LINE_EDGE_BOTH, NULL); + config = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(offset_vals, 2, + sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(config, offsets, + settings); + + request = gpiod_glib_test_chip_request_lines_or_fail(chip, NULL, + config); + g_signal_connect(request, "edge-event", + G_CALLBACK(on_seqno_edge_event), &cb_data); + + timeout_id = g_timeout_add_seconds(5, on_seqno_timeout, &cb_data); + + thread = g_thread_new("two-rising-edge-events", + rising_edge_events_on_two_offsets, sim); + g_thread_ref(thread); + + while (!cb_data.failed && (!cb_data.first || !cb_data.second)) + g_main_context_iteration(NULL, TRUE); + + g_source_remove(timeout_id); + g_thread_join(thread); + + g_assert_cmpuint(cb_data.first_offset, ==, 2); + g_assert_cmpuint(cb_data.second_offset, ==, 3); + g_assert_cmpuint(cb_data.first_line_seqno, ==, 1); + g_assert_cmpuint(cb_data.second_line_seqno, ==, 1); + g_assert_cmpuint(cb_data.first_global_seqno, ==, 1); + g_assert_cmpuint(cb_data.second_global_seqno, ==, 2); +} diff --git a/bindings/glib/tests/tests-info-event.c b/bindings/glib/tests/tests-info-event.c new file mode 100644 index 0000000..1884e0b --- /dev/null +++ b/bindings/glib/tests/tests-info-event.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include +#include + +#include "helpers.h" + +#define GPIOD_TEST_GROUP "glib/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(GPIODChip) chip = NULL; + g_autoptr(GPIODLineInfo) info = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + info = gpiod_glib_test_chip_watch_line_info_or_fail(chip, 3); + g_assert_cmpuint(g_gpiod_line_info_get_offset(info), ==, 3); +} + +GPIOD_TEST_CASE(try_offset_of_out_range) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineInfo) info = NULL; + g_autoptr(GError) err = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + info = g_gpiod_chip_watch_line_info(chip, 11, &err); + g_assert_null(info); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} + +static void on_bad_info_event(GPIODChip *chip G_GNUC_UNUSED, + GPIODInfoEvent *event G_GNUC_UNUSED, + gpointer data G_GNUC_UNUSED) +{ + g_test_fail_printf("unexpected info event received"); +} + +static gboolean on_expected_timeout(gpointer data) +{ + gboolean *done = data; + + *done = TRUE; + + return G_SOURCE_REMOVE; +} + +GPIOD_TEST_CASE(event_timeout) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineInfo) info = NULL; + gboolean done = FALSE; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + g_signal_connect(chip, "info-event", + G_CALLBACK(on_bad_info_event), NULL); + g_timeout_add(100, on_expected_timeout, &done); + + info = gpiod_glib_test_chip_watch_line_info_or_fail(chip, 3); + + while (!done && !g_test_failed()) + g_main_context_iteration(NULL, TRUE); +} + +typedef struct { + const gchar *chip_path; + guint offset; +} RequestContext; + +typedef struct { + GPtrArray *events; + guint done; + gboolean failed; +} EventContext; + +static gpointer request_reconfigure_release_line(gpointer data) +{ + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + RequestContext *ctx = data; + gboolean ret; + + chip = g_gpiod_chip_new(ctx->chip_path, &err); + g_assert_no_error(err); + if (g_test_failed()) + return NULL; + + offsets = gpiod_glib_test_array_from_const(&ctx->offset, 1, + sizeof(guint)); + config = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new(NULL); + + ret = g_gpiod_line_config_add_line_settings(config, offsets, + settings, &err); + g_assert_true(ret); + g_assert_no_error(err); + if (g_test_failed()) + return NULL; + + g_usleep(1000); + + request = g_gpiod_chip_request_lines(chip, NULL, config, &err); + g_assert_nonnull(request); + g_assert_no_error(err); + + g_usleep(1000); + + g_gpiod_line_config_reset(config); + g_gpiod_line_settings_set_direction(settings, + G_GPIOD_LINE_DIRECTION_OUTPUT); + ret = g_gpiod_line_config_add_line_settings(config, offsets, + settings, &err); + g_assert_true(ret); + g_assert_no_error(err); + if (g_test_failed()) + return NULL; + + ret = g_gpiod_line_request_reconfigure_lines(request, config, &err); + g_assert_true(ret); + g_assert_no_error(err); + if (g_test_failed()) + return NULL; + + g_usleep(1000); + + g_gpiod_line_request_release(request); + + return NULL; +} + +static void basic_on_info_event(GPIODChip *chip G_GNUC_UNUSED, + GPIODInfoEvent *event, gpointer data) +{ + EventContext *ctx = data; + + g_ptr_array_add(ctx->events, g_object_ref(event)); + ctx->done++; +} + +static gboolean on_timeout(gpointer data) +{ + gboolean *failed = data; + + g_test_fail_printf("wait for info event timed out"); + *failed = TRUE; + + return G_SOURCE_CONTINUE; +} + +GPIOD_TEST_CASE(request_reconfigure_release_events) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineInfo) info = NULL; + g_autoptr(GPtrArray) events = NULL; + g_autoptr(GThread) thread = NULL; + const gchar *chip_path = g_gpiosim_chip_get_dev_path(sim); + GPIODInfoEvent *req_ev, *reconf_ev, *rel_ev; + guint64 req_ts, reconf_ts, rel_ts; + EventContext ev_ctx; + RequestContext req_ctx; + guint timeout_id; + + events = g_ptr_array_new_full(3, g_object_unref); + + chip = gpiod_glib_test_new_chip_or_fail(chip_path); + g_signal_connect(chip, "info-event", G_CALLBACK(basic_on_info_event), + &ev_ctx); + timeout_id = g_timeout_add_seconds(5, on_timeout, &ev_ctx.failed); + + info = gpiod_glib_test_chip_watch_line_info_or_fail(chip, 3); + + g_assert_false(g_gpiod_line_info_is_used(info)); + + req_ctx.chip_path = chip_path; + req_ctx.offset = 3; + + thread = g_thread_new("request-reconfigure-release", + request_reconfigure_release_line, &req_ctx); + g_thread_ref(thread); + + ev_ctx.done = 0; + ev_ctx.failed = FALSE; + ev_ctx.events = events; + + while (ev_ctx.done != 3 && !ev_ctx.failed) + g_main_context_iteration(NULL, TRUE); + + g_source_remove(timeout_id); + g_thread_join(thread); + + req_ev = g_ptr_array_index(events, 0); + reconf_ev = g_ptr_array_index(events, 1); + rel_ev = g_ptr_array_index(events, 2); + + g_assert_cmpint(g_gpiod_info_event_get_event_type(req_ev), ==, + G_GPIOD_INFO_EVENT_LINE_REQUESTED); + g_assert_cmpint(g_gpiod_info_event_get_event_type(reconf_ev), ==, + G_GPIOD_INFO_EVENT_LINE_CONFIG_CHANGED); + g_assert_cmpint(g_gpiod_info_event_get_event_type(rel_ev), ==, + G_GPIOD_INFO_EVENT_LINE_RELEASED); + + req_ts = g_gpiod_info_event_get_timestamp_ns(req_ev); + reconf_ts = g_gpiod_info_event_get_timestamp_ns(reconf_ev); + rel_ts = g_gpiod_info_event_get_timestamp_ns(rel_ev); + + g_assert_cmpuint(req_ts, <, reconf_ts); + g_assert_cmpuint(reconf_ts, <, rel_ts); +} + +static void unwatch_on_info_event(GPIODChip *chip G_GNUC_UNUSED, + GPIODInfoEvent *event G_GNUC_UNUSED, + gpointer data) +{ + gboolean *got_event = data; + + *got_event = TRUE; +} + +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(GPIODChip) chip = NULL; + g_autoptr(GPIODLineInfo) info = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GArray) offsets = NULL; + gboolean got_event = FALSE; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + g_signal_connect(chip, "info-event", G_CALLBACK(unwatch_on_info_event), + &got_event); + + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + config = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new(NULL); + + gpiod_glib_test_line_config_add_line_settings_or_fail(config, offsets, + settings); + + info = gpiod_glib_test_chip_watch_line_info_or_fail(chip, offset); + + request = gpiod_glib_test_chip_request_lines_or_fail(chip, NULL, + config); + + g_main_context_iteration(NULL, TRUE); + + g_assert_true(got_event); + + gpiod_glib_test_chip_unwatch_line_info_or_fail(chip, offset); + + got_event = FALSE; + g_gpiod_line_request_release(request); + + g_main_context_iteration(NULL, TRUE); + + g_assert_false(got_event); +} + +static void check_line_info_on_info_event(GPIODChip *chip G_GNUC_UNUSED, + GPIODInfoEvent *event, gpointer data) +{ + GPIODLineInfo **info = data; + + *info = g_gpiod_info_event_get_line_info(event); +} + +GPIOD_TEST_CASE(info_event_contains_new_line_info) +{ + static const guint offset = 3; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineInfo) initial_info = NULL; + g_autoptr(GPIODLineInfo) event_info = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GArray) offsets = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + g_signal_connect(chip, "info-event", + G_CALLBACK(check_line_info_on_info_event), + &event_info); + + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + config = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new(NULL); + + gpiod_glib_test_line_config_add_line_settings_or_fail(config, offsets, + settings); + + initial_info = gpiod_glib_test_chip_watch_line_info_or_fail(chip, + offset); + + request = gpiod_glib_test_chip_request_lines_or_fail(chip, NULL, + config); + + g_main_context_iteration(NULL, TRUE); + + g_assert_nonnull(event_info); +} diff --git a/bindings/glib/tests/tests-line-config.c b/bindings/glib/tests/tests-line-config.c new file mode 100644 index 0000000..cbdf589 --- /dev/null +++ b/bindings/glib/tests/tests-line-config.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include +#include + +#include "helpers.h" + +#define GPIOD_TEST_GROUP "glib/line-config" + +GPIOD_TEST_CASE(too_many_lines) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + guint i; + + settings = g_gpiod_line_settings_new(NULL); + config = g_gpiod_line_config_new(); + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + + for (i = 0; i < 65; i++) + g_array_append_val(offsets, i); + + ret = g_gpiod_line_config_add_line_settings(config, offsets, + settings, &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_E2BIG); +} + +GPIOD_TEST_CASE(get_line_settings) +{ + static const guint offset_vals[] = { 0, 1, 2, 3 }; + + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineSettings) retrieved = NULL; + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GArray) offsets = NULL; + + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, + "bias", G_GPIOD_LINE_BIAS_PULL_DOWN, + NULL); + config = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(offset_vals, 4, + sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(config, offsets, + settings); + + retrieved = gpiod_glib_test_line_config_get_line_settings_or_fail( + config, 2); + g_assert_cmpint(g_gpiod_line_settings_get_direction(retrieved), ==, + G_GPIOD_LINE_DIRECTION_INPUT); + g_assert_cmpint(g_gpiod_line_settings_get_bias(retrieved), ==, + G_GPIOD_LINE_BIAS_PULL_DOWN); +} + +GPIOD_TEST_CASE(null_settings) +{ + static const guint offset_vals[] = { 0, 1, 2, 3 }; + + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GArray) offsets = NULL; + + config = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(offset_vals, 4, + sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(config, offsets, + NULL); + + settings = gpiod_glib_test_line_config_get_line_settings_or_fail(config, + 2); + + g_assert_cmpint(g_gpiod_line_settings_get_drive(settings), ==, + G_GPIOD_LINE_DIRECTION_AS_IS); +} + +GPIOD_TEST_CASE(null_offsets) +{ + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + settings = g_gpiod_line_settings_new(NULL); + config = g_gpiod_line_config_new(); + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + + ret = g_gpiod_line_config_add_line_settings(config, NULL, settings, + &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} + +GPIOD_TEST_CASE(zero_offsets) +{ + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + settings = g_gpiod_line_settings_new(NULL); + config = g_gpiod_line_config_new(); + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + + ret = g_gpiod_line_config_add_line_settings(config, offsets, settings, + &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} + +GPIOD_TEST_CASE(set_global_output_values) +{ + static const guint offset_vals[] = { 0, 1, 2, 3 }; + static const GPIODLineValue output_values[] = { + G_GPIOD_LINE_VALUE_ACTIVE, + G_GPIOD_LINE_VALUE_INACTIVE, + G_GPIOD_LINE_VALUE_ACTIVE, + G_GPIOD_LINE_VALUE_INACTIVE, + }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 4, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_OUTPUT, NULL); + config = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(offset_vals, 4, + sizeof(guint)); + values = gpiod_glib_test_array_from_const(output_values, 4, + sizeof(GPIODLineValue)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(config, offsets, + settings); + gpiod_glib_test_line_config_set_output_values_or_fail(config, values); + + request = gpiod_glib_test_chip_request_lines_or_fail(chip, NULL, + config); + + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 0), ==, + G_GPIOSIM_VALUE_ACTIVE); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 1), ==, + G_GPIOSIM_VALUE_INACTIVE); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 2), ==, + G_GPIOSIM_VALUE_ACTIVE); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 3), ==, + G_GPIOSIM_VALUE_INACTIVE); +} + +GPIOD_TEST_CASE(handle_duplicate_offsets) +{ + static const guint offset_vals[] = { 0, 2, 2, 3 }; + + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) retrieved = NULL; + + config = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(offset_vals, 4, + sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(config, offsets, + NULL); + + retrieved = g_gpiod_line_config_get_configured_offsets(config); + g_assert_cmpuint(retrieved->len, ==, 3); + g_assert_cmpuint(g_array_index(retrieved, guint, 0), ==, 0); + g_assert_cmpuint(g_array_index(retrieved, guint, 1), ==, 2); + g_assert_cmpuint(g_array_index(retrieved, guint, 2), ==, 3); +} diff --git a/bindings/glib/tests/tests-line-info.c b/bindings/glib/tests/tests-line-info.c new file mode 100644 index 0000000..c132c06 --- /dev/null +++ b/bindings/glib/tests/tests-line-info.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include +#include + +#include "helpers.h" + +#define GPIOD_TEST_GROUP "glib/line-info" + +GPIOD_TEST_CASE(get_line_info_good) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineInfo) info = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + info = gpiod_glib_test_chip_get_line_info_or_fail(chip, 3); + + g_assert_cmpuint(g_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(GPIODChip) chip = NULL; + g_autoptr(GPIODLineInfo) info = NULL; + g_autoptr(GError) err = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + info = g_gpiod_chip_get_line_info(chip, 8, &err); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} + +GPIOD_TEST_CASE(line_info_basic_properties) +{ + static const GPIOSimLineName names[] = { + { .offset = 1, .name = "foo", }, + { .offset = 2, .name = "bar", }, + { .offset = 4, .name = "baz", }, + { .offset = 5, .name = "xyz", }, + { } + }; + + static const GPIOSimHog hogs[] = { + { + .offset = 3, + .name = "hog3", + .direction = G_GPIOSIM_DIRECTION_OUTPUT_HIGH, + }, + { + .offset = 4, + .name = "hog4", + .direction = G_GPIOSIM_DIRECTION_OUTPUT_LOW, + }, + { } + }; + + g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names); + g_autoptr(GVariant) vhogs = g_gpiosim_package_hogs(hogs); + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, + "line-names", vnames, + "hogs", vhogs, + NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineInfo) info4 = NULL; + g_autoptr(GPIODLineInfo) info6 = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + info4 = gpiod_glib_test_chip_get_line_info_or_fail(chip, 4); + info6 = gpiod_glib_test_chip_get_line_info_or_fail(chip, 6); + + g_assert_cmpuint(g_gpiod_line_info_get_offset(info4), ==, 4); + g_assert_cmpstr(g_gpiod_line_info_get_name(info4), ==, "baz"); + g_assert_cmpstr(g_gpiod_line_info_get_consumer(info4), ==, "hog4"); + g_assert_true(g_gpiod_line_info_is_used(info4)); + g_assert_cmpint(g_gpiod_line_info_get_direction(info4), ==, + G_GPIOD_LINE_DIRECTION_OUTPUT); + g_assert_cmpint(g_gpiod_line_info_get_edge_detection(info4), ==, + G_GPIOD_LINE_EDGE_NONE); + g_assert_false(g_gpiod_line_info_is_active_low(info4)); + g_assert_cmpint(g_gpiod_line_info_get_bias(info4), ==, + G_GPIOD_LINE_BIAS_UNKNOWN); + g_assert_cmpint(g_gpiod_line_info_get_drive(info4), ==, + G_GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_cmpint(g_gpiod_line_info_get_event_clock(info4), ==, + G_GPIOD_LINE_CLOCK_MONOTONIC); + g_assert_false(g_gpiod_line_info_is_debounced(info4)); + g_assert_cmpuint(g_gpiod_line_info_get_debounce_period_us(info4), ==, + 0); +} diff --git a/bindings/glib/tests/tests-line-request.c b/bindings/glib/tests/tests-line-request.c new file mode 100644 index 0000000..ae13c38 --- /dev/null +++ b/bindings/glib/tests/tests-line-request.c @@ -0,0 +1,704 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include +#include + +#include "helpers.h" + +#define GPIOD_TEST_GROUP "glib/line-request" + +GPIOD_TEST_CASE(request_fails_with_no_offsets) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GError) err = NULL; + + line_cfg = g_gpiod_line_config_new(); + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + request = g_gpiod_chip_request_lines(chip, NULL, line_cfg, &err); + g_assert_null(request); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} + +GPIOD_TEST_CASE(request_fails_with_no_line_config) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GError) err = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + request = g_gpiod_chip_request_lines(chip, NULL, NULL, &err); + g_assert_null(request); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} + +GPIOD_TEST_CASE(set_consumer) +{ + static const gchar *const consumer = "foobar"; + static const guint offset = 2; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODRequestConfig) req_cfg = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GPIODLineInfo) info = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + req_cfg = g_gpiod_request_config_new("consumer", consumer, NULL); + line_cfg = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, NULL); + + request = gpiod_glib_test_chip_request_lines_or_fail(chip, req_cfg, + line_cfg); + + info = gpiod_glib_test_chip_get_line_info_or_fail(chip, offset); + g_assert_cmpstr(g_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", 8, NULL); + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GPIODLineInfo) info = NULL; + + chip = gpiod_glib_test_new_chip_or_fail( + g_gpiosim_chip_get_dev_path(sim)); + + line_cfg = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, NULL); + + request = gpiod_glib_test_chip_request_lines_or_fail(chip, NULL, + line_cfg); + + info = gpiod_glib_test_chip_get_line_info_or_fail(chip, offset); + g_assert_cmpstr(g_gpiod_line_info_get_consumer(info), ==, "?"); +} + +GPIOD_TEST_CASE(get_requested_offsets) +{ + static const guint offset_vals[] = { 2, 1, 6, 4 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) retrieved = NULL; + + line_cfg = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(offset_vals, 4, + sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, NULL); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + retrieved = g_gpiod_line_request_get_requested_offsets(request); + g_assert_cmpuint(retrieved->len, ==, 4); + g_assert_cmpuint(g_array_index(retrieved, guint, 0), ==, 2); + g_assert_cmpuint(g_array_index(retrieved, guint, 1), ==, 1); + g_assert_cmpuint(g_array_index(retrieved, guint, 2), ==, 6); + g_assert_cmpuint(g_array_index(retrieved, guint, 3), ==, 4); +} + +GPIOD_TEST_CASE(released_request_cannot_be_used_reconfigure) +{ + static const guint offset = 3; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + line_cfg = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, NULL); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + g_gpiod_line_request_release(request); + + ret = g_gpiod_line_request_reconfigure_lines(request, line_cfg, &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_REQUEST_RELEASED); +} + +GPIOD_TEST_CASE(released_request_cannot_be_used_get_value) +{ + static const guint offset = 3; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + GPIODLineValue value; + gboolean ret; + + line_cfg = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, NULL); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, NULL); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + g_gpiod_line_request_release(request); + + ret = g_gpiod_line_request_get_value(request, offset, &value, &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_REQUEST_RELEASED); + + g_clear_pointer(&err, g_error_free); + + ret = g_gpiod_line_request_get_values(request, &values, &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_REQUEST_RELEASED); +} + +GPIOD_TEST_CASE(released_request_cannot_be_used_set_value) +{ + static const guint offset = 3; + static const GPIODLineValue value = G_GPIOD_LINE_VALUE_ACTIVE; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + line_cfg = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_OUTPUT, NULL); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, NULL); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + g_gpiod_line_request_release(request); + + ret = g_gpiod_line_request_set_value(request, offset, value, &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_REQUEST_RELEASED); + + g_clear_pointer(&err, g_error_free); + + values = gpiod_glib_test_array_from_const(&value, 1, sizeof(value)); + ret = g_gpiod_line_request_set_values(request, values, &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_REQUEST_RELEASED); +} + +GPIOD_TEST_CASE(reconfigure_lines) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GError) err = NULL; + guint offset_vals[2]; + gboolean ret; + + line_cfg = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_OUTPUT, + "output-value", G_GPIOD_LINE_VALUE_ACTIVE, + NULL); + + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + offset_vals[0] = 0; + offset_vals[1] = 2; + g_array_append_vals(offsets, offset_vals, 2); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + g_free(g_array_steal(offsets, NULL)); + + g_gpiod_line_settings_set_output_value(settings, + G_GPIOD_LINE_VALUE_INACTIVE); + offset_vals[0] = 1; + offset_vals[1] = 3; + g_array_append_vals(offsets, offset_vals, 2); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + g_free(g_array_steal(offsets, NULL)); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 0), ==, + G_GPIOSIM_VALUE_ACTIVE); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 1), ==, + G_GPIOSIM_VALUE_INACTIVE); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 2), ==, + G_GPIOSIM_VALUE_ACTIVE); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 3), ==, + G_GPIOSIM_VALUE_INACTIVE); + + g_gpiod_line_config_reset(line_cfg); + + g_gpiod_line_settings_set_output_value(settings, + G_GPIOD_LINE_VALUE_INACTIVE); + offset_vals[0] = 0; + offset_vals[1] = 2; + g_array_append_vals(offsets, offset_vals, 2); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + g_free(g_array_steal(offsets, NULL)); + + g_gpiod_line_settings_set_output_value(settings, + G_GPIOD_LINE_VALUE_ACTIVE); + offset_vals[0] = 1; + offset_vals[1] = 3; + g_array_append_vals(offsets, offset_vals, 2); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + + ret = g_gpiod_line_request_reconfigure_lines(request, line_cfg, &err); + g_assert_true(ret); + g_assert_no_error(err); + gpiod_test_return_if_failed(); + + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 0), ==, + G_GPIOSIM_VALUE_INACTIVE); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 1), ==, + G_GPIOSIM_VALUE_ACTIVE); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 2), ==, + G_GPIOSIM_VALUE_INACTIVE); + g_assert_cmpint(g_gpiosim_chip_get_value(sim, 3), ==, + G_GPIOSIM_VALUE_ACTIVE); +} + +GPIOD_TEST_CASE(reconfigure_fails_without_config) +{ + static const guint offset = 3; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + line_cfg = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, NULL); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + ret = g_gpiod_line_request_reconfigure_lines(request, NULL, &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} + +GPIOD_TEST_CASE(reconfigure_with_different_offsets) +{ + static const guint offsets0[] = { 0, 1, 2, 3 }; + static const guint offsets1[] = { 2, 4, 5 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + line_cfg = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(offsets0, 4, sizeof(guint)); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, NULL); + g_free(g_array_steal(offsets, NULL)); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + g_gpiod_line_config_reset(line_cfg); + + g_array_append_vals(offsets, offsets1, 3); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, NULL); + + ret = g_gpiod_line_request_reconfigure_lines(request, line_cfg, &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} + +GPIOD_TEST_CASE(read_one_value) +{ + static const guint offset_vals[] = { 0, 2, 4 }; + static const gint pulls[] = { 0, 1, 0 }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GError) err = NULL; + GPIODLineValue value; + gboolean ret; + guint i; + + line_cfg = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, NULL); + offsets = gpiod_glib_test_array_from_const(offset_vals, 3, + sizeof(guint)); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + for (i = 0; i < 3; i++) + g_gpiosim_chip_set_pull(sim, offset_vals[i], + pulls[i] ? G_GPIOSIM_PULL_UP : + G_GPIOSIM_PULL_DOWN); + + ret = g_gpiod_line_request_get_value(request, 2, &value, &err); + g_assert_true(ret); + g_assert_no_error(err); + gpiod_test_return_if_failed(); + g_assert_cmpint(value, ==, G_GPIOD_LINE_VALUE_ACTIVE); +} + +GPIOD_TEST_CASE(read_all_values_null_array) +{ + static const guint offset_vals[] = { 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(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + guint i; + + line_cfg = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, NULL); + offsets = gpiod_glib_test_array_from_const(offset_vals, 5, + sizeof(guint)); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + for (i = 0; i < 5; i++) + g_gpiosim_chip_set_pull(sim, offset_vals[i], + pulls[i] ? G_GPIOSIM_PULL_UP : + G_GPIOSIM_PULL_DOWN); + + ret = g_gpiod_line_request_get_values(request, &values, &err); + g_assert_true(ret); + g_assert_no_error(err); + gpiod_test_return_if_failed(); + + g_assert_cmpuint(values->len, ==, 5); + + for (i = 0; i < 5; i++) + g_assert_cmpint(g_array_index(values, GPIODLineValue, i), ==, + pulls[i]); +} + +GPIOD_TEST_CASE(read_all_values_preallocated_array) +{ + static const guint offset_vals[] = { 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(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + guint i; + + line_cfg = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, NULL); + offsets = gpiod_glib_test_array_from_const(offset_vals, 5, + sizeof(guint)); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + for (i = 0; i < 5; i++) + g_gpiosim_chip_set_pull(sim, offset_vals[i], + pulls[i] ? G_GPIOSIM_PULL_UP : + G_GPIOSIM_PULL_DOWN); + + values = g_array_new(FALSE, TRUE, sizeof(GPIODLineValue)); + g_array_set_size(values, 5); + + ret = g_gpiod_line_request_get_values(request, &values, &err); + g_assert_true(ret); + g_assert_no_error(err); + gpiod_test_return_if_failed(); + + g_assert_cmpuint(values->len, ==, 5); + + for (i = 0; i < 5; i++) + g_assert_cmpint(g_array_index(values, GPIODLineValue, i), ==, + pulls[i]); +} + +GPIOD_TEST_CASE(set_one_value) +{ + static const guint offset = 4; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + line_cfg = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_OUTPUT, + "output-value", G_GPIOD_LINE_VALUE_INACTIVE, + NULL); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + g_assert_cmpuint(g_gpiosim_chip_get_value(sim, offset), ==, + G_GPIOSIM_VALUE_INACTIVE); + + ret = g_gpiod_line_request_set_value(request, 4, + G_GPIOD_LINE_VALUE_ACTIVE, &err); + g_assert_true(ret); + g_assert_no_error(err); + + g_assert_cmpuint(g_gpiosim_chip_get_value(sim, offset), ==, + G_GPIOSIM_VALUE_ACTIVE); +} + +GPIOD_TEST_CASE(set_all_values) +{ + static const guint offset_vals[] = { 0, 2, 4, 5, 6 }; + static const GPIODLineValue value_vals[] = { + G_GPIOD_LINE_VALUE_ACTIVE, + G_GPIOD_LINE_VALUE_INACTIVE, + G_GPIOD_LINE_VALUE_ACTIVE, + G_GPIOD_LINE_VALUE_ACTIVE, + G_GPIOD_LINE_VALUE_ACTIVE + }; + static const GPIOSimValue sim_values[] = { + G_GPIOSIM_VALUE_ACTIVE, + G_GPIOSIM_VALUE_INACTIVE, + G_GPIOSIM_VALUE_ACTIVE, + G_GPIOSIM_VALUE_ACTIVE, + G_GPIOSIM_VALUE_ACTIVE + }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + guint i; + + line_cfg = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_OUTPUT, NULL); + offsets = gpiod_glib_test_array_from_const(offset_vals, 5, sizeof(guint)); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + values = gpiod_glib_test_array_from_const(value_vals, 5, + sizeof(GPIODLineValue)); + + ret = g_gpiod_line_request_set_values(request, values, &err); + g_assert_true(ret); + g_assert_no_error(err); + gpiod_test_return_if_failed(); + + for (i = 0; i < 5; i++) + g_assert_cmpint(g_gpiosim_chip_get_value(sim, offset_vals[i]), + ==, sim_values[i]); +} + +GPIOD_TEST_CASE(get_values_invalid_arguments) +{ + static const guint offset = 3; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + line_cfg = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, NULL); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(offset)); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + ret = g_gpiod_line_request_get_values_subset(request, offsets, NULL, + &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); + + g_clear_pointer(&err, g_error_free); + + ret = g_gpiod_line_request_get_values_subset(request, NULL, &values, + &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} + +GPIOD_TEST_CASE(set_values_invalid_arguments) +{ + static const guint offset = 3; + static const GPIODLineValue value_vals[] = { + G_GPIOD_LINE_VALUE_ACTIVE, + G_GPIOD_LINE_VALUE_INACTIVE, + }; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GArray) vals_inval = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + line_cfg = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_OUTPUT, NULL); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(offset)); + values = gpiod_glib_test_array_from_const(value_vals, 1, + sizeof(GPIODLineValue)); + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, + settings); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + ret = g_gpiod_line_request_set_values_subset(request, offsets, NULL, + &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); + + g_clear_pointer(&err, g_error_free); + + ret = g_gpiod_line_request_set_values_subset(request, NULL, values, + &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); + + g_clear_pointer(&err, g_error_free); + + vals_inval = gpiod_glib_test_array_from_const(value_vals, 2, + sizeof(GPIODLineValue)); + + ret = g_gpiod_line_request_set_values_subset(request, offsets, + vals_inval, &err); + g_assert_false(ret); + g_assert_error(err, G_GPIOD_ERROR, G_GPIOD_ERR_INVAL); +} + +GPIOD_TEST_CASE(get_chip_name) +{ + static const guint offset = 4; + + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GArray) offsets = NULL; + + line_cfg = g_gpiod_line_config_new(); + offsets = gpiod_glib_test_array_from_const(&offset, 1, sizeof(guint)); + + gpiod_glib_test_line_config_add_line_settings_or_fail(line_cfg, + offsets, NULL); + + request = gpiod_glib_test_request_lines_or_fail( + g_gpiosim_chip_get_dev_path(sim), NULL, line_cfg); + + g_assert_cmpstr(g_gpiosim_chip_get_name(sim), ==, + g_gpiod_line_request_get_chip_name(request)); +} diff --git a/bindings/glib/tests/tests-line-settings.c b/bindings/glib/tests/tests-line-settings.c new file mode 100644 index 0000000..ad195ba --- /dev/null +++ b/bindings/glib/tests/tests-line-settings.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include + +#include "helpers.h" + +#define GPIOD_TEST_GROUP "glib/line-settings" + +GPIOD_TEST_CASE(default_config) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new(NULL); + + g_assert_cmpint(g_gpiod_line_settings_get_direction(settings), ==, + G_GPIOD_LINE_DIRECTION_AS_IS); + g_assert_cmpint(g_gpiod_line_settings_get_edge_detection(settings), + ==, G_GPIOD_LINE_EDGE_NONE); + g_assert_cmpint(g_gpiod_line_settings_get_bias(settings), ==, + G_GPIOD_LINE_BIAS_AS_IS); + g_assert_cmpint(g_gpiod_line_settings_get_drive(settings), ==, + G_GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_false(g_gpiod_line_settings_get_active_low(settings)); + g_assert_cmpint(g_gpiod_line_settings_get_debounce_period_us(settings), + ==, 0); + g_assert_cmpint(g_gpiod_line_settings_get_event_clock(settings), ==, + G_GPIOD_LINE_CLOCK_MONOTONIC); + g_assert_cmpint(g_gpiod_line_settings_get_output_value(settings), ==, + G_GPIOD_LINE_VALUE_INACTIVE); +} + +GPIOD_TEST_CASE(set_direction) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new(NULL); + + g_gpiod_line_settings_set_direction(settings, + G_GPIOD_LINE_DIRECTION_INPUT); + g_assert_cmpint(g_gpiod_line_settings_get_direction(settings), ==, + G_GPIOD_LINE_DIRECTION_INPUT); + + g_gpiod_line_settings_set_direction(settings, + G_GPIOD_LINE_DIRECTION_AS_IS); + g_assert_cmpint(g_gpiod_line_settings_get_direction(settings), ==, + G_GPIOD_LINE_DIRECTION_AS_IS); + + g_gpiod_line_settings_set_direction(settings, + G_GPIOD_LINE_DIRECTION_OUTPUT); + g_assert_cmpint(g_gpiod_line_settings_get_direction(settings), ==, + G_GPIOD_LINE_DIRECTION_OUTPUT); +} + +GPIOD_TEST_CASE(set_edge_detection) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new(NULL); + + g_gpiod_line_settings_set_edge_detection(settings, + G_GPIOD_LINE_EDGE_BOTH); + g_assert_cmpint(g_gpiod_line_settings_get_edge_detection(settings), ==, + G_GPIOD_LINE_EDGE_BOTH); + + g_gpiod_line_settings_set_edge_detection(settings, + G_GPIOD_LINE_EDGE_NONE); + g_assert_cmpint(g_gpiod_line_settings_get_edge_detection(settings), ==, + G_GPIOD_LINE_EDGE_NONE); + + g_gpiod_line_settings_set_edge_detection(settings, + G_GPIOD_LINE_EDGE_FALLING); + g_assert_cmpint(g_gpiod_line_settings_get_edge_detection(settings), ==, + G_GPIOD_LINE_EDGE_FALLING); + + g_gpiod_line_settings_set_edge_detection(settings, + G_GPIOD_LINE_EDGE_RISING); + g_assert_cmpint(g_gpiod_line_settings_get_edge_detection(settings), ==, + G_GPIOD_LINE_EDGE_RISING); +} + +GPIOD_TEST_CASE(set_bias) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new(NULL); + + g_gpiod_line_settings_set_bias(settings, G_GPIOD_LINE_BIAS_DISABLED); + g_assert_cmpint(g_gpiod_line_settings_get_bias(settings), ==, + G_GPIOD_LINE_BIAS_DISABLED); + + g_gpiod_line_settings_set_bias(settings, G_GPIOD_LINE_BIAS_AS_IS); + g_assert_cmpint(g_gpiod_line_settings_get_bias(settings), ==, + G_GPIOD_LINE_BIAS_AS_IS); + + g_gpiod_line_settings_set_bias(settings, G_GPIOD_LINE_BIAS_PULL_DOWN); + g_assert_cmpint(g_gpiod_line_settings_get_bias(settings), ==, + G_GPIOD_LINE_BIAS_PULL_DOWN); + + g_gpiod_line_settings_set_bias(settings, G_GPIOD_LINE_BIAS_PULL_UP); + g_assert_cmpint(g_gpiod_line_settings_get_bias(settings), ==, + G_GPIOD_LINE_BIAS_PULL_UP); +} + +GPIOD_TEST_CASE(set_drive) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new(NULL); + + g_gpiod_line_settings_set_drive(settings, + G_GPIOD_LINE_DRIVE_OPEN_DRAIN); + g_assert_cmpint(g_gpiod_line_settings_get_drive(settings), ==, + G_GPIOD_LINE_DRIVE_OPEN_DRAIN); + + g_gpiod_line_settings_set_drive(settings, + G_GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_cmpint(g_gpiod_line_settings_get_drive(settings), ==, + G_GPIOD_LINE_DRIVE_PUSH_PULL); + + g_gpiod_line_settings_set_drive(settings, + G_GPIOD_LINE_DRIVE_OPEN_SOURCE); + g_assert_cmpint(g_gpiod_line_settings_get_drive(settings), ==, + G_GPIOD_LINE_DRIVE_OPEN_SOURCE); +} + +GPIOD_TEST_CASE(set_active_low) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new(NULL); + + g_gpiod_line_settings_set_active_low(settings, TRUE); + g_assert_true(g_gpiod_line_settings_get_active_low(settings)); + + g_gpiod_line_settings_set_active_low(settings, FALSE); + g_assert_false(g_gpiod_line_settings_get_active_low(settings)); +} + +GPIOD_TEST_CASE(set_debounce_period) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new(NULL); + + g_gpiod_line_settings_set_debounce_period_us(settings, 4000); + g_assert_cmpint(g_gpiod_line_settings_get_debounce_period_us(settings), + ==, 4000); +} + +GPIOD_TEST_CASE(set_event_clock) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new(NULL); + + g_gpiod_line_settings_set_event_clock(settings, + G_GPIOD_LINE_CLOCK_MONOTONIC); + g_assert_cmpint(g_gpiod_line_settings_get_event_clock(settings), ==, + G_GPIOD_LINE_CLOCK_MONOTONIC); + + g_gpiod_line_settings_set_event_clock(settings, + G_GPIOD_LINE_CLOCK_REALTIME); + g_assert_cmpint(g_gpiod_line_settings_get_event_clock(settings), ==, + G_GPIOD_LINE_CLOCK_REALTIME); + + g_gpiod_line_settings_set_event_clock(settings, + G_GPIOD_LINE_CLOCK_HTE); + g_assert_cmpint(g_gpiod_line_settings_get_event_clock(settings), ==, + G_GPIOD_LINE_CLOCK_HTE); +} + +GPIOD_TEST_CASE(set_output_value) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new(NULL); + + g_gpiod_line_settings_set_output_value(settings, + G_GPIOD_LINE_VALUE_ACTIVE); + g_assert_cmpint(g_gpiod_line_settings_get_output_value(settings), ==, + G_GPIOD_LINE_VALUE_ACTIVE); + + g_gpiod_line_settings_set_output_value(settings, + G_GPIOD_LINE_VALUE_INACTIVE); + g_assert_cmpint(g_gpiod_line_settings_get_output_value(settings), ==, + G_GPIOD_LINE_VALUE_INACTIVE); +} + +GPIOD_TEST_CASE(reset_settings) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new(NULL); + + g_gpiod_line_settings_set_direction(settings, + G_GPIOD_LINE_DIRECTION_INPUT); + g_gpiod_line_settings_set_edge_detection(settings, + G_GPIOD_LINE_EDGE_BOTH); + g_gpiod_line_settings_set_debounce_period_us(settings, 2000); + g_gpiod_line_settings_set_event_clock(settings, + G_GPIOD_LINE_CLOCK_REALTIME); + + g_gpiod_line_settings_reset(settings); + + g_assert_cmpint(g_gpiod_line_settings_get_direction(settings), ==, + G_GPIOD_LINE_DIRECTION_AS_IS); + g_assert_cmpint(g_gpiod_line_settings_get_edge_detection(settings), ==, + G_GPIOD_LINE_EDGE_NONE); + g_assert_cmpint(g_gpiod_line_settings_get_bias(settings), ==, + G_GPIOD_LINE_BIAS_AS_IS); + g_assert_cmpint(g_gpiod_line_settings_get_drive(settings), ==, + G_GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_false(g_gpiod_line_settings_get_active_low(settings)); + g_assert_cmpint(g_gpiod_line_settings_get_debounce_period_us(settings), + ==, 0); + g_assert_cmpint(g_gpiod_line_settings_get_event_clock(settings), ==, + G_GPIOD_LINE_CLOCK_MONOTONIC); + g_assert_cmpint(g_gpiod_line_settings_get_output_value(settings), ==, + G_GPIOD_LINE_VALUE_INACTIVE); +} + +GPIOD_TEST_CASE(set_props_in_constructor) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + + settings = g_gpiod_line_settings_new( + "direction", G_GPIOD_LINE_DIRECTION_INPUT, + "edge-detection", G_GPIOD_LINE_EDGE_BOTH, + "active-low", TRUE, + "debounce-period-us", (GTimeSpan)3000, + "bias", G_GPIOD_LINE_BIAS_PULL_UP, + "event-clock", G_GPIOD_LINE_CLOCK_REALTIME, + NULL); + + g_assert_cmpint(g_gpiod_line_settings_get_direction(settings), ==, + G_GPIOD_LINE_DIRECTION_INPUT); + g_assert_cmpint(g_gpiod_line_settings_get_edge_detection(settings), ==, + G_GPIOD_LINE_EDGE_BOTH); + g_assert_cmpint(g_gpiod_line_settings_get_bias(settings), ==, + G_GPIOD_LINE_BIAS_PULL_UP); + g_assert_cmpint(g_gpiod_line_settings_get_drive(settings), ==, + G_GPIOD_LINE_DRIVE_PUSH_PULL); + g_assert_true(g_gpiod_line_settings_get_active_low(settings)); + g_assert_cmpint(g_gpiod_line_settings_get_debounce_period_us(settings), + ==, 3000); + g_assert_cmpint(g_gpiod_line_settings_get_event_clock(settings), ==, + G_GPIOD_LINE_CLOCK_REALTIME); + g_assert_cmpint(g_gpiod_line_settings_get_output_value(settings), ==, + G_GPIOD_LINE_VALUE_INACTIVE); +} diff --git a/bindings/glib/tests/tests-misc.c b/bindings/glib/tests/tests-misc.c new file mode 100644 index 0000000..7db9ec2 --- /dev/null +++ b/bindings/glib/tests/tests-misc.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include +#include +#include +#include + +#define GPIOD_TEST_GROUP "glib/misc" + +GPIOD_TEST_CASE(is_gpiochip_bad) +{ + g_assert_false(g_gpiod_is_gpiochip_device("/dev/null")); + g_assert_false(g_gpiod_is_gpiochip_device("/dev/nonexistent")); +} + +GPIOD_TEST_CASE(is_gpiochip_good) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + + g_assert_true(g_gpiod_is_gpiochip_device( + g_gpiosim_chip_get_dev_path(sim))); +} + +GPIOD_TEST_CASE(is_gpiochip_link_bad) +{ + g_autofree gchar *link = NULL; + gint ret; + + link = g_strdup_printf("/tmp/gpiod-test-link.%u", getpid()); + ret = symlink("/dev/null", link); + g_assert_cmpint(ret, ==, 0); + gpiod_test_return_if_failed(); + + g_assert_false(g_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 *link = NULL; + gint ret; + + link = g_strdup_printf("/tmp/gpiod-test-link.%u", getpid()); + ret = symlink(g_gpiosim_chip_get_dev_path(sim), link); + g_assert_cmpint(ret, ==, 0); + gpiod_test_return_if_failed(); + + g_assert_true(g_gpiod_is_gpiochip_device(link)); + ret = unlink(link); + g_assert_cmpint(ret, ==, 0); +} + +GPIOD_TEST_CASE(version_string) +{ + static const gchar *const pattern = "^\\d+\\.\\d+(\\.\\d+|\\-devel|\\-rc\\d+)$"; + + g_autoptr(GError) err = NULL; + g_autoptr(GRegex) regex = NULL; + g_autoptr(GMatchInfo) match = NULL; + g_autofree gchar *res = NULL; + const gchar *ver; + gboolean ret; + + ver = g_gpiod_api_version(); + g_assert_nonnull(ver); + gpiod_test_return_if_failed(); + + regex = g_regex_new(pattern, 0, 0, &err); + g_assert_nonnull(regex); + g_assert_no_error(err); + gpiod_test_return_if_failed(); + + 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/bindings/glib/tests/tests-request-config.c b/bindings/glib/tests/tests-request-config.c new file mode 100644 index 0000000..665f2a4 --- /dev/null +++ b/bindings/glib/tests/tests-request-config.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include + +#include "helpers.h" + +#define GPIOD_TEST_GROUP "glib/request-config" + +GPIOD_TEST_CASE(default_config) +{ + g_autoptr(GPIODRequestConfig) config = NULL; + + config = g_gpiod_request_config_new(NULL); + + g_assert_null(g_gpiod_request_config_get_consumer(config)); + g_assert_cmpuint(g_gpiod_request_config_get_event_buffer_size(config), + ==, 0); +} + +GPIOD_TEST_CASE(set_consumer) +{ + g_autoptr(GPIODRequestConfig) config = NULL; + + config = g_gpiod_request_config_new(NULL); + + g_gpiod_request_config_set_consumer(config, "foobar"); + g_assert_cmpstr(g_gpiod_request_config_get_consumer(config), ==, + "foobar"); + + g_gpiod_request_config_set_consumer(config, NULL); + g_assert_null(g_gpiod_request_config_get_consumer(config)); +} + +GPIOD_TEST_CASE(set_event_buffer_size) +{ + g_autoptr(GPIODRequestConfig) config = NULL; + + config = g_gpiod_request_config_new(NULL); + + g_gpiod_request_config_set_event_buffer_size(config, 128); + g_assert_cmpuint(g_gpiod_request_config_get_event_buffer_size(config), + ==, 128); +} + +GPIOD_TEST_CASE(set_properties_in_constructor) +{ + g_autoptr(GPIODRequestConfig) config = NULL; + + config = g_gpiod_request_config_new("consumer", "foobar", + "event-buffer-size", 64, NULL); + g_assert_cmpstr(g_gpiod_request_config_get_consumer(config), ==, + "foobar"); + g_assert_cmpuint(g_gpiod_request_config_get_event_buffer_size(config), + ==, 64); +} From patchwork Fri Jun 28 18:58:27 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808531 Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3159E57CA6 for ; Fri, 28 Jun 2024 18:59:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601150; cv=none; b=YfyD6CBpbyJa4SDkdWJopRVTTqsXFjxqu2EcwATbOmRzBtpT5pfDAZ1XXo6PGHKqb05p4AcKtV/kO+krnKHsSCVvcAwm6fDTcCpdG2umTlMUkwafIoCvc9CTks8fyuo9wuYUeAdrDNSkQFP2/15aKzcMAHrxGjk45R/+7x0TGns= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601150; c=relaxed/simple; bh=NwUWEm9OUZ9vpVs1svXaVqJGdK7etUS1VgNeDtUHwwk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=AFVM9ACzj9nzqG6dUDCA+/cvM73RvtzHHF/XAkxZjxSIKp+phPbI7u5jQeoMapSpoZGv50KfIdn3vhwN+IXnA+KT+lLnf2nAM52lK4fn97ZwcAHuBDsw0Reb/9fb/7dQmGmtNneNvXtsvtvdfrMH4MfRYl7vEoqoj8gNQL529Ho= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=qKVt/g+X; arc=none smtp.client-ip=209.85.221.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="qKVt/g+X" Received: by mail-wr1-f46.google.com with SMTP id ffacd0b85a97d-3658197cdbbso517895f8f.3 for ; Fri, 28 Jun 2024 11:59:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601147; x=1720205947; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=UJJuDpMqd8L7Svo1KOrBhJ6B/x9FaW2uynzmvqKA0SE=; b=qKVt/g+XSigwWPRdazBAoU/Sjke2yhmBQVY44fl6tAi139du77c0C5uRhqr8DExqVn b+gGJHlAK0NF0T81mqyK6SgqO6iioiGx6LfqyaeuixHVjneqpNqWaS67GppqYgYH7om7 A7pXSBEw69OvfJVoDGUdqXJQA7FyZZEsHPWNL1T/4ElzjqbRGW0KuHxfbHXtdKBbFYyG h+6PR00lJ0hHi2M0FqmAfgrtj5aKBAK3iBF0zL5XcPZcFT2ghEmEWQr9WakJjvpYCuQE K/IVWJqF765j86wZmELn2wiM/QuYYqwfpjvMsTrnE6QjpJCIAk1opRRNXsmGyoPSPzSs CE0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601147; x=1720205947; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UJJuDpMqd8L7Svo1KOrBhJ6B/x9FaW2uynzmvqKA0SE=; b=ss2FowbjNgxUSoTNKJGiwXgUrnFBZsw9teX/RAvXNYYn+HysNB5cqslid+vC7luN3b 9pcA+LNkMZp3ocKpFieaCpp9GUHDbE5rjYYX4NCquYoVBOLYqzVMtdwk+AYYNoN6ZdSf 9IZF6XQzo4kLxqsqXGTTTtPnTIeDi2jm2qDXML7mYUMzWUj/xMgDaCq1U0YBCpu8T+Zr nhHMWyqSsEJXueCE/E7WqQljzhdu8DFugjTV9Kon4Dg7DU98iFkQiXuwvvW8yzKetSFz 7vt6YFqUJ3Ac3kOEXMafu43DjOUHEo7Udk/O+DkkOoh5Ny75jrDWzc9dOrIDK8aMfFY6 qltw== X-Forwarded-Encrypted: i=1; AJvYcCW+PJMmTkdLL9AVO/XzQJIA9TEgm6DFdl8N9sXLerKyvwExTp1UbLeFKZjtjM327L3IHR5eE6hoiXALfMC+kW6oqtna7NVDTjRmjw== X-Gm-Message-State: AOJu0YzfPYeO+p92AswQ0NSuWmEJj/lJp9c4Hf8B5OKuhsT4Q92S7tiJ BOcxOsRYGIcfimj+3SDe2Jn3ArdO+CiB+BwozkvXB2lx2aFOqgdAyLJDj8EOd2k= X-Google-Smtp-Source: AGHT+IFdsxncTOJ47r4n3br/b2o6Y12BM4SkNadBjQkfdDxPTXT8zcCuGwON6eij+DFkpbwEbfHEeQ== X-Received: by 2002:adf:e9ce:0:b0:367:418d:650b with SMTP id ffacd0b85a97d-367418d6850mr4212311f8f.53.1719601147725; Fri, 28 Jun 2024 11:59:07 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:06 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:27 +0200 Subject: [PATCH RESEND libgpiod v2 08/18] README: document GLib bindings Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-8-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=1222; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=phVDuEqV263LENkg5bMa3wSzLi97jDzlTD+quy69pmI=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfrMOxXYitB6nca/eM//a/6Q45z+JY9LTPxk QW7XGIhRZ2JAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H6wAKCRARpy6gFHHX coasEAC81mKOV/scB7bOLx86BABTYXEZE5ob4v3G2ophlrY9/DLegP4yQi1jabJ7X429JoO6KFJ 4RbsQNFiCtsE4glkVu401nNctUthFOmuoLe2hfVjJRiY3umTauwDuP5lr3B/0EoaSM1qAFyEbls jy363sNeVSI4Jbu7t/ANN1G65cKsROKLw0TfcINWl8wq4jKmN/bNADop0IEypD+r5707lrUN51K wLQ8lWU7VpXJZRNG438Wx1CrqzDFgsbgK2QUcd2FuUbyB0ORh9oXu5g3oMa5Lt+7y2y108DQn93 GvdvqEa0vzdZUi/lgj0xqsCLDLlORQ3xvG/yXMLbI711H9aIV2mNMRVanXaptEQ38r+oVsNpBGh 5H5zSkbsRZ9quJkAwHPcJaL8rNGQsihq+Bo35cifz30sugcPSIQhHsef0x35zhVEXSiEeP2Jnz7 oW7wZQvmLYBvGaHAk7V2mVD3BX0Zlv02KcyY4VCI2Zbqmk7KmDcTV6iGbNNqkcfYFMtJcGlfKeD ef4bHmwOORQ/3lsMTyyLbmbtZkm0EY0pB/4OFL/yN/aUh0VolavQE9u3t3wzLIZuixloA6sLBU0 g1dy6syZardvVjCEvjXgdGnk5OErGTzKdtvoO0nHjUDQGtuPAMF6YKI5xz8/s56HJCqRWrpj9uM fcbjw6P3b17v8hQ== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add information on the requirements of and the configure build switch for GLib bindings to libgpiod. Signed-off-by: Bartosz Golaszewski --- README | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README b/README index a6f24d1..ef5d328 100644 --- a/README +++ b/README @@ -206,13 +206,16 @@ Examples: BINDINGS -------- -High-level, object-oriented bindings for C++, python3 and Rust are provided. -They can be enabled by passing --enable-bindings-cxx, --enable-bindings-python -and --enable-bindings-rust arguments respectively to configure. +High-level, object-oriented bindings for C++, GLib, python3 and Rust are +provided. They can be enabled by passing --enable-bindings-cxx, +--enable-bindings-glib, --enable-bindings-python and --enable-bindings-rust +arguments respectively to configure. C++ bindings require C++11 support and autoconf-archive collection if building from git. +GLib bindings requires GLib (as well as GObject, GIO and GIO-Unix) v2.54. + Python bindings require python3 support and libpython development files. Please refer to bindings/python/README.md for more information. From patchwork Fri Jun 28 18:58:28 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808380 Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AC01B55E58 for ; Fri, 28 Jun 2024 18:59:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601152; cv=none; b=fo5oJ+QHEZgV5cFrfGxGLwtleNFT1qcSYaXELjUMrEOiNfNjYDfjatBQEzmEgc5NR6Ryd+bVPLyep9+xvAocEnC05ugRjhZnf6siu32zRJdpX7sAwfINHWdYPUS/afMpklWoHFLshJlF+ivn35/BEwWRk9lhYdVR7hgZrWRtIBk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601152; c=relaxed/simple; bh=12WLLf11vHaGYUlNSI4OwRQi/+2kHPE8Pe20WHXjABA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=lMca19nk20Ej1C+kZv6GDQXv/ZPk1OGLrhEbfQjei82W4jMl3fkPQSuAIPeKEFM0AlW7O2Yx1j9ibCjXY58hmp2I8pvGsUrg2P2o1hE+nJkUavjDR2IJtUzgthhq8A8hHoEEKPlQ0FhE9aaOxkD47eKklf1SOVr3DvP7Y70zezY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=lPjc8MU3; arc=none smtp.client-ip=209.85.128.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="lPjc8MU3" Received: by mail-wm1-f48.google.com with SMTP id 5b1f17b1804b1-424acfff613so9366015e9.0 for ; Fri, 28 Jun 2024 11:59:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601149; x=1720205949; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=fr78fWiEH3X+jXUcAWkuN6d8PZAgF7LiprLtGRT8qbU=; b=lPjc8MU3AXop+df4NyBaqUuDOmOdIg0zh28ACnNAp3xgRVlhMWzmqsI9TN+g0EaqdR ixEJOK6HMMdbTH71gSny+8UZtgs1qI9MB2ljfwWR7VgO520oqBmol6uQe0GkJO1P/rts 3Uor5EkBxw5hgAjjlPuOiFQL4Y+I6ftDF/G0UmFexcgee0xY1CeN4aD8F+vuw/NHTlrw 1+cz5yr+w7qLVasPBW5Lru78n8l0dPYqQMTMtZTh12cT2pADmUVKR8HO5SKAnvYHE1On FtuN87Me7n1K44gUnE4P9xEmhrH5w9fcShiWhNMzvbEeioYifDHfq8CsIbAenkQYvg1m 5sMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601149; x=1720205949; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=fr78fWiEH3X+jXUcAWkuN6d8PZAgF7LiprLtGRT8qbU=; b=tpytJs7Woqu5vASXrjQur91xPwYTmkMnxNbLYeT0sfeZEYQ5vX5finpdTN1yJyXaCy 7ceQK9eXjg5yIIiPHbFdPTsoFzgZmc8twPNmqLwtqOy+nnogrhOv8V8EWYUaTfe8PcnQ Odp1PCBYeYqALFp6SgpBBWBMqlkS5JOrbjI+kI8jA590BY4XsyPOs01jbKYwd/+DEcpo 4n1dNBU7FA4xpdBGqnCfVRhIyTP7zBVBf58/iNN5ixwvH3sct27rXjzI9korRSWB7vvD k8qRik66JNiawD7VamWeMJ8Vgd0QM2sB23YBQld4RbVf/bATeAaZ1XTSFnxaZK74DXEx NUmw== X-Forwarded-Encrypted: i=1; AJvYcCX8wRINR3xN4ekGWZk11zTqoP1TwNLcEqKlAO+ffOeuJSVfWWE/ZzJ2KDEqgPXn3wZPV6YoWWWA/flD/ER+VHyDBzVm3N/LoL3J/g== X-Gm-Message-State: AOJu0YxYZNwWCx9ro+MriCF/jTSuJ/LKvhEowRR5ed5XsWRjvvnM6LzQ CuMNUP9DGqv9rGyrCUWT87oKZ1TC1R802qeQXIMXZGWuZCMA9X7T8jAgam08p5g= X-Google-Smtp-Source: AGHT+IHu+IFNIB5hS7Jc6mAe7JutHHPV4EP+kiNLs8SnPWPCE1fiw7VVTZedvtcmZ5zyjdtvBf0NPQ== X-Received: by 2002:a5d:6dae:0:b0:367:4337:3b35 with SMTP id ffacd0b85a97d-36743373cabmr6837370f8f.63.1719601149219; Fri, 28 Jun 2024 11:59:09 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:08 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:28 +0200 Subject: [PATCH RESEND libgpiod v2 09/18] dbus: add build files Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-9-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=9626; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=p2WgCOijJiXb3jPeXwEP5qvNWIHa5FbcqmlFZm3DyaQ=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfsIdjRvEckmObNLsZ+3kp49iql9l8A0XtfZ ziQkv+z5IWJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H7AAKCRARpy6gFHHX cnZoD/4om8sCGABvjptb0L9O8xgR35ijhA6fO+8TIifIBjdsYYMv4aNgYhQdc4qkTANm0f6KMwv /+7h0SbkQLNUlJoVVaMJ46/6U8PJUQ2ICBFIEdzf+ypW0ECp/APct085ClAUn/bdDad0p1MqNac wdg79AlXscssw8wJUykwPIETbqTrlzJ5LkMnjSJWxQtSPT9fKn2B6DFEXYi+dvUav3FfwjIlmnJ YvR67uAu7kME+i7KlyyUbSvGCyg9TXWgMBJ1/hS21AMdu6uyfDwIEqMzfQSVJdfxfKD0+YGx7MU BCX3zQsdEFUWIDnhsydmBI2su6Gksgaar6x4kHJRPWtYD5nff/XhLEPvOdkOp1WPQp23GtuD7za RGUyiiqke6TTZ28vgpJL9vqu21oyj5Vd5yTOQ4BHK2f6A1NsPTmO+FllV/7cs9X2jJaDa1xzPjd E64D5botSgxiCEXVNIueZIQYXsKGet0GG5SSjPms57Djnj7Ct3CVb6llDyrX4nJjz7pDtIg5UjT IipRC4nHSxQWeKWxcSnEt3Uqya2awqF80zMF0O3W4Pbsx1S10+31+u0VD8FTKuHsjm/7/y3Y1zj xpNa8Mys0nrB2+X9YFKONLBgjobLDkPprwyi/f89EfmqLt799hkebp9oXmNp56hjdYiB7ctNYYw 6TPzcx8bRXILr9A== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add the directory structure and build files as well as changes to .gitignore for the DBus API. Signed-off-by: Bartosz Golaszewski --- Makefile.am | 7 +++++++ configure.ac | 45 +++++++++++++++++++++++++++++++++++++++++++++ dbus/Makefile.am | 10 ++++++++++ dbus/client/.gitignore | 4 ++++ dbus/client/Makefile.am | 31 +++++++++++++++++++++++++++++++ dbus/data/Makefile.am | 13 +++++++++++++ dbus/lib/Makefile.am | 28 ++++++++++++++++++++++++++++ dbus/manager/.gitignore | 4 ++++ dbus/manager/Makefile.am | 21 +++++++++++++++++++++ dbus/tests/.gitignore | 4 ++++ dbus/tests/Makefile.am | 25 +++++++++++++++++++++++++ 11 files changed, 192 insertions(+) diff --git a/Makefile.am b/Makefile.am index 2ace901..c824dc4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later # SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski ACLOCAL_AMFLAGS = -I m4 AUTOMAKE_OPTIONS = foreign @@ -37,6 +38,12 @@ endif # libgpiosim to be already present. SUBDIRS += bindings +if WITH_DBUS + +SUBDIRS += dbus + +endif + if HAS_DOXYGEN doc: Doxyfile diff --git a/configure.ac b/configure.ac index 74ba004..0b6a9f9 100644 --- a/configure.ac +++ b/configure.ac @@ -267,6 +267,45 @@ then AC_MSG_ERROR([glib-mkenums not found - needed to build GLib bindings])) fi +# Depends on GLib bindings so must come after +AC_ARG_ENABLE([dbus], + [AS_HELP_STRING([--enable-dbus], [build dbus daemon [default=no]])], + [if test "x$enableval" == xyes; then with_dbus=true; fi], + [with_dbus=false]) +AM_CONDITIONAL([WITH_DBUS], [test "x$with_dbus" = xtrue]) + +AC_DEFUN([FUNC_NOT_FOUND_DBUS], + [ERR_NOT_FOUND([$1()], [dbus daemon])]) + +if test "x$with_dbus" = xtrue && test "x$with_bindings_glib" != xtrue +then + AC_MSG_ERROR([GLib bindings are required to build the dbus daemon - use --enable-bindings-glib]) +fi + +if test "x$with_dbus" = xtrue +then + AC_CHECK_FUNC([daemon], [], [FUNC_NOT_FOUND_DBUS([daemon])]) + AC_CHECK_FUNC([strverscmp], [], [FUNC_NOT_FOUND_DBUS([strverscmp])]) + PKG_CHECK_MODULES([GUDEV], [gudev-1.0 >= 230]) + AC_CHECK_PROG([has_gdbus_codegen], [gdbus-codegen], [true], [false]) + if test "x$has_gdbus_codegen" = xfalse + then + AC_MSG_ERROR([gdbus-codegen not found - needed to build dbus daemon]) + fi +fi + +AC_ARG_ENABLE([systemd], + [AS_HELP_STRING([--enable-systemd], [enable systemd support [default=no]])], + [if test "x$enableval" == xyes; then with_systemd=true; fi], + [with_systemd=false]) +AM_CONDITIONAL([WITH_SYSTEMD], [test "x$with_systemd" = xtrue]) + +if test "x$with_systemd" = xtrue +then + PKG_CHECK_VAR([systemdsystemunitdir], [systemd], [systemdsystemunitdir], [], + AC_MSG_ERROR([systemdsystemunitdir not found - needed to enable systemd support])) +fi + AC_CHECK_PROG([has_doxygen], [doxygen], [true], [false]) AM_CONDITIONAL([HAS_DOXYGEN], [test "x$has_doxygen" = xtrue]) if test "x$has_doxygen" = xfalse @@ -325,6 +364,12 @@ AC_CONFIG_FILES([Makefile bindings/rust/Makefile bindings/rust/gpiosim-sys/src/Makefile bindings/rust/gpiosim-sys/Makefile + dbus/Makefile + dbus/client/Makefile + dbus/data/Makefile + dbus/lib/Makefile + dbus/manager/Makefile + dbus/tests/Makefile man/Makefile]) AC_OUTPUT diff --git a/dbus/Makefile.am b/dbus/Makefile.am new file mode 100644 index 0000000..7868a96 --- /dev/null +++ b/dbus/Makefile.am @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +SUBDIRS = data lib manager client + +if WITH_TESTS + +SUBDIRS += tests + +endif diff --git a/dbus/client/.gitignore b/dbus/client/.gitignore new file mode 100644 index 0000000..08ec6c8 --- /dev/null +++ b/dbus/client/.gitignore @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski + +gpiocli diff --git a/dbus/client/Makefile.am b/dbus/client/Makefile.am new file mode 100644 index 0000000..1f99daf --- /dev/null +++ b/dbus/client/Makefile.am @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2024 Bartosz Golaszewski + +AM_CFLAGS = -include $(top_builddir)/config.h +AM_CFLAGS += -I$(top_builddir)/dbus/lib/ -I$(top_srcdir)/dbus/lib/ +AM_CFLAGS += -Wall -Wextra -g +AM_CFLAGS += $(GLIB_CFLAGS) $(GIO_CFLAGS) $(GIO_UNIX_CFLAGS) +AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiocli\" +AM_LDFLAGS = $(GLIB_LIBS) $(GIO_LIBS) $(GIO_UNIX_LIBS) +LDADD = $(top_builddir)/dbus/lib/libgpiodbus.la + +bin_PROGRAMS = gpiocli + +gpiocli_SOURCES = \ + common.c \ + common.h \ + detect.c \ + find.c \ + get.c \ + gpiocli.c \ + info.c \ + monitor.c \ + notify.c \ + reconfigure.c \ + release.c \ + request.c \ + requests.c \ + set.c \ + wait.c + +noinst_SCRIPTS = gpiocli-test.bash diff --git a/dbus/data/Makefile.am b/dbus/data/Makefile.am new file mode 100644 index 0000000..3d5c2e1 --- /dev/null +++ b/dbus/data/Makefile.am @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +EXTRA_DIST = gpio-manager.service + +dbusdir = $(sysconfdir)/dbus-1/system.d/ +dbus_DATA = io.gpiod1.conf + +if WITH_SYSTEMD + +systemdsystemunit_DATA = gpio-manager.service + +endif diff --git a/dbus/lib/Makefile.am b/dbus/lib/Makefile.am new file mode 100644 index 0000000..c7cbc13 --- /dev/null +++ b/dbus/lib/Makefile.am @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +libgpiodbus_la_CFLAGS = -include $(top_builddir)/config.h -Wall -Wextra -g +libgpiodbus_la_CFLAGS += $(GLIB_CFLAGS) $(GIO_CFLAGS) +libgpiodbus_la_CFLAGS += -DG_LOG_DOMAIN=\"gpiodbus\" +libgpiodbus_la_LDFLAGS = -version-info 1 + +generated-gpiodbus.h generated-gpiodbus.c: io.gpiod1.xml + $(AM_V_GEN)gdbus-codegen \ + --interface-prefix io.gpiod1 \ + --c-namespace GPIODBus \ + --generate-c-code generated-gpiodbus \ + --c-generate-object-manager \ + --c-generate-autocleanup=all \ + $(srcdir)/io.gpiod1.xml + +lib_LTLIBRARIES = libgpiodbus.la +include_HEADERS = \ + generated-gpiodbus.h \ + gpiodbus.h +libgpiodbus_la_SOURCES = generated-gpiodbus.c + +BUILT_SOURCES = generated-gpiodbus.c generated-gpiodbus.h +CLEANFILES = $(BUILT_SOURCES) + +dbusdir = $(datadir)/dbus-1/interfaces +dbus_DATA = io.gpiod1.xml diff --git a/dbus/manager/.gitignore b/dbus/manager/.gitignore new file mode 100644 index 0000000..5507c6d --- /dev/null +++ b/dbus/manager/.gitignore @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski + +gpio-manager diff --git a/dbus/manager/Makefile.am b/dbus/manager/Makefile.am new file mode 100644 index 0000000..d1cef8e --- /dev/null +++ b/dbus/manager/Makefile.am @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +AM_CFLAGS = -I$(top_srcdir)/bindings/glib/ -include $(top_builddir)/config.h +AM_CFLAGS += -Wall -Wextra -g +AM_CFLAGS += -I$(top_builddir)/dbus/lib/ -I$(top_srcdir)/dbus/lib/ +AM_CFLAGS += $(GLIB_CFLAGS) $(GIO_CFLAGS) $(GIO_UNIX_CFLAGS) $(GUDEV_CFLAGS) +AM_CFLAGS += -DG_LOG_DOMAIN=\"gpio-manager\" +AM_CFLAGS += $(PROFILING_CFLAGS) +AM_LDFLAGS = $(GLIB_LIBS) $(GIO_LIBS) $(GIO_UNIX_LIBS) $(GUDEV_LIBS) +AM_LDFLAGS += $(PROFILING_LDFLAGS) +LDADD = $(top_builddir)/bindings/glib/libgpiod-glib.la +LDADD += $(top_builddir)/dbus/lib/libgpiodbus.la + +bin_PROGRAMS = gpio-manager +gpio_manager_SOURCES = \ + daemon.c \ + daemon.h \ + helpers.c \ + helpers.h \ + gpio-manager.c diff --git a/dbus/tests/.gitignore b/dbus/tests/.gitignore new file mode 100644 index 0000000..19f64af --- /dev/null +++ b/dbus/tests/.gitignore @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski + +gpiodbus-test diff --git a/dbus/tests/Makefile.am b/dbus/tests/Makefile.am new file mode 100644 index 0000000..ec4e26c --- /dev/null +++ b/dbus/tests/Makefile.am @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +noinst_PROGRAMS = gpiodbus-test +gpiodbus_test_SOURCES = \ + daemon-process.c \ + daemon-process.h \ + helpers.c \ + helpers.h \ + tests-chip.c \ + tests-line.c \ + tests-request.c + +AM_CFLAGS = -I$(top_srcdir)/tests/gpiosim-glib/ +AM_CFLAGS += -I$(top_builddir)/dbus/lib/ -I$(top_srcdir)/dbus/lib/ +AM_CFLAGS += -I$(top_srcdir)/tests/harness/ +AM_CFLAGS += -include $(top_builddir)/config.h +AM_CFLAGS += -Wall -Wextra -g -std=gnu89 +AM_CFLAGS += $(GLIB_CFLAGS) $(GIO_CFLAGS) +AM_CFLAGS += -DG_LOG_DOMAIN=\"gpiodbus-test\" +LDADD = $(top_builddir)/tests/gpiosim/libgpiosim.la +LDADD += $(top_builddir)/tests/gpiosim-glib/libgpiosim-glib.la +LDADD += $(top_builddir)/tests/harness/libgpiod-test-harness.la +LDADD += $(top_builddir)/dbus/lib/libgpiodbus.la +LDADD += $(GLIB_LIBS) $(GIO_LIBS) From patchwork Fri Jun 28 18:58:29 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808379 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BDF0F57888 for ; Fri, 28 Jun 2024 18:59:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601153; cv=none; b=IPnWjUBvgt1FLaUVUmCzaVdQak8gtOe9iWfIbWj42JGr2a+R6phiMFXrt2+b6BiTFt9QTFayV6p+pa6xzjNVja6LklOmvCxJz7aB7IGl+12dm3RLorRQgMVDKKn1DOcmsHbCOdrQf4Xqj8IvtE0GMVgeIoZgw8tJ+EeTAbjtYnQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601153; c=relaxed/simple; bh=RfnFsRFxKoRbW6HwXAxSlUxfZmidHUhysONb+hRaFuw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=djqzyV54EKt4kYnoc1nHGM/tsH/HDXgyg0ExeLP07C7pMMfq3AFXowpeBdILo8ZwwTFJHaf4Ny44/PlR2WXutRGttHZydcmCnxSND8clL3SP9QltV2D4XxxyoQ/5mf8O5/njA2efcpAVTAvWWcI2lJrHy5JFOcWQwF5swdeZ/eQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=TY6yCaFG; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="TY6yCaFG" Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-424aa70fbc4so6613785e9.1 for ; Fri, 28 Jun 2024 11:59:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601150; x=1720205950; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=neKXPs3bOvwzNiGHUhG9IGSZVWPySB8qQPyLZxnqDjI=; b=TY6yCaFGgAg+c69YgV6VoDzEif9oIeqhu6lHxnvyi+NTeNMFy7eN+xSKv2q8759KRY n8O9Zu3GHpb2rIMOt2eXxpjunQpkzeSkGvD02te0PZYXWLePv+fi2LUoN+iPP9f329I5 kuGAP0A53ePWTq1X/aoB/zBVtDBAZuYGzWoWaoJsz7r7JjtA8lcSHUuUTT0uxDrvV2cE 9w+TZtVzRNP5QMnGv62OFvzhjugcFT8AlccnFrgOwgnJrPUSVblMQEMoLY6a/LqBs14N ncAOrfLxOcL2WSAU5DPUCubxIe4y9A31+75E/yUjKPj1Uon3d992UbgPD6RtyMCXZ3IS XtOQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601150; x=1720205950; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=neKXPs3bOvwzNiGHUhG9IGSZVWPySB8qQPyLZxnqDjI=; b=eVphi4w1YSa13dEQj6d+BVhUEwuUOf3G7W8ZBbenVt1JyqUIdG6S28aIcpuMk9G+9P eWov3F8lf7RCP4FYH62mmSCCUEUAW/4mstGh1ix1ukRkzbKmd2WO0uQ+mNdszaDZncw/ X6Esz80We8h0U9NCNm3dOI7VHwsVqavaq08UlJ/vqyFesDfgoghfV1+9Udt3QmOZAyy9 Z0zYWy3xsgQMPf1La+UOLtLIiHnVeNGCPBWM11jNXXGJzyaI48tOsxpMCGvsMXHyDLvU h5og0PxeYhedbavTiwx5CAEZsr6aaS3lAqyn+dhvp2riKCHAOnnTJERdzebb68V0iLzO oGsw== X-Forwarded-Encrypted: i=1; AJvYcCXo7p69EG9z7FU7TNCaf4ueS+2BZ10cIHCq1MJr22b0BSfofNrMfN1ak8+LL028MuUMPWpiGoKI6NwxeSxgYGZyrmO+z7h+7PSWhg== X-Gm-Message-State: AOJu0Ywpv+XEdRkHGnlJByKri7U28uOMUZNXYoB7g4+KtZ3oxwatmowY DDXJ5rP3OjTCrSbybsWxv8efjT3cb3+DCpUU5ZJB6TVollD/EPfmeaUQ36lZTv4= X-Google-Smtp-Source: AGHT+IHYPO9BOtCo5WnKdU2i7pHJqrQRjhwPw9m+fi9/ZQG3V06uUpxJB+UVoW3gBW9gS+1GegP8/w== X-Received: by 2002:adf:f003:0:b0:360:9bf5:1eab with SMTP id ffacd0b85a97d-366e94cbbf9mr12080438f8f.36.1719601150361; Fri, 28 Jun 2024 11:59:10 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:09 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:29 +0200 Subject: [PATCH RESEND libgpiod v2 10/18] dbus: add the API definitions Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-10-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=10373; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=9YSC6tCMsudc40rcjXLYANsSvgN0Thlim+M3w+pdRjw=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfsbLi7J+ufkJUaqs0E0SpO6wOu+NHBcTBnv dzrYI9TGMqJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H7AAKCRARpy6gFHHX cojmD/99g+XbQwxBJSTN0/W4lgRpTi1y4bcTbNtQOzl3DP4azY+gz8dOTOrg4wVDbVR8Wfpvt7j bch+M1g29Ni3W166I/OSlpEnd+KQiajv0WhU1dyqCD5LMV2iBTkzBxEPZH4qbyuNRrZVYiikOBF TptQJujlt+AiKPKlJlOk7N+2sVjNxDTfB9W4QHx6Tkv0YfAxCpd3y+PXybM+pKwFm8GGXFeCur3 0l5kVRkQmd6IKANKd9X7BwigdWLCj/b67stuikJfiYAFgVsxh9gpoZ8vXqPticZWct2oDJJ4lFC 7q/ZNEDGjEj6UlR4PY+F+UC9Xs8EDps1PX7JUQfKY+ccsJJamhINTFCmQSf84SAz9T2HEgrtnHd 3Q/t5FiNLd9n8k7vICffG+RcTUTFrxAHK2kFBmD7GkJIMl34lAHpmqI9bFnaYEPkq/PNV1fSOxi GgliSldFqkLUrc4n53BJY7kQf5P+DoC2kofhsvw4ISYj1jRlylviIIhzg5/0XBGcfBc3sQQxqVT 6Gte+EgkXshhncN0AF9YXur2Gy+TjOEg4gg2TSizZtIptlAsb9JbjYDxRDk0ee+Y/ggwAee9ykB td3Vw9hpXriwup3Vckaka0wLVY9RKMb/kIKf4OGG0c0dDUGdc+BSt7qwjTkSG4QgFrHuhzJSidj MIR2n3TPLv0560A== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add the XML file defining all the interfaces for the io.gpiod1 service. Signed-off-by: Bartosz Golaszewski --- dbus/lib/io.gpiod1.xml | 318 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) diff --git a/dbus/lib/io.gpiod1.xml b/dbus/lib/io.gpiod1.xml new file mode 100644 index 0000000..ca95302 --- /dev/null +++ b/dbus/lib/io.gpiod1.xml @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From patchwork Fri Jun 28 18:58:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808529 Received: from mail-wr1-f54.google.com (mail-wr1-f54.google.com [209.85.221.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7D8BA558B7 for ; Fri, 28 Jun 2024 18:59:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601155; cv=none; b=RTqUGFwK5/V42a5ApsgEOVSeaOH+pRV3feCKyDYhRyJFwmgteZgsafbROrKpiqMO1C+nTrgFwAWQeI9/KZmEm9voPvewQ11whXRPjknBfeMKdh5OiCaKJhhY9XNgq8YeseNZ5ERnOalArp/NoqsSc9De2ydStRbbucJQH8/wH8c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601155; c=relaxed/simple; bh=NlO0aZfhz/s6o0n59m2zOkLbSwgJ5oykFJRbT8FwPQQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ExfJX2UGj3rM0KDVgFksk1jX7fWlrDp18Appdpqnogx4VaAZsy/Np0fnBQkTlfEkFdTYohfrLKJQRhRRqwIQYzlxKB23K/ReV49zMbbZAJarG7t1MractpMpcslmqYT+5oadtZyAChB5ywrJ0uzktUttIORriz2i3H5VcF9HfKE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=sU57V9dV; arc=none smtp.client-ip=209.85.221.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="sU57V9dV" Received: by mail-wr1-f54.google.com with SMTP id ffacd0b85a97d-364a39824baso557625f8f.1 for ; Fri, 28 Jun 2024 11:59:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601152; x=1720205952; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=39E0yrdWdDHUQfncbDygAV8xbrSDzDO4WmDOOUxTBXI=; b=sU57V9dV7MjCewcATZQfNEwmEgGrNt+adTa1Sa4MseptSekVw5gADKzdtSf9pZ0CGF 98zMk2684TeaEMkbCzeYaKTyajYG75SELCZgaH/bZxGcQGgxMbPU7w7nEnUtxbjkDkgh BHDcal2XvdAFrjvkrhBD3H+XFPWEv6WnN8KRrqRjGpKjdXktbMX17XR5ZUpmAx31vCQm DJmDXDmg0pPxE5+YNcP2K6fztTqFhyY8gNoTF4byOkvLmHrKqbtot1tSAvOVGs5q7mdm YmBTZJJr1lstwEdAdIomjL4EEIsaMVInd/z75Q8YPVKsv981Z8enrjTBfkXToqaC/7Yo tr0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601152; x=1720205952; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=39E0yrdWdDHUQfncbDygAV8xbrSDzDO4WmDOOUxTBXI=; b=NQBFQRnZPxJ3vpM9mTRHkS5Wa7bIeN/XOh+oHWlDOKD2z203YqAab04C9LD+ZSqvU8 Hm6gsyw02Y2I4H20HgAB7aZWiVqfwQRsLcgnr92VUgRHJvhQCWE0Z0hOSj4mX6ugWUcm NfWllE5p3yWlV+GlbsGyT/ThLjd+ftMKuA4kKs38hD0yiJzmF9c6AOdf427UV9BbRgGE P/8SpvjJTYeik3vDLlt2f4FIjOR1klOvS5M6iKtzByou68sVrjNwnGWNWchmwWRtPQ7d 3MZQ2ufjQT3nAadcu2M+9BmNcfrsZNJ3wi71/HBrE6rwqtisRdja3H/oiNG+od3orYCw RCKg== X-Forwarded-Encrypted: i=1; AJvYcCV4qF0YvVbJVUSV+YLJ/14cKJyMk9GrVdMapAGTyLKrWwRBreE0T1lGbDbBnlukOjg6d6JD74yvgbZzvkwXR9Fg8BWwt5xy+xyziA== X-Gm-Message-State: AOJu0YxvOJRKa+2ZY3TFRGOyus3qc9ylYENvNjxW7fp6n45TV2bss4xl AiawWNfhkHVWA/m+LO2d3fXI0UDhiJoQikJSDqxYkVoUJIvWhfC6ED5r0bxjcEIKNH7A6IEOoqV + X-Google-Smtp-Source: AGHT+IFEY69BQtqxaJChTb7gXvOep5IHRcMY90Go+BelbRKb3vwjUxml1YzjqZFpOPNBs4AAQf1K1g== X-Received: by 2002:adf:f7cf:0:b0:360:9d23:67f8 with SMTP id ffacd0b85a97d-366e7a520b9mr11705691f8f.69.1719601151871; Fri, 28 Jun 2024 11:59:11 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:10 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:30 +0200 Subject: [PATCH RESEND libgpiod v2 11/18] dbus: add a wrapper around the gdbus-codegen generated header Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-11-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=709; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=uenmBNlg72EuxwSfuAZIjIaF0DfWjeAIoTp/JAJRqKI=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwftqTsw6nsacQTIkYIZHEwhKxrVuaJzqn8N7 xbWEAQBn52JAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H7QAKCRARpy6gFHHX cjtCD/46g627HF1GuZoKKdLsUI8iStCxAcugzCHse7Y5ynmfGK1ILkyrXPS5Y5eGHEQB3z51glt uhBoOMEu4u+aZgluJHBeWYS00a1WU3I+mmeONEdsjZ/za0TSiufHnf9TdAE2n/xUkLYtCmBwOka O/jVDjruZQQKAngkIYhnRM59ZbV0QMHXY3kDg6V06Ijq/E8h31hmWBf5kDu2MFE/7ikVD9HH1Ra 4YZEo2VGBbrIf7kM4Gan3PkCWs1MNxI0lMYm93NQ0Y16q6QJ7B8HiZ2LNAA9uX9QDmGUqEMdwiE xTaCU9jZgfqc+OKlOWYA4vhbYng71wyExtAKD1uWpdAjqEIELeWNv19eLdXSDth70dFfeagrOVC FSuNeL6Yl584HTJ38WIXuuH86/MH/uQgHETYWizG2ojzsfCdfHuBnzqvJm5bAm/NBdkTU/3LzkL /CBk3AQgtckk0o3QWXGIUYWlBqYh5AoUMFj6D1nXmxMNPrtWGD754vjrAUoCWK1Pd3bIxSJWmQi 3eJu53sBWYzOJpsTCYLsWWKzmfwZOz9pzZh2qNCovHOs5Ea89sQH+3+pA8Le9Hu84oN6WfIiFJ5 sKOlEap3wjV+Smf2mUqOdDVK7/4rrwyDA+HOOaU/RPBpLRCdqmjn4Q8Dz+d8bInDM3DWI++Qeht r8MpKaL3+6bMHUQ== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add a header that will be included by all the code using the automatically generated GLib layer for the DBus interface. Signed-off-by: Bartosz Golaszewski --- dbus/lib/gpiodbus.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dbus/lib/gpiodbus.h b/dbus/lib/gpiodbus.h new file mode 100644 index 0000000..69362f0 --- /dev/null +++ b/dbus/lib/gpiodbus.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2023 Bartosz Golaszewski */ + +#ifndef __GPIODBUS_H__ +#define __GPIODBUS_H__ + +#include "generated-gpiodbus.h" + +#endif /* __GPIODBUS_H__ */ From patchwork Fri Jun 28 18:58:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808378 Received: from mail-wr1-f50.google.com (mail-wr1-f50.google.com [209.85.221.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2D9D457888 for ; Fri, 28 Jun 2024 18:59:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601155; cv=none; b=qmegqVH8cjRs2LGP292NPwKQlLn1X/fOx7TpPUQRbzlZixqUuxZOxygejdzvelS+FTLhDAF4K4r0glIr8ktpEfYva2cnF3gvUDSnEhjz5OAs7OnWyWRWmyse9sT0nDdDA+gq8JR2CfE9stMvkQ3/8HuZz2nh+aB2fDYfT3NMM6w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601155; c=relaxed/simple; bh=DdvrNh9CUM8EGByowb0nAfUbRPX0ZycLnwii9270CsQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=s3RIQKXjFhf3lr51N4CnvlbVCIo61htbFqxeMPJQBe19wF7bAjej8+jEUOsq9+WN/mueZfcSL/04UGwDG6rEfvdOs+FQTDEDeRnWgHuD4wquUuaPbV02oIU2fQ7bL6025M403MWa8B+MBZ7nQ9WmuiUzsYwK34RTuruLo5yvuzI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=qn0Qzlfq; arc=none smtp.client-ip=209.85.221.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="qn0Qzlfq" Received: by mail-wr1-f50.google.com with SMTP id ffacd0b85a97d-3658197cdbbso517933f8f.3 for ; Fri, 28 Jun 2024 11:59:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601153; x=1720205953; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=IUcuuGRbJ56dacHqrtu+36kwpTXuoyZIlL4CYBgLiFs=; b=qn0Qzlfqxl0PVHEqYv9uxiIprWsnA2KPK7lwoLTDj/to/DOt3iCriwCVd5TsWVa+kQ LiwAp4kMYswDAvGsuIGVU94Qe6EIHDasrBKjFNJ2P9Qs7hHWdYckI97Cbj8SnCkeSBpB AWt6waDsHBDKNz9s/74Y/RByRua4k/bUfXddkFPLJ1YR5TWWliLXKHHaOjL49UToyM3A BhhZbBi1OwlzNmpvbusBpDzfIN3T4SnNI8THvRoodH9K8/HjhUnaXf1QbV8nPrv4vPkZ J5PhiJ7zmSYYxMOy1/BDp5RYREpjoXK10GJCo+8UnNNyNUlv/QsooBS3rs7LevJSUJ0Z rBjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601153; x=1720205953; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=IUcuuGRbJ56dacHqrtu+36kwpTXuoyZIlL4CYBgLiFs=; b=dQGCmushxLxwU2aNFVNFoUDYXSwnS3Hn+snolOjoaYxP/1R/9EOlJw36bFhfnhLVeR BiruDpnQ2TmSIGubS99rGqNJ4WWeXrf2sLxP2N8r4oGmbUDxUSGhzzItY0fV4g7OThHz TXwFdo0kjOQR1BMj0Xtpm1l8Z5FFpP5aa/H9T09f3+RrhuHwxblRi+AzQ8y3te6JyDdW 9K+97HuXMMawrF4udPvPTgoymRTyCvrV2iBtJ+KcVemVgYSzTBBKqqyWoid03Vjw6aP1 xkDJaOwMbwo3+umMHzX6xd0hWYEQFx5cgmd4m+v0+uuPwbVos+AxRCYst/NLICBDj4eV 4OsQ== X-Forwarded-Encrypted: i=1; AJvYcCV4ln1QOmGgug6IJmUqw9AZCRv3o/mqzYKsBECiqgngPEJcY/buzt9POld7GlaiKVSNz1JZ9NshrspfBS5nakp5CTfPbsXnjBX3YQ== X-Gm-Message-State: AOJu0YxVLFRoN9TZEoJzZvHaUfiZOK1GjbZP7fNJmRONeQ2ZZ2VCEcxu nsFmg7Hqe4677b9tZXFb+Wn8WwmOhyOMGQxhYLFSJOwEV37WuxoPVC8UgkRJD8s= X-Google-Smtp-Source: AGHT+IGEaINwVQllqqQOOhJvcWwv73G1fV7OaEx9Ge5/pesRC8C4X4DcICMAnxJ1toFOWtjv7OMpgg== X-Received: by 2002:a5d:6681:0:b0:362:7c2e:e9f7 with SMTP id ffacd0b85a97d-366e94cbbe3mr11372485f8f.32.1719601152799; Fri, 28 Jun 2024 11:59:12 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:12 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:31 +0200 Subject: [PATCH RESEND libgpiod v2 12/18] dbus: add data files Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-12-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2158; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=C9K7A3TozwJBiuPu+rWOYmEK2blsjQR/Lw7cpTrkuIE=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwftxhrh6QQt6bR3/zHKlMeF3MGf6TEsTnjh2 6WTxv5zPkiJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H7QAKCRARpy6gFHHX cop/D/0UuyFWjkY8YhFpdIza95rLKKxFb4Z6YQdoNntNSsO9jf0WqVeLqk53pPILYGZUAFXunY5 L6nh/309Pwbfr6kRVudjPvMvP5x2Ncs8EzyD/bs8TDqIZRzZDf+9wT9dmC9rwLPfcvWITFszhSy oZJDsYJQleg3v//jx/pHeInn8gmisLc1fLMLcwFNj0HZOpO2qVy/jjAWm/ctcj5+QuoKqWns1wn IOVY0nJzXb4ctERKnEIUEqbR21UJagwab96JW/jI2JDpODvdXAUUJY3tyA8ui3EoE6IPKk4UrE4 pnoXMxG7Z7qDKP/hts6ukJdAVvCQ+iV3xFLAp0oxYUmw29kCGEANcdi/TscnDM3/hqbMShbHRgI wjGQdgwjkXa5A8UyNnazmm8TmFJUc6tF+Fd7m2tUfLMoVZBCdwudLc/hA+kH/k/Exbccvwa9evA HUTL6dx24VQJqxU8fzKQDdHDx8KpOaL2u8hiJMmEfXszsCf991WhfxgA8CUxF1nbo21sZqb/rG/ xEsiV2nwHiFKVQWq+3oSItf6jwHpMhYUzt+iHMquCYqb8tGxJEIfNbIdG9nNhz23PhFVwJGJPvF DX7yViiCZ7E6z14qlPDUHm724C8XEmyxpuDkv0BVuDgkgcwoOvXM3vZDI0BvJD54hoEquLGZzEM q4go44I8iyVLMaA== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add the default service configuration file for the DBus GPIO API and a systemd unit file that allows to start up the gpio-manager. Signed-off-by: Bartosz Golaszewski --- dbus/data/gpio-manager.service | 14 ++++++++++++++ dbus/data/io.gpiod1.conf | 29 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/dbus/data/gpio-manager.service b/dbus/data/gpio-manager.service new file mode 100644 index 0000000..f84f8cc --- /dev/null +++ b/dbus/data/gpio-manager.service @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +[Unit] +Description=Centralized GPIO manager daemon + +[Service] +Type=dbus +BusName=io.gpiod1 +ExecStart=/usr/bin/gpio-manager +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/dbus/data/io.gpiod1.conf b/dbus/data/io.gpiod1.conf new file mode 100644 index 0000000..339a382 --- /dev/null +++ b/dbus/data/io.gpiod1.conf @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + From patchwork Fri Jun 28 18:58:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808528 Received: from mail-wr1-f49.google.com (mail-wr1-f49.google.com [209.85.221.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 50DF8558B7 for ; Fri, 28 Jun 2024 18:59:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601158; cv=none; b=VLT+Qp59JJTds69fv815jnvOhsVQbXffQ+Oy5dBLcMiXDNcvSYc3NAFtiEc01l2fT8sDgWs3dFamBLqxk1PXixw5WIeHdJnOoA+E8kZsjcGTVFsFpIEmR8j+KQf9p2LHs+JsHYTsqvEJbbprPzvKk9Ql2cNLBvpUED4LpsMl28w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601158; c=relaxed/simple; bh=YD1vziIM00KArqdRjvLO8ds4lHOkjFlGs8FahfyiWcA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=aKkb+zPTAinGGDVQknmO4D5Jb6l51gvwGGgkddkxrhCvPjx5ThUGi2eaaiqGoD+xwnb8Br6VT7dVkfVppNZK7cc9Zj1GNs9UMaGXTTBc3yVk4JoYmHqW5wiXFqjHGY6LIva6dQpk2p+/OI3COSHrhU5p3VgpREzzEpA9TS6r1nQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=SkXPNFbz; arc=none smtp.client-ip=209.85.221.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="SkXPNFbz" Received: by mail-wr1-f49.google.com with SMTP id ffacd0b85a97d-35f06861ae6so636698f8f.2 for ; Fri, 28 Jun 2024 11:59:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601154; x=1720205954; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=JxgtI6Iz5Ubsr2mHqJ7nrRQR2a2//lfW6fpcYmVVTx0=; b=SkXPNFbzKBNo09CHAAvwtvj/r+SWzvxPx8QyM54S0IEvPqWRR9QVQrrkofykYgrglX SHbF8v1glcUhUtN3wrszI5DU+DBb8gOssqQhsldINyYcXzrHZEDYu5XKuLR+QI/K0GUU /3Q+WCKlyvGVXnZ/Jp5O3VJb8Cvh32f+W/6/NPV6hZqqo5bTaFGHfScDLcfjefW2aEzJ u/23l4125/pKbvGTEPymxTh/VUg+x/wn6zFZ16qtH9GDPbIb8eGELsNubDEuU1it5Xsw o2D/xn6HWtLE4qeLKPW8E7Qcgfd5K5TsMKeuAosul9bKEOJgslG0TGDP4YSnDAGsfV+s uyXQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601154; x=1720205954; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=JxgtI6Iz5Ubsr2mHqJ7nrRQR2a2//lfW6fpcYmVVTx0=; b=KWyD+YWIQ85wP6C6t34Wssd3gvHHsq6MPh0mrv+6d2HzKSgyahTd7VJP+Arm0CmtaZ m+PpU/c2DTS56V3fTgXQGNesgCv/TyqByKCQaExQoc0mq43hahvZod9KYKMcS07VmYT2 8ayp2iPVOPHWns/WIBGMu07fmslqrSLX1Hhq6lt61tMBuWFFAg0YoV5n4JuGT/RBe6EE WhLbUqote0SGRhQ4BRWeh7oilSIMArRp2skMhySUUaUdHDtNwhEu8ZIxufQvPIElIBsA N5424FjZ1d+eZTMujGj7/gFoWbh3i0rYyM8h0LAxEt4MW33IK2lW5iMVy4FCvxpN7PN2 QvxA== X-Forwarded-Encrypted: i=1; AJvYcCWOU2f9mOIQFi2nv42NBPN6sv7Td81oHiAvbHYSFP/sFEz9KiKmU1BX7/ye37rPfAEwEETccXh/7Qd0aozVf3d3IeGdjOvqozVqeQ== X-Gm-Message-State: AOJu0YyD0W5vzMMUEuCL/RDv9Vfj1U7kBa508YeJD8IFr80/M2ON9cwP z2xfBMzaYpHsdA0fh5lf54STLnLhHBrcpOME1vbxO4CnBI0HSoJCRdQoVS7X2g8= X-Google-Smtp-Source: AGHT+IFGtY/ZEjmDsEn1XTIFtqNxVhz0uli7RQ4fWBkhflOL3AMVmghEIVbZGgaGSU0Vd3WmdR1xtQ== X-Received: by 2002:a5d:4107:0:b0:362:e357:5ceb with SMTP id ffacd0b85a97d-366e9522b7amr11072222f8f.18.1719601153797; Fri, 28 Jun 2024 11:59:13 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:13 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:32 +0200 Subject: [PATCH RESEND libgpiod v2 13/18] dbus: add gpio-manager code Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-13-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=46828; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=79skB2WmG5/S2a5FmZnI72dC/A6q9E0IUOnYZ5favu4=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfts/9orkwRMRiBFo2gYkFYgpYCuN4OLzHln AwjWR/PYf2JAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H7QAKCRARpy6gFHHX crKjEACU+iHNYdvshDMN+es0/PxeuFtp0QomJia/C0225nfQC7Vs8ExMMADK53HgG99hHDb6DbK clz7dmRO7LJhEPQhBJZ/PR96XOrc/BjVt3I4I/Fjvrgso6VHi/6HebNQsRsqRyJ6WazILqxJaeR x7tpTMP8601lEbVCW9Z7eT/g5pVjGpb65aLV1OdBrSF3zsAdcbeXlF4yy/EydK+2s3MhBlDRK4u ZFknUSHDTunEe43sHWQsj3zyGiCnRYA5f4eWaYHJ/A4VMTRzwt73Qgl+Vjy7D64SYst3h+qHw/c jk3hL+67+Oa2Kn1MYerEGv0xmYJ6QeePPKIPauiKbHyOslEy7Pcb5GHU2rMEtd+QIlvv/i0fO2j DJTGMWVbn0JGPszb5J2UCIQ5GdKQArGrEb1P6hY7/a1S/2jF+vVEKuQgk2fmI5ztJjdoi4PKv7H CV36OHweAkbgjCupBLWcJ0b8RW6wU7KM98DEw413IhMNtr2dgRejZK36uvXzNktHFAJGSuNCS6h nvgT8Ubxf4XSBOKp4GfuSZTbAsctN7fEowOBZymKEctVU5wJgZ94OdoXAE+m+9NtlahNofrniCy /e2wUd75xp0G0kCtt6S91+qjXvvGrj1hgJrmttmu19GAJJP50HCMI73ExHfAJfWSrp7JeQ4BXcz FTB15k692oeSYmw== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add the code for the gpio-manager - the actual implementation of the DBus API defined in io.gpiod1.xml. Signed-off-by: Bartosz Golaszewski --- dbus/manager/daemon.c | 821 ++++++++++++++++++++++++++++++++++++++++++++ dbus/manager/daemon.h | 22 ++ dbus/manager/gpio-manager.c | 167 +++++++++ dbus/manager/helpers.c | 420 ++++++++++++++++++++++ dbus/manager/helpers.h | 24 ++ 5 files changed, 1454 insertions(+) diff --git a/dbus/manager/daemon.c b/dbus/manager/daemon.c new file mode 100644 index 0000000..59bf1ee --- /dev/null +++ b/dbus/manager/daemon.c @@ -0,0 +1,821 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2024 Bartosz Golaszewski + +#include +#include +#include + +#include "daemon.h" +#include "helpers.h" + +struct _GPIODBusDaemon { + GObject parent; + GDBusConnection *con; + GUdevClient *udev; + GDBusObjectManagerServer *chip_manager; + GDBusObjectManagerServer *request_manager; + GHashTable *chips; + GHashTable *requests; + GTree *req_id_root; +}; + +G_DEFINE_TYPE(GPIODBusDaemon, gpiodbus_daemon, G_TYPE_OBJECT); + +typedef struct { + GPIODChip *chip; + GPIODBusChip *dbus_chip; + GPIODBusDaemon *daemon; + GDBusObjectManagerServer *line_manager; + GHashTable *lines; +} GPIODBusDaemonChipData; + +typedef struct { + GPIODLineRequest *request; + GPIODBusRequest *dbus_request; + gint id; + GPIODBusDaemonChipData *chip_data; +} GPIODBusDaemonRequestData; + +typedef struct { + GPIODBusLine *dbus_line; + GPIODBusDaemonChipData *chip_data; + GPIODBusDaemonRequestData *req_data; +} GPIODBusDaemonLineData; + +static const gchar* const gpiodbus_daemon_udev_subsystems[] = { "gpio", NULL }; + +static void gpiodbus_daemon_dispose(GObject *obj) +{ + GPIODBusDaemon *self = GPIODBUS_DAEMON(obj); + + g_debug("disposing of the GPIO daemon"); + + g_clear_pointer(&self->chips, g_hash_table_unref); + /* + * REVISIT: Do we even need to unref the request hash table here at + * all? All requests should have been freed when removing their parent + * chips. + */ + g_clear_pointer(&self->requests, g_hash_table_unref); + g_clear_pointer(&self->req_id_root, g_tree_destroy); + g_clear_object(&self->con); + + G_OBJECT_CLASS(gpiodbus_daemon_parent_class)->dispose(obj); +} + +static void gpiodbus_daemon_finalize(GObject *obj) +{ + GPIODBusDaemon *self = GPIODBUS_DAEMON(obj); + + g_debug("finalizing GPIO daemon"); + + g_clear_object(&self->request_manager); + g_clear_object(&self->chip_manager); + g_clear_object(&self->udev); + + G_OBJECT_CLASS(gpiodbus_daemon_parent_class)->finalize(obj); +} + +static void gpiodbus_daemon_class_init(GPIODBusDaemonClass *daemon_class) +{ + GObjectClass *class = G_OBJECT_CLASS(daemon_class); + + class->dispose = gpiodbus_daemon_dispose; + class->finalize = gpiodbus_daemon_finalize; +} + +static gboolean +gpiodbus_remove_request_if_chip_matches(gpointer key G_GNUC_UNUSED, + gpointer value, gpointer user_data) +{ + GPIODBusDaemonChipData *chip_data = user_data; + GPIODBusDaemonRequestData *req_data = value; + + return req_data->chip_data == chip_data; +} + +static void gpiodbus_daemon_chip_data_free(gpointer data) +{ + GPIODBusDaemonChipData *chip_data = data; + const gchar *obj_path; + + obj_path = g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(chip_data->dbus_chip)); + + g_debug("unexporting object for GPIO chip: '%s'", obj_path); + + g_hash_table_foreach_remove(chip_data->daemon->requests, + gpiodbus_remove_request_if_chip_matches, + chip_data); + + g_dbus_object_manager_server_unexport(chip_data->daemon->chip_manager, + obj_path); + + g_hash_table_unref(chip_data->lines); + g_object_unref(chip_data->line_manager); + g_object_unref(chip_data->chip); + g_object_unref(chip_data->dbus_chip); + g_free(chip_data); +} + +static void gpiodbus_daemon_line_data_free(gpointer data) +{ + GPIODBusDaemonLineData *line_data = data; + const gchar *obj_path; + + obj_path = g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(line_data->dbus_line)); + + g_debug("unexporting object for GPIO line: '%s'", + obj_path); + + g_dbus_object_manager_server_unexport( + line_data->chip_data->line_manager, obj_path); + + g_object_unref(line_data->dbus_line); + g_free(line_data); +} + +static void gpiodbus_lines_set_managed(GPIODBusDaemonRequestData *req_data, + gboolean managed) +{ + g_autoptr(GDBusObject) obj = NULL; + const gchar *const *line_paths; + GPIODBusLine *line; + const gchar *path; + guint i; + + line_paths = gpiodbus_request_get_line_paths(req_data->dbus_request); + + for (path = line_paths[0], i = 0; path; path = line_paths[++i]) { + obj = g_dbus_object_manager_get_object( + G_DBUS_OBJECT_MANAGER( + req_data->chip_data->line_manager), path); + line = gpiodbus_object_peek_line(GPIODBUS_OBJECT(obj)); + + g_debug("Setting line %u on chip object '%s' to '%s'", + gpiodbus_line_get_offset(line), + g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON( + req_data->chip_data->dbus_chip)), + managed ? "managed" : "unmanaged"); + + gpiodbus_line_set_managed(line, managed); + gpiodbus_line_set_request_path(line, + managed ? g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON( + req_data->dbus_request)) : NULL); + g_dbus_interface_skeleton_flush( + G_DBUS_INTERFACE_SKELETON(line)); + } +} + +static void gpiodbus_daemon_request_data_free(gpointer data) +{ + GPIODBusDaemonRequestData *req_data = data; + const gchar *obj_path; + + obj_path = g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(req_data->dbus_request)); + + g_debug("unexporting object for GPIO request: '%s'", obj_path); + + g_dbus_object_manager_server_unexport( + req_data->chip_data->daemon->request_manager, obj_path); + + gpiodbus_lines_set_managed(req_data, FALSE); + gpiodbus_id_free(req_data->chip_data->daemon->req_id_root, + req_data->id); + g_object_unref(req_data->request); + g_object_unref(req_data->dbus_request); + g_free(req_data); +} + +static void gpiodbus_daemon_init(GPIODBusDaemon *self) +{ + g_debug("initializing GPIO DBus daemon"); + + self->con = NULL; + self->udev = g_udev_client_new(gpiodbus_daemon_udev_subsystems); + self->chip_manager = + g_dbus_object_manager_server_new("/io/gpiod1/chips"); + self->request_manager = + g_dbus_object_manager_server_new("/io/gpiod1/requests"); + self->chips = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + gpiodbus_daemon_chip_data_free); + self->requests = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + gpiodbus_daemon_request_data_free); + self->req_id_root = g_tree_new_full(gpiodbus_id_cmp, NULL, + g_free, NULL); +} + +GPIODBusDaemon *gpiodbus_daemon_new(void) +{ + return GPIODBUS_DAEMON(g_object_new(GPIODBUS_DAEMON_TYPE, NULL)); +} + +static void gpiodbus_daemon_on_info_event(GPIODChip *chip G_GNUC_UNUSED, + GPIODInfoEvent *event, + gpointer data) +{ + GPIODBusDaemonChipData *chip_data = data; + g_autoptr(GPIODLineInfo) info = NULL; + GPIODBusDaemonLineData *line_data; + guint offset; + + info = g_gpiod_info_event_get_line_info(event); + offset = g_gpiod_line_info_get_offset(info); + + g_debug("line info event received for offset %u on chip '%s'", + offset, + g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(chip_data->dbus_chip))); + + line_data = g_hash_table_lookup(chip_data->lines, + GINT_TO_POINTER(offset)); + if (!line_data) + g_error("failed to retrieve line data - programming bug?"); + + gpiodbus_line_set_props(line_data->dbus_line, info); +} + +static void gpiodbus_daemon_export_line(GPIODBusDaemon *self, + GPIODBusDaemonChipData *chip_data, + GPIODLineInfo *info) +{ + g_autofree GPIODBusDaemonLineData *line_data = NULL; + g_autoptr(GPIODBusObjectSkeleton) skeleton = NULL; + g_autoptr(GPIODBusLine) dbus_line = NULL; + g_autofree gchar *obj_path = NULL; + const gchar *obj_prefix; + guint line_offset; + gboolean ret; + + obj_prefix = g_dbus_object_manager_get_object_path( + G_DBUS_OBJECT_MANAGER(chip_data->line_manager)); + line_offset = g_gpiod_line_info_get_offset(info); + dbus_line = gpiodbus_line_skeleton_new(); + obj_path = g_strdup_printf("%s/line%u", obj_prefix, line_offset); + + gpiodbus_line_set_props(dbus_line, info); + + skeleton = gpiodbus_object_skeleton_new(obj_path); + gpiodbus_object_skeleton_set_line(skeleton, GPIODBUS_LINE(dbus_line)); + + g_debug("exporting object for GPIO line: '%s'", obj_path); + + g_dbus_object_manager_server_export(chip_data->line_manager, + G_DBUS_OBJECT_SKELETON(skeleton)); + g_dbus_object_manager_server_set_connection(chip_data->line_manager, + self->con); + + line_data = g_malloc0(sizeof(*line_data)); + line_data->dbus_line = g_steal_pointer(&dbus_line); + line_data->chip_data = chip_data; + + ret = g_hash_table_insert(chip_data->lines, + GUINT_TO_POINTER(line_offset), + g_steal_pointer(&line_data)); + /* It's a programming bug if the line is already in the hashmap. */ + g_assert(ret); +} + +static gboolean gpiodbus_daemon_export_lines(GPIODBusDaemon *self, + GPIODBusDaemonChipData *chip_data) +{ + g_autoptr(GPIODChipInfo) chip_info = NULL; + GPIODChip *chip = chip_data->chip; + g_autoptr(GError) err = NULL; + guint i, num_lines; + gint j; + + chip_info = g_gpiod_chip_get_info(chip, &err); + if (!chip_info) { + g_critical("failed to read chip info: %s", err->message); + return FALSE; + } + + num_lines = g_gpiod_chip_info_get_num_lines(chip_info); + + g_signal_connect(chip, "info-event", + G_CALLBACK(gpiodbus_daemon_on_info_event), chip_data); + + for (i = 0; i < num_lines; i++) { + g_autoptr(GPIODLineInfo) linfo = NULL; + + linfo = g_gpiod_chip_watch_line_info(chip, i, &err); + if (!linfo) { + g_critical("failed to setup a line-info watch: %s", + err->message); + for (j = i; j >= 0; j--) + g_gpiod_chip_unwatch_line_info(chip, i, NULL); + return FALSE; + } + + gpiodbus_daemon_export_line(self, chip_data, linfo); + } + + return TRUE; +} + +static gboolean +gpiodbus_daemon_handle_release_lines(GPIODBusRequest *request, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + GPIODBusDaemonRequestData *req_data = user_data; + g_autofree gchar *obj_path = NULL; + gboolean ret; + + obj_path = g_strdup(g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(request))); + + g_debug("release call received on request '%s'", obj_path); + + ret = g_hash_table_remove(req_data->chip_data->daemon->requests, + obj_path); + /* It's a programming bug if the request was not in the hashmap. */ + if (!ret) + g_warning("request '%s' is not registered - logic error?", + obj_path); + + g_dbus_method_invocation_return_value(invocation, NULL); + + return G_SOURCE_CONTINUE; +} + +static gboolean +gpiodbus_daemon_handle_reconfigure_lines(GPIODBusRequest *request, + GDBusMethodInvocation *invocation, + GVariant *arg_line_cfg, + gpointer user_data) +{ + GPIODBusDaemonRequestData *req_data = user_data; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autofree gchar *line_cfg_str = NULL; + g_autoptr(GError) err = NULL; + const gchar *obj_path; + gboolean ret; + + obj_path = g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(request)); + line_cfg_str = g_variant_print(arg_line_cfg, FALSE); + + g_debug("reconfigure call received on request '%s', line config: %s", + obj_path, line_cfg_str); + + line_cfg = gpiodbus_line_config_from_variant(arg_line_cfg); + if (!line_cfg) { + g_critical("failed to convert method call arguments '%s' to line config", + line_cfg_str); + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "Invalid line configuration"); + goto out; + } + + ret = g_gpiod_line_request_reconfigure_lines(req_data->request, + line_cfg, &err); + if (!ret) { + g_critical("failed to reconfigure GPIO lines on request '%s': %s", + obj_path, err->message); + g_dbus_method_invocation_return_dbus_error(invocation, + "io.gpiod1.ReconfigureFailed", + err->message); + goto out; + } + + g_dbus_method_invocation_return_value(invocation, NULL); + +out: + return G_SOURCE_CONTINUE; +} + +static gboolean +gpiodbus_daemon_handle_get_values(GPIODBusRequest *request, + GDBusMethodInvocation *invocation, + GVariant *arg_offsets, gpointer user_data) +{ + GPIODBusDaemonRequestData *req_data = user_data; + g_autoptr(GVariant) out_values = NULL; + g_autofree gchar *offsets_str = NULL; + g_autoptr(GVariant) response = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + GVariantBuilder builder; + const gchar *obj_path; + GVariantIter iter; + gsize num_offsets; + guint offset, i; + gboolean ret; + + obj_path = g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(request)); + offsets_str = g_variant_print(arg_offsets, FALSE); + num_offsets = g_variant_n_children(arg_offsets); + + g_debug("get-values call received on request '%s' for offsets: %s", + obj_path, offsets_str); + + if (num_offsets == 0) { + ret = g_gpiod_line_request_get_values(req_data->request, + &values, &err); + } else { + offsets = g_array_sized_new(FALSE, TRUE, sizeof(offset), + num_offsets); + g_variant_iter_init(&iter, arg_offsets); + while (g_variant_iter_next(&iter, "u", &offset)) + g_array_append_val(offsets, offset); + + ret = g_gpiod_line_request_get_values_subset(req_data->request, + offsets, &values, + &err); + } + if (!ret) { + g_critical("failed to get GPIO line values on request '%s': %s", + obj_path, err->message); + g_dbus_method_invocation_return_dbus_error(invocation, + "io.gpiod1.GetValuesFailed", + err->message); + goto out; + } + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + for (i = 0; i < values->len; i++) + g_variant_builder_add(&builder, "i", + g_array_index(values, gint, i)); + out_values = g_variant_ref_sink(g_variant_builder_end(&builder)); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&builder, out_values); + response = g_variant_ref_sink(g_variant_builder_end(&builder)); + + g_dbus_method_invocation_return_value(invocation, response); + +out: + return G_SOURCE_CONTINUE; +} + +static gboolean +gpiodbus_daemon_handle_set_values(GPIODBusRequest *request, + GDBusMethodInvocation *invocation, + GVariant *arg_values, gpointer user_data) +{ + GPIODBusDaemonRequestData *req_data = user_data; + g_autofree gchar *values_str = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + const gchar *obj_path; + GVariantIter iter; + gsize num_values; + guint offset; + gboolean ret; + gint value; + + obj_path = g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(request)); + values_str = g_variant_print(arg_values, FALSE); + num_values = g_variant_n_children(arg_values); + + g_debug("set-values call received on request '%s': %s", + obj_path, values_str); + + if (num_values == 0) { + g_critical("Client passed no offset to value mappings"); + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "No offset <-> value mappings specified"); + goto out; + } + + offsets = g_array_sized_new(FALSE, TRUE, sizeof(offset), num_values); + values = g_array_sized_new(FALSE, TRUE, sizeof(value), num_values); + + g_variant_iter_init(&iter, arg_values); + while (g_variant_iter_next(&iter, "{ui}", &offset, &value)) { + g_array_append_val(offsets, offset); + g_array_append_val(values, value); + } + + ret = g_gpiod_line_request_set_values_subset(req_data->request, offsets, + values, &err); + if (!ret) { + g_critical("failed to set GPIO line values on request '%s': %s", + obj_path, err->message); + g_dbus_method_invocation_return_dbus_error(invocation, + "io.gpiod1.SetValuesFailed", + err->message); + goto out; + } + + g_dbus_method_invocation_return_value(invocation, NULL); + +out: + return G_SOURCE_CONTINUE; +} + +static void +gpiodbus_daemon_on_edge_event(GPIODLineRequest *request G_GNUC_UNUSED, + GPIODEdgeEvent *event, gpointer user_data) +{ + GPIODBusDaemonRequestData *req_data = user_data; + GPIODBusDaemonLineData *line_data; + gulong line_seqno, global_seqno; + GPIODEdgeEventType edge; + guint64 timestamp; + guint offset; + gint val; + + edge = g_gpiod_edge_event_get_event_type(event); + offset = g_gpiod_edge_event_get_line_offset(event); + timestamp = g_gpiod_edge_event_get_timestamp_ns(event); + global_seqno = g_gpiod_edge_event_get_global_seqno(event); + line_seqno = g_gpiod_edge_event_get_line_seqno(event); + + val = edge == G_GPIOD_EDGE_EVENT_RISING_EDGE ? 1 : 0; + + g_debug("%s edge event received for offset %u on request '%s'", + val ? "rising" : "falling", offset, + g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(req_data->dbus_request))); + + line_data = g_hash_table_lookup(req_data->chip_data->lines, + GINT_TO_POINTER(offset)); + if (!line_data) + g_error("failed to retrieve line data - programming bug?"); + + gpiodbus_line_emit_edge_event(line_data->dbus_line, + g_variant_new("(ittt)", val, timestamp, + global_seqno, line_seqno)); +} + +static void +gpiodbus_daemon_export_request(GPIODBusDaemon *self, GPIODLineRequest *request, + GPIODBusDaemonChipData *chip_data, gint id) +{ + g_autofree GPIODBusDaemonRequestData *req_data = NULL; + g_autoptr(GPIODBusObjectSkeleton) skeleton = NULL; + g_autoptr(GPIODBusRequest) dbus_req = NULL; + g_autofree gchar *obj_path = NULL; + gboolean ret; + + dbus_req = gpiodbus_request_skeleton_new(); + obj_path = g_strdup_printf("/io/gpiod1/requests/request%d", id); + + gpiodbus_request_set_props(dbus_req, request, chip_data->dbus_chip, + G_DBUS_OBJECT_MANAGER(chip_data->line_manager)); + + skeleton = gpiodbus_object_skeleton_new(obj_path); + gpiodbus_object_skeleton_set_request(skeleton, + GPIODBUS_REQUEST(dbus_req)); + + g_debug("exporting object for GPIO request: '%s'", obj_path); + + g_dbus_object_manager_server_export(self->request_manager, + G_DBUS_OBJECT_SKELETON(skeleton)); + + req_data = g_malloc0(sizeof(*req_data)); + req_data->chip_data = chip_data; + req_data->dbus_request = g_steal_pointer(&dbus_req); + req_data->id = id; + req_data->request = g_object_ref(request); + + g_signal_connect(req_data->dbus_request, "handle-release", + G_CALLBACK(gpiodbus_daemon_handle_release_lines), + req_data); + g_signal_connect(req_data->dbus_request, "handle-reconfigure-lines", + G_CALLBACK(gpiodbus_daemon_handle_reconfigure_lines), + req_data); + g_signal_connect(req_data->dbus_request, "handle-get-values", + G_CALLBACK(gpiodbus_daemon_handle_get_values), + req_data); + g_signal_connect(req_data->dbus_request, "handle-set-values", + G_CALLBACK(gpiodbus_daemon_handle_set_values), + req_data); + g_signal_connect(req_data->request, "edge-event", + G_CALLBACK(gpiodbus_daemon_on_edge_event), req_data); + + gpiodbus_lines_set_managed(req_data, TRUE); + + ret = g_hash_table_insert(self->requests, g_steal_pointer(&obj_path), + g_steal_pointer(&req_data)); + /* It's a programming bug if the request is already in the hashmap. */ + g_assert(ret); +} + +static gboolean +gpiodbus_daemon_handle_request_lines(GPIODBusChip *chip, + GDBusMethodInvocation *invocation, + GVariant *arg_line_cfg, + GVariant *arg_req_cfg, + gpointer user_data) +{ + GPIODBusDaemonChipData *chip_data = user_data; + g_autoptr(GPIODRequestConfig) req_cfg = NULL; + g_autoptr(GPIODLineRequest) request = NULL; + g_autoptr(GPIODLineConfig) line_cfg = NULL; + g_autofree gchar *line_cfg_str = NULL; + g_autofree gchar *req_cfg_str = NULL; + g_autofree gchar *response = NULL; + g_autoptr(GError) err = NULL; + const gchar *obj_path; + guint id; + + obj_path = g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(chip)); + line_cfg_str = g_variant_print(arg_line_cfg, FALSE); + req_cfg_str = g_variant_print(arg_req_cfg, FALSE); + + g_debug("line request received on chip '%s', line config: %s, request_config: %s", + obj_path, line_cfg_str, req_cfg_str); + + line_cfg = gpiodbus_line_config_from_variant(arg_line_cfg); + if (!line_cfg) { + g_critical("failed to convert method call arguments '%s' to line config", + line_cfg_str); + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "Invalid line configuration"); + goto out; + } + + req_cfg = gpiodbus_request_config_from_variant(arg_req_cfg); + if (!req_cfg) { + g_critical("failed to convert method call arguments '%s' to request config", + req_cfg_str); + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "Invalid request configuration"); + goto out; + } + + request = g_gpiod_chip_request_lines(chip_data->chip, req_cfg, line_cfg, + &err); + if (err) { + g_critical("failed to request GPIO lines on chip '%s': %s", + obj_path, err->message); + g_dbus_method_invocation_return_dbus_error(invocation, + "io.gpiod1.RequestFailed", err->message); + goto out; + } + + g_debug("line request succeeded on chip '%s'", obj_path); + + id = gpiodbus_id_alloc(chip_data->daemon->req_id_root); + gpiodbus_daemon_export_request(chip_data->daemon, request, + chip_data, id); + + response = g_strdup_printf("/io/gpiod1/requests/request%d", id); + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(o)", response)); + +out: + return G_SOURCE_CONTINUE; +} + +static void gpiodbus_daemon_export_chip(GPIODBusDaemon *self, GUdevDevice *dev) +{ + g_autofree GPIODBusDaemonChipData *chip_data = NULL; + g_autoptr(GDBusObjectManagerServer) manager = NULL; + g_autoptr(GPIODBusObjectSkeleton) skeleton = NULL; + const gchar *devname, *devpath, *obj_prefix; + g_autoptr(GPIODBusChip) dbus_chip = NULL; + g_autoptr(GHashTable) lines = NULL; + g_autofree gchar *obj_path = NULL; + g_autoptr(GPIODChip) chip = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + devname = g_udev_device_get_name(dev); + devpath = g_udev_device_get_device_file(dev); + obj_prefix = g_dbus_object_manager_get_object_path( + G_DBUS_OBJECT_MANAGER(self->chip_manager)); + + chip = g_gpiod_chip_new(devpath, &err); + if (!chip) { + g_critical("failed to open GPIO chip %s: %s", + devpath, err->message); + return; + } + + dbus_chip = gpiodbus_chip_skeleton_new(); + obj_path = g_strdup_printf("%s/%s", obj_prefix, devname); + + ret = gpiodbus_chip_set_props(dbus_chip, chip, &err); + if (!ret) { + g_critical("failed to set chip properties: %s", err->message); + return; + } + + skeleton = gpiodbus_object_skeleton_new(obj_path); + gpiodbus_object_skeleton_set_chip(skeleton, GPIODBUS_CHIP(dbus_chip)); + + g_debug("exporting object for GPIO chip: '%s'", obj_path); + + g_dbus_object_manager_server_export(self->chip_manager, + G_DBUS_OBJECT_SKELETON(skeleton)); + + lines = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, + gpiodbus_daemon_line_data_free); + manager = g_dbus_object_manager_server_new(obj_path); + + chip_data = g_malloc0(sizeof(*chip_data)); + chip_data->daemon = self; + chip_data->chip = g_steal_pointer(&chip); + chip_data->dbus_chip = g_steal_pointer(&dbus_chip); + chip_data->lines = g_steal_pointer(&lines); + chip_data->line_manager = g_steal_pointer(&manager); + + ret = gpiodbus_daemon_export_lines(self, chip_data); + if (!ret) { + g_dbus_object_manager_server_unexport(self->chip_manager, + obj_path); + return; + } + + g_signal_connect(chip_data->dbus_chip, "handle-request-lines", + G_CALLBACK(gpiodbus_daemon_handle_request_lines), + chip_data); + + ret = g_hash_table_insert(self->chips, g_strdup(devname), + g_steal_pointer(&chip_data)); + /* It's a programming bug if the chip is already in the hashmap. */ + g_assert(ret); +} + +static void gpiodbus_daemon_unexport_chip(GPIODBusDaemon *self, + GUdevDevice *dev) +{ + const gchar *name = g_udev_device_get_name(dev); + gboolean ret; + + ret = g_hash_table_remove(self->chips, name); + /* It's a programming bug if the chip was not in the hashmap. */ + if (!ret) + g_warning("chip '%s' is not registered - exporting failed?", + name); +} + +/* + * We can get two uevents per action per gpiochip. One is for the new-style + * character device, the other for legacy sysfs devices. We are only concerned + * with the former, which we can tell from the latter by the presence of + * the device file. + */ +static gboolean gpiodbus_daemon_is_gpiochip_device(GUdevDevice *dev) +{ + return g_udev_device_get_device_file(dev) != NULL; +} + +static void gpiodbus_daemon_on_uevent(GUdevClient *udev G_GNUC_UNUSED, + const gchar *action, GUdevDevice *dev, + gpointer data) +{ + GPIODBusDaemon *self = data; + + if (!gpiodbus_daemon_is_gpiochip_device(dev)) + return; + + g_debug("uevent: %s action on %s device", + action, g_udev_device_get_name(dev)); + + if (g_strcmp0(action, "bind") == 0) + gpiodbus_daemon_export_chip(self, dev); + else if (g_strcmp0(action, "unbind") == 0) + gpiodbus_daemon_unexport_chip(self, dev); +} + +static void gpiodbus_daemon_process_chip_dev(gpointer data, gpointer user_data) +{ + GPIODBusDaemon *daemon = user_data; + GUdevDevice *dev = data; + + if (gpiodbus_daemon_is_gpiochip_device(dev)) + gpiodbus_daemon_export_chip(daemon, dev); +} + +void gpiodbus_daemon_start(GPIODBusDaemon *self, GDBusConnection *con) +{ + g_autolist(GUdevDevice) devs = NULL; + + g_assert(self); + g_assert(!self->con); /* Don't allow to call this twice. */ + + self->con = g_object_ref(con); + + /* Subscribe for GPIO uevents. */ + g_signal_connect(self->udev, "uevent", + G_CALLBACK(gpiodbus_daemon_on_uevent), self); + + devs = g_udev_client_query_by_subsystem(self->udev, "gpio"); + g_list_foreach(devs, gpiodbus_daemon_process_chip_dev, self); + + g_dbus_object_manager_server_set_connection(self->chip_manager, + self->con); + g_dbus_object_manager_server_set_connection(self->request_manager, + self->con); + + g_debug("GPIO daemon now listening"); +} diff --git a/dbus/manager/daemon.h b/dbus/manager/daemon.h new file mode 100644 index 0000000..7674892 --- /dev/null +++ b/dbus/manager/daemon.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +#ifndef __GPIODBUS_DAEMON_H__ +#define __GPIODBUS_DAEMON_H__ + +#include +#include +#include + +G_DECLARE_FINAL_TYPE(GPIODBusDaemon, gpiodbus_daemon, + GPIODBUS, DAEMON, GObject); + +#define GPIODBUS_DAEMON_TYPE (gpiodbus_daemon_get_type()) +#define GPIODBUS_DAEMON(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + GPIODBUS_DAEMON_TYPE, GPIODBusDaemon)) + +GPIODBusDaemon *gpiodbus_daemon_new(void); +void gpiodbus_daemon_start(GPIODBusDaemon *daemon, GDBusConnection *con); + +#endif /* __GPIODBUS_DAEMON_H__ */ diff --git a/dbus/manager/gpio-manager.c b/dbus/manager/gpio-manager.c new file mode 100644 index 0000000..ad12fb7 --- /dev/null +++ b/dbus/manager/gpio-manager.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include +#include +#include +#include + +#include "daemon.h" + +static gboolean stop_main_loop_on_sig(gpointer data, const gchar *signame) +{ + GMainLoop *loop = data; + + g_debug("%s received", signame); + + g_main_loop_quit(loop); + + return G_SOURCE_REMOVE; +} + +static gboolean on_sigterm(gpointer data) +{ + return stop_main_loop_on_sig(data, "SIGTERM"); +} + +static gboolean on_sigint(gpointer data) +{ + return stop_main_loop_on_sig(data, "SIGINT"); +} + +static gboolean on_sighup(gpointer data G_GNUC_UNUSED) +{ + g_debug("SIGHUB received, ignoring"); + + return G_SOURCE_CONTINUE; +} + +static void on_bus_acquired(GDBusConnection *con, + const gchar *name G_GNUC_UNUSED, + gpointer data) +{ + GPIODBusDaemon *daemon = data; + + g_debug("DBus connection acquired"); + + gpiodbus_daemon_start(daemon, con); +} + +static void on_name_acquired(GDBusConnection *con G_GNUC_UNUSED, + const gchar *name, gpointer data G_GNUC_UNUSED) +{ + g_debug("DBus name acquired: '%s'", name); +} + +static void on_name_lost(GDBusConnection *con, + const gchar *name, gpointer data G_GNUC_UNUSED) +{ + g_debug("DBus name lost: '%s'", name); + + if (!con) + g_error("unable to make connection to the bus"); + + if (g_dbus_connection_is_closed(con)) + g_error("connection to the bus closed"); + + g_error("name '%s' lost on the bus", name); +} + +static void print_version_and_exit(void) +{ + g_print("%s (libgpiod) v%s\n", g_get_prgname(), g_gpiod_api_version()); + + exit(EXIT_SUCCESS); +} + +static void parse_opts(int argc, char **argv) +{ + gboolean ret, opt_debug = FALSE, opt_version = FALSE; + g_autoptr(GOptionContext) ctx = NULL; + g_auto(GStrv) remaining = NULL; + g_autoptr(GError) err = NULL; + + const GOptionEntry opts[] = { + { + .long_name = "debug", + .short_name = 'd', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_NONE, + .arg_data = &opt_debug, + .description = "Emit additional debug log messages.", + }, + { + .long_name = "version", + .short_name = 'v', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_NONE, + .arg_data = &opt_version, + .description = "Print version and exit.", + }, + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &remaining, + }, + { } + }; + + ctx = g_option_context_new(NULL); + g_option_context_set_summary(ctx, "DBus daemon managing GPIOs."); + g_option_context_add_main_entries(ctx, opts, NULL); + + ret = g_option_context_parse(ctx, &argc, &argv, &err); + if (!ret) { + g_printerr("Option parsing failed: %s\n\nUse %s --help\n", + err->message, g_get_prgname()); + exit(EXIT_FAILURE); + } + + if (remaining) { + g_printerr("Option parsing failed: additional arguments are not allowed\n"); + exit(EXIT_FAILURE); + } + + if (opt_version) + print_version_and_exit(); + + if (opt_debug) + g_setenv("G_MESSAGES_DEBUG", "gpio-manager", FALSE); +} + +int main(int argc, char **argv) +{ + g_autoptr(GPIODBusDaemon) daemon = NULL; + g_autofree gchar *basename = NULL; + g_autoptr(GMainLoop) loop = NULL; + guint bus_id; + + basename = g_path_get_basename(argv[0]); + g_set_prgname(basename); + parse_opts(argc, argv); + + g_message("initializing %s", g_get_prgname()); + + loop = g_main_loop_new(NULL, FALSE); + daemon = gpiodbus_daemon_new(); + + g_unix_signal_add(SIGTERM, on_sigterm, loop); + g_unix_signal_add(SIGINT, on_sigint, loop); + g_unix_signal_add(SIGHUP, on_sighup, NULL); /* Ignore SIGHUP. */ + + bus_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, "io.gpiod1", + G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, + on_name_acquired, on_name_lost, daemon, NULL); + + g_message("%s started", g_get_prgname()); + + g_main_loop_run(loop); + + g_bus_unown_name(bus_id); + + g_message("%s exiting", g_get_prgname()); + + return EXIT_SUCCESS; +} diff --git a/dbus/manager/helpers.c b/dbus/manager/helpers.c new file mode 100644 index 0000000..c19ff59 --- /dev/null +++ b/dbus/manager/helpers.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski + +#include "helpers.h" + +gint gpiodbus_id_cmp(gconstpointer a, gconstpointer b, + gpointer user_data G_GNUC_UNUSED) +{ + const gint *id_a = a; + const gint *id_b = b; + + if (*id_a < *id_b) + return -1; + else if (*id_a > *id_b) + return 1; + + return 0; +} + +static gboolean find_lowest(gpointer key, gpointer value G_GNUC_UNUSED, + gpointer data) +{ + gint *lowest = data, *curr = key; + + if (*lowest == *curr) + (*lowest)++; + + return FALSE; +} + +gint gpiodbus_id_alloc(GTree *id_root) +{ + gint lowest = 0, *key; + + g_tree_foreach(id_root, find_lowest, &lowest); + + key = g_malloc(sizeof(*key)); + *key = lowest; + g_tree_insert(id_root, key, NULL); + + return lowest; +} + +void gpiodbus_id_free(GTree *id_root, gint id) +{ + g_assert(g_tree_remove(id_root, &id)); +} + +gboolean +gpiodbus_chip_set_props(GPIODBusChip *skeleton, GPIODChip *chip, GError **err) +{ + g_autoptr(GPIODChipInfo) info = NULL; + + info = g_gpiod_chip_get_info(chip, err); + if (!info) + return FALSE; + + gpiodbus_chip_set_name(skeleton, g_gpiod_chip_info_get_name(info)); + gpiodbus_chip_set_label(skeleton, g_gpiod_chip_info_get_label(info)); + gpiodbus_chip_set_num_lines(skeleton, + g_gpiod_chip_info_get_num_lines(info)); + gpiodbus_chip_set_path(skeleton, g_gpiod_chip_get_path(chip)); + g_dbus_interface_skeleton_flush(G_DBUS_INTERFACE_SKELETON(skeleton)); + + return TRUE; +} + +static const gchar *map_direction(GPIODLineDirection direction) +{ + switch (direction) { + case G_GPIOD_LINE_DIRECTION_INPUT: + return "input"; + case G_GPIOD_LINE_DIRECTION_OUTPUT: + return "output"; + default: + g_error("invalid direction value returned by libgpiod-glib"); + } +} + +static const gchar *map_edge(GPIODLineEdge edge) +{ + switch (edge) { + case G_GPIOD_LINE_EDGE_NONE: + return "none"; + case G_GPIOD_LINE_EDGE_FALLING: + return "falling"; + case G_GPIOD_LINE_EDGE_RISING: + return "rising"; + case G_GPIOD_LINE_EDGE_BOTH: + return "both"; + default: + g_error("invalid edge value returned by libgpiod-glib"); + } +} + +static const gchar *map_bias(GPIODLineBias bias) +{ + switch (bias) { + case G_GPIOD_LINE_BIAS_UNKNOWN: + return "unknown"; + case G_GPIOD_LINE_BIAS_DISABLED: + return "disabled"; + case G_GPIOD_LINE_BIAS_PULL_UP: + return "pull-up"; + case G_GPIOD_LINE_BIAS_PULL_DOWN: + return "pull-down"; + default: + g_error("invalid bias value returned by libgpiod-glib"); + } +} + +static const gchar *map_drive(GPIODLineDrive drive) +{ + switch (drive) { + case G_GPIOD_LINE_DRIVE_PUSH_PULL: + return "push-pull"; + case G_GPIOD_LINE_DRIVE_OPEN_DRAIN: + return "open-drain"; + case G_GPIOD_LINE_DRIVE_OPEN_SOURCE: + return "open-source"; + default: + g_error("invalid drive value returned by libgpiod-glib"); + } +} + +static const gchar *map_clock(GPIODLineClock event_clock) +{ + switch (event_clock) { + case G_GPIOD_LINE_CLOCK_MONOTONIC: + return "monotonic"; + case G_GPIOD_LINE_CLOCK_REALTIME: + return "realtime"; + case G_GPIOD_LINE_CLOCK_HTE: + return "hte"; + default: + g_error("invalid event clock value returned by libgpiod-glib"); + } +} + +void gpiodbus_line_set_props(GPIODBusLine *skeleton, GPIODLineInfo *info) +{ + gpiodbus_line_set_offset(skeleton, g_gpiod_line_info_get_offset(info)); + gpiodbus_line_set_name(skeleton, g_gpiod_line_info_get_name(info)); + gpiodbus_line_set_used(skeleton, g_gpiod_line_info_is_used(info)); + gpiodbus_line_set_consumer(skeleton, + g_gpiod_line_info_get_consumer(info)); + gpiodbus_line_set_direction(skeleton, + map_direction(g_gpiod_line_info_get_direction(info))); + gpiodbus_line_set_edge_detection(skeleton, + map_edge(g_gpiod_line_info_get_edge_detection(info))); + gpiodbus_line_set_bias(skeleton, + map_bias(g_gpiod_line_info_get_bias(info))); + gpiodbus_line_set_drive(skeleton, + map_drive(g_gpiod_line_info_get_drive(info))); + gpiodbus_line_set_active_low(skeleton, + g_gpiod_line_info_is_active_low(info)); + gpiodbus_line_set_debounced(skeleton, + g_gpiod_line_info_is_debounced(info)); + gpiodbus_line_set_debounce_period_us(skeleton, + g_gpiod_line_info_get_debounce_period_us(info)); + gpiodbus_line_set_event_clock(skeleton, + map_clock(g_gpiod_line_info_get_event_clock(info))); + g_dbus_interface_skeleton_flush(G_DBUS_INTERFACE_SKELETON(skeleton)); +} + +static gint line_offset_cmp(gconstpointer a, gconstpointer b) +{ + GPIODBusObject *line_obj = (GPIODBusObject *)a; + GPIODBusLine *line; + const guint *offset = b; + + line = gpiodbus_object_peek_line(line_obj); + + return gpiodbus_line_get_offset(line) != *offset; +} + +void gpiodbus_request_set_props(GPIODBusRequest *skeleton, + GPIODLineRequest *request, GPIODBusChip *chip, + GDBusObjectManager *line_manager) +{ + g_autolist(GPIODBusObject) line_objs = NULL; + g_autoptr(GStrvBuilder) builder = NULL; + g_autoptr(GArray) offsets = NULL; + g_auto(GStrv) paths = NULL; + GList *found; + guint i; + + offsets = g_gpiod_line_request_get_requested_offsets(request); + line_objs = g_dbus_object_manager_get_objects(line_manager); + builder = g_strv_builder_new(); + + for (i = 0; i < offsets->len; i++) { + found = g_list_find_custom(line_objs, + &g_array_index(offsets, guint, i), + line_offset_cmp); + if (found) + g_strv_builder_add(builder, + g_dbus_object_get_object_path( + G_DBUS_OBJECT(found->data))); + } + + paths = g_strv_builder_end(builder); + + gpiodbus_request_set_chip_path(skeleton, + g_dbus_interface_skeleton_get_object_path( + G_DBUS_INTERFACE_SKELETON(chip))); + gpiodbus_request_set_line_paths(skeleton, (const gchar *const *)paths); + g_dbus_interface_skeleton_flush(G_DBUS_INTERFACE_SKELETON(skeleton)); +} + +static gboolean +set_settings_from_variant(GPIODLineSettings *settings, const gchar *key, + GVariant *val) +{ + GPIODLineDirection direction; + GPIODLineClock event_clock; + GPIODLineDrive drive; + GPIODLineEdge edge; + GPIODLineBias bias; + const gchar *str; + + /* FIXME: Make it into a nice set of hashmaps and callbacks. */ + if (g_strcmp0(key, "direction") == 0) { + str = g_variant_get_string(val, NULL); + + if (g_strcmp0(str, "input") == 0) { + direction = G_GPIOD_LINE_DIRECTION_INPUT; + } else if (g_strcmp0(str, "output") == 0) { + direction = G_GPIOD_LINE_DIRECTION_OUTPUT; + } else if (g_strcmp0(str, "as-is") == 0) { + direction = G_GPIOD_LINE_DIRECTION_AS_IS; + } else { + g_critical("invalid direction value received: '%s'", + str); + return FALSE; + } + + g_gpiod_line_settings_set_direction(settings, direction); + } else if (g_strcmp0(key, "edge") == 0) { + str = g_variant_get_string(val, NULL); + + if (g_strcmp0(str, "falling") == 0) { + edge = G_GPIOD_LINE_EDGE_FALLING; + } else if (g_strcmp0(str, "rising") == 0) { + edge = G_GPIOD_LINE_EDGE_RISING; + } else if (g_strcmp0(str, "both") == 0) { + edge = G_GPIOD_LINE_EDGE_BOTH; + } else { + g_critical("invalid edge value received: '%s'", str); + return FALSE; + } + + g_gpiod_line_settings_set_edge_detection(settings, edge); + } else if (g_strcmp0(key, "active-low") == 0) { + if (g_variant_get_boolean(val)) + g_gpiod_line_settings_set_active_low(settings, TRUE); + } else if (g_strcmp0(key, "bias") == 0) { + str = g_variant_get_string(val, NULL); + + if (g_strcmp0(str, "as-is") == 0) { + bias = G_GPIOD_LINE_BIAS_AS_IS; + } else if (g_strcmp0(str, "pull-up") == 0) { + bias = G_GPIOD_LINE_BIAS_PULL_UP; + } else if (g_strcmp0(str, "pull-down") == 0) { + bias = G_GPIOD_LINE_BIAS_PULL_DOWN; + } else if (g_strcmp0(str, "disabled") == 0) { + bias = G_GPIOD_LINE_BIAS_DISABLED; + } else { + g_critical("invalid bias value received: '%s'", str); + return FALSE; + } + + g_gpiod_line_settings_set_bias(settings, bias); + } else if (g_strcmp0(key, "drive") == 0) { + str = g_variant_get_string(val, NULL); + + if (g_strcmp0(str, "push-pull") == 0) { + drive = G_GPIOD_LINE_DRIVE_PUSH_PULL; + } else if (g_strcmp0(str, "open-drain") == 0) { + drive = G_GPIOD_LINE_DRIVE_OPEN_DRAIN; + } else if (g_strcmp0(str, "open-source") == 0) { + drive = G_GPIOD_LINE_DRIVE_OPEN_SOURCE; + } else { + g_critical("invalid drive value received: '%s'", str); + return FALSE; + } + + g_gpiod_line_settings_set_drive(settings, drive); + } else if (g_strcmp0(key, "debounce-period") == 0) { + g_gpiod_line_settings_set_debounce_period_us(settings, + g_variant_get_int64(val)); + } else if (g_strcmp0(key, "event-clock") == 0) { + str = g_variant_get_string(val, NULL); + + if (g_strcmp0(str, "monotonic") == 0) { + event_clock = G_GPIOD_LINE_CLOCK_MONOTONIC; + } else if (g_strcmp0(str, "realtime") == 0) { + event_clock = G_GPIOD_LINE_CLOCK_REALTIME; + } else if (g_strcmp0(str, "hte") == 0) { + event_clock = G_GPIOD_LINE_CLOCK_HTE; + } else { + g_critical("invalid event clock value received: '%s'", + str); + return FALSE; + } + + g_gpiod_line_settings_set_event_clock(settings, event_clock); + } else { + g_critical("invalid config option received: '%s'", key); + return FALSE; + } + + return TRUE; +} + +GPIODLineConfig *gpiodbus_line_config_from_variant(GVariant *variant) +{ + g_autoptr(GPIODLineSettings) settings = NULL; + g_autoptr(GPIODLineConfig) config = NULL; + g_autoptr(GVariant) output_values_v = NULL; + g_autoptr(GVariant) line_configs_v = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + GVariantIter iter0, iter1; + guint offset; + gboolean ret; + GVariant *v; + gchar *k; + gint val; + + line_configs_v = g_variant_get_child_value(variant, 0); + output_values_v = g_variant_get_child_value(variant, 1); + + config = g_gpiod_line_config_new(); + settings = g_gpiod_line_settings_new(NULL); + + g_variant_iter_init(&iter0, line_configs_v); + while ((v = g_variant_iter_next_value(&iter0))) { + g_autoptr(GVariant) line_settings_v = NULL; + g_autoptr(GVariant) line_config_v = v; + g_autoptr(GVariant) offsets_v = NULL; + g_autoptr(GArray) offsets = NULL; + + offsets_v = g_variant_get_child_value(line_config_v, 0); + line_settings_v = g_variant_get_child_value(line_config_v, 1); + + g_gpiod_line_settings_reset(settings); + g_variant_iter_init(&iter1, line_settings_v); + while (g_variant_iter_next(&iter1, "{sv}", &k, &v)) { + g_autoptr(GVariant) val = v; + g_autofree gchar *key = k; + + ret = set_settings_from_variant(settings, key, val); + if (!ret) + return NULL; + } + + offsets = g_array_sized_new(FALSE, TRUE, sizeof(guint), + g_variant_n_children(offsets_v)); + g_variant_iter_init(&iter1, offsets_v); + while (g_variant_iter_next(&iter1, "u", &offset)) + g_array_append_val(offsets, offset); + + ret = g_gpiod_line_config_add_line_settings(config, offsets, + settings, &err); + if (!ret) { + g_critical("failed to add line settings: %s", + err->message); + return NULL; + } + } + + values = g_array_sized_new(FALSE, TRUE, sizeof(gint), + g_variant_n_children(output_values_v)); + g_variant_iter_init(&iter0, output_values_v); + while (g_variant_iter_next(&iter0, "i", &val)) + g_array_append_val(values, val); + + if (values->len > 0) { + ret = g_gpiod_line_config_set_output_values(config, values, + &err); + if (!ret) { + g_critical("failed to set output values: %s", + err->message); + return NULL; + } + } + + return g_object_ref(config); +} + +GPIODRequestConfig *gpiodbus_request_config_from_variant(GVariant *variant) +{ + g_autoptr(GPIODRequestConfig) config = NULL; + GVariantIter iter; + GVariant *v; + gchar *k; + + config = g_gpiod_request_config_new(NULL); + + g_variant_iter_init(&iter, variant); + while (g_variant_iter_next(&iter, "{sv}", &k, &v)) { + g_autoptr(GVariant) val = v; + g_autofree gchar *key = k; + + if (g_strcmp0(key, "consumer") == 0) { + g_gpiod_request_config_set_consumer(config, + g_variant_get_string(val, NULL)); + } else if (g_strcmp0(key, "event-buffer-size") == 0) { + g_gpiod_request_config_set_event_buffer_size(config, + g_variant_get_uint32(val)); + } else { + g_critical("invalid request config option received: '%s'", + key); + return NULL; + } + } + + return g_object_ref(config); +} diff --git a/dbus/manager/helpers.h b/dbus/manager/helpers.h new file mode 100644 index 0000000..03b3240 --- /dev/null +++ b/dbus/manager/helpers.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2023 Bartosz Golaszewski */ + +#ifndef __GPIODBUS_HELPERS_H__ +#define __GPIODBUS_HELPERS_H__ + +#include +#include +#include +#include + +gint gpiodbus_id_cmp(gconstpointer a, gconstpointer b, gpointer user_data); +gint gpiodbus_id_alloc(GTree *id_root); +void gpiodbus_id_free(GTree *id_root, gint id); +gboolean +gpiodbus_chip_set_props(GPIODBusChip *skeleton, GPIODChip *chip, GError **err); +void gpiodbus_line_set_props(GPIODBusLine *skeleton, GPIODLineInfo *info); +void gpiodbus_request_set_props(GPIODBusRequest *skeleton, + GPIODLineRequest *request, GPIODBusChip *chip, + GDBusObjectManager *line_manager); +GPIODLineConfig *gpiodbus_line_config_from_variant(GVariant *variant); +GPIODRequestConfig *gpiodbus_request_config_from_variant(GVariant *variant); + +#endif /* __GPIODBUS_HELPERS_H__ */ From patchwork Fri Jun 28 18:58:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808377 Received: from mail-wr1-f44.google.com (mail-wr1-f44.google.com [209.85.221.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1617655E58 for ; Fri, 28 Jun 2024 18:59:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601158; cv=none; b=mRHOoB35fKulCGE1szvqpytN2/seD1p7UkLF6odEe5e1PEqK9uAWmfXySnqZ2Vb36UkopEwAAb/A9ruPdQYN7RJ7mx2WduySA5CU4k6KbHp7KlJ0kGavh0Ldx7F1DqWjEHWXp2cCoBpSPmmMm+swKlvXBae7xjeSSE33mQJoBNE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601158; c=relaxed/simple; bh=M/nxP1APEaOQdL1Fp1K6cdVWiGbW9uHGM+i0lqffMDY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PU/DSZCemz7G560c07nL62q2DuLEKcKlPlo7dDHLfvrkNDDAn4X28A0Jf93+FhhO0b5yWGdLmOSWNBpEnmu5hbhSXT61OnjsixRDonTNctfFcBVW/J8XnAGhra1tOi7bGOuW0QgCzQfvhnXxvmVKsjQnZJuQBb8krnh3v9pXOgw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=H1T6n+hW; arc=none smtp.client-ip=209.85.221.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="H1T6n+hW" Received: by mail-wr1-f44.google.com with SMTP id ffacd0b85a97d-3674e1931b7so699043f8f.2 for ; Fri, 28 Jun 2024 11:59:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601154; x=1720205954; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=f2eBDW364M8zAI7+85aEbywY4CV9ymJQrKq8441onCI=; b=H1T6n+hWPA27HIMLbqPyXvxjTBYH/EZqITJkfrBJ5agCheHcdjVZvOH86vQwUoC5E/ ldsXBcV//RX8LqLdUOWBTEXiEwllknbMnvi2cxHpVyeVyfdRXNNXF5gYt+jKswxgJe4S SMtKNe9+BvBDiSQ8UF/gzclBGn3czbTnq3xN5CHgGXt8S+UYlrc41VWMIfE3NsVpQ4/Q QzVDylgYYOG/OdJm/Om85c3noc0f+Ph9PLq7CubYlhVnlsbGYeb5CmVi3XM2rIpPjEzb eCws73UImY72kY6e/tcWsN8fFYl6w1f3gzJ652j1pxI401F9Yvm8nk0A4Cmo9TvBayB8 +xXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601154; x=1720205954; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=f2eBDW364M8zAI7+85aEbywY4CV9ymJQrKq8441onCI=; b=aJl5GO4hQCBGY+C7L2En6RM3OQl/D2jVuEofx7wWntSCRiTNHDKZJo7lVKSqGs90vC 3W8Zc8I+7h3oxzgYya2N/ZEO1O+0CZRrRgMG5mSoZlqSiHWgPhBY6SKbRLQhKzr8GkNT +w2HviI3Z7ew6e5+uo7ONlGf9vJFde0pzMEBAjVUu5f83M+LPPpPru33OMonvUiocSqU yKE1Hzmn7FCAfq9OBV1ul7UJzJi6MPVLRmvV2uWAv0g8MLg8OMOHZ0Z5lgYq6Zhb5vTQ hMD+qihgScCYX2k3uo1r1gTUvkz4Mi8Xd3RK7hfsV/o0i362hjfsBnXEN2CZ7mkvORat c++Q== X-Forwarded-Encrypted: i=1; AJvYcCUmUwjL2TGYLoWmO1aGWwNKQyk7rgNii0TuMl+pgy2gFqvP+fAxTwjAl4w8JJqs/tgSIP95rxW8kCmzrPUs1h8SRldBfKtqyU7e1g== X-Gm-Message-State: AOJu0Yyzi1Or0xkk/o/8bhuTLaOyM6nJM1MNvGjfYpsvAy5HW9euTy9z OYuV571vIiJYwaSYDAljXhsCf9nMHKgMC6D0ENRR5Pr3y+bKOuIZGS038m2pVpY= X-Google-Smtp-Source: AGHT+IFPpOpeDwWtsRGdlzPOa7rLnNN7faWC+Kx5yQeLDzh/S+QWb17Wt2y98riieKcxe6zI9yBPMw== X-Received: by 2002:a05:6000:4010:b0:367:47e6:c7d8 with SMTP id ffacd0b85a97d-36747e6ca56mr4618723f8f.9.1719601154616; Fri, 28 Jun 2024 11:59:14 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:14 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:33 +0200 Subject: [PATCH RESEND libgpiod v2 14/18] dbus: add tests Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-14-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=29190; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=nI6Vwx8s+qcXK1eIPmjv63mVe69KqjXQ4/yD5nDi88I=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfuawM73+7zi2xGChax3uY5zxt6Fu41rR/Yt 9K0pIgCQW+JAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H7gAKCRARpy6gFHHX ckqTEADHYSZs/TfbtLZjsgtpb76pmeEaZrQl628uECMdbyKalXdznxOJS5wSN5Srf/cxIZlp5MD DHX5JTcjAjBg/W26cgkvVsIZvWzcyB+D3T8XoNeKk4DBj4T6tRhk40ZhheDlBc9HGmQRWSzRQRM n11LQEuxPhCBqpLKuRTFv7HiCZXKYQWHQAuR4StblUJX433+bzJWFU2oeEfdy0iGuMw03UWomfU egP++m1nY688XAq1ZaWDnRkdjV1XP0cirFDl59S6cec/cCpTS7AiB7F9mfeoZ/n+Evxt+4G+Ar+ hT2leP0QJNuAoZ7LedUmSWfihwk7+ne+VUkAo9J3EJalisBW1Lrv427DwbRQ/JdtOU/dmYDj9a1 lM5G44STSFz6ViklPqYwSHqwWbc+Unod10wq+dwdKZOXrpznYlSr5ZKxMsrKJy0mNuya8cG2XaM +n6vys80Ego9RKeqXEUt7lhaO95WRs8ToieUA/k/ICL7BQeAQwqG+zc9xTgUzTZIByuX0Y8CjPO yVYeUvOBvB0OFU0PZWUq5LJvKUjOUTAkh/THZrhZEr5pxbLBj4b/TUCXLe7AtsQqPCVkT8RWo0r jgvZ0mesVycAgLt2Ox2Cm7NFI0EjXdJ5tBNh6q2sW86QbgLZ7ExkNVt633mLo1UAjKxSpx3yIHp IoyVDx7D3NtZQ9A== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add a set of test-cases verifying the functionality of the gpio-manager daemon. Signed-off-by: Bartosz Golaszewski --- dbus/tests/daemon-process.c | 129 +++++++++++++++++++++++++ dbus/tests/daemon-process.h | 20 ++++ dbus/tests/helpers.c | 107 ++++++++++++++++++++ dbus/tests/helpers.h | 112 +++++++++++++++++++++ dbus/tests/tests-chip.c | 133 +++++++++++++++++++++++++ dbus/tests/tests-line.c | 231 ++++++++++++++++++++++++++++++++++++++++++++ dbus/tests/tests-request.c | 116 ++++++++++++++++++++++ 7 files changed, 848 insertions(+) diff --git a/dbus/tests/daemon-process.c b/dbus/tests/daemon-process.c new file mode 100644 index 0000000..9eec71b --- /dev/null +++ b/dbus/tests/daemon-process.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include + +#include "daemon-process.h" + +struct _GPIODBusDaemonProcess { + GObject parent_instance; + GSubprocess *proc; +}; + +G_DEFINE_TYPE(GPIODBusDaemonProcess, gpiodbus_daemon_process, G_TYPE_OBJECT); + +static gboolean on_timeout(gpointer data G_GNUC_UNUSED) +{ + g_error("timeout reached waiting for the daemon name to appear on the system bus"); + + return G_SOURCE_REMOVE; +} + +static void on_name_appeared(GDBusConnection *con G_GNUC_UNUSED, + const gchar *name G_GNUC_UNUSED, + const gchar *name_owner G_GNUC_UNUSED, + gpointer data) +{ + gboolean *name_state = data; + + *name_state = TRUE; +} + +static void gpiodbus_daemon_process_constructed(GObject *obj) +{ + GPIODBusDaemonProcess *self = GPIODBUS_DAEMON_PROCESS_OBJ(obj); + const gchar *path = g_getenv("GPIODBUS_TEST_DAEMON_PATH"); + g_autoptr(GDBusConnection) con = NULL; + g_autofree gchar *addr = NULL; + g_autoptr(GError) err = NULL; + gboolean name_state = FALSE; + guint watch_id, timeout_id; + + if (!path) + g_error("GPIODBUS_TEST_DAEMON_PATH environment variable must be set"); + + addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SYSTEM, NULL, &err); + if (!addr) + g_error("failed to get an address for system bus: %s", + err->message); + + con = g_dbus_connection_new_for_address_sync(addr, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + NULL, NULL, &err); + if (!con) + g_error("failed to get a dbus connection: %s", err->message); + + watch_id = g_bus_watch_name_on_connection(con, "io.gpiod1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + on_name_appeared, NULL, + &name_state, NULL); + + self->proc = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_SILENCE | + G_SUBPROCESS_FLAGS_STDERR_SILENCE, + &err, path, NULL); + if (!self->proc) + g_error("failed to launch the gpio-manager process: %s", + err->message); + + timeout_id = g_timeout_add_seconds(5, on_timeout, NULL); + + while (!name_state) + g_main_context_iteration(NULL, TRUE); + + g_bus_unwatch_name(watch_id); + g_source_remove(timeout_id); + + G_OBJECT_CLASS(gpiodbus_daemon_process_parent_class)->constructed(obj); +} + +static void gpiodbus_daemon_process_kill(GSubprocess *proc) +{ + g_autoptr(GError) err = NULL; + gint status; + + g_subprocess_send_signal(proc, SIGTERM); + g_subprocess_wait(proc, NULL, &err); + if (err) + g_error("failed to collect the exit status of gpio-manager: %s", + err->message); + + if (!g_subprocess_get_if_exited(proc)) + g_error("dbus-manager process did not exit normally"); + + status = g_subprocess_get_exit_status(proc); + if (status != 0) + g_error("dbus-manager process exited with a non-zero status: %d", + status); + + g_object_unref(proc); +} + +static void gpiodbus_daemon_process_dispose(GObject *obj) +{ + GPIODBusDaemonProcess *self = GPIODBUS_DAEMON_PROCESS_OBJ(obj); + + g_clear_pointer(&self->proc, gpiodbus_daemon_process_kill); + + G_OBJECT_CLASS(gpiodbus_daemon_process_parent_class)->dispose(obj); +} + +static void +gpiodbus_daemon_process_class_init(GPIODBusDaemonProcessClass *proc_class) +{ + GObjectClass *class = G_OBJECT_CLASS(proc_class); + + class->constructed = gpiodbus_daemon_process_constructed; + class->dispose = gpiodbus_daemon_process_dispose; +} + +static void gpiodbus_daemon_process_init(GPIODBusDaemonProcess *self) +{ + self->proc = NULL; +} + +GPIODBusDaemonProcess *gpiodbus_daemon_process_new(void) +{ + return g_object_new(GPIODBUS_DAEMON_PROCESS_TYPE, NULL); +} diff --git a/dbus/tests/daemon-process.h b/dbus/tests/daemon-process.h new file mode 100644 index 0000000..24d22a8 --- /dev/null +++ b/dbus/tests/daemon-process.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +#ifndef __GPIODBUS_TEST_DAEMON_PROCESS_H__ +#define __GPIODBUS_TEST_DAEMON_PROCESS_H__ + +#include + +G_DECLARE_FINAL_TYPE(GPIODBusDaemonProcess, gpiodbus_daemon_process, + GPIODBUS, DAEMON_PROCESS, GObject); + +#define GPIODBUS_DAEMON_PROCESS_TYPE (gpiodbus_daemon_process_get_type()) +#define GPIODBUS_DAEMON_PROCESS_OBJ(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST(obj, \ + GPIODBUS_DAEMON_PROCESS_TYPE, \ + GPIODBusDaemonProcess)) + +GPIODBusDaemonProcess *gpiodbus_daemon_process_new(void); + +#endif /* __GPIODBUS_TEST_DAEMON_PROCESS_H__ */ diff --git a/dbus/tests/helpers.c b/dbus/tests/helpers.c new file mode 100644 index 0000000..c2f5142 --- /dev/null +++ b/dbus/tests/helpers.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include + +#include "helpers.h" + +GDBusConnection *gpiodbus_test_get_dbus_connection(void) +{ + g_autoptr(GDBusConnection) con = NULL; + g_autofree gchar *addr = NULL; + g_autoptr(GError) err = NULL; + + addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SYSTEM, NULL, &err); + if (!addr) + g_error("Failed to get address on the bus: %s", err->message); + + con = g_dbus_connection_new_for_address_sync(addr, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + NULL, NULL, &err); + if (!con) + g_error("Failed to get system bus connection: %s", + err->message); + + return g_object_ref(con); +} + +typedef struct { + gboolean *added; + gchar *obj_path; +} OnObjectAddedData; + +static void on_object_added(GDBusObjectManager *manager G_GNUC_UNUSED, + GPIODBusObject *object, gpointer data) +{ + OnObjectAddedData *cb_data = data; + const gchar *path; + + path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object)); + + if (g_strcmp0(path, cb_data->obj_path) == 0) + *cb_data->added = TRUE; +} + +static gboolean on_timeout(gpointer data G_GNUC_UNUSED) +{ + g_error("timeout reached waiting for the gpiochip interface to appear on the bus"); + + return G_SOURCE_REMOVE; +} + +void gpiodbus_test_wait_for_sim_intf(GPIOSimChip *sim) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + g_autoptr(GDBusConnection) con = NULL; + g_autoptr(GPIODBusObject) obj = NULL; + g_autoptr(GError) err = NULL; + g_autofree gchar *obj_path; + OnObjectAddedData cb_data; + gboolean added = FALSE; + guint timeout_id; + + con = gpiodbus_test_get_dbus_connection(); + if (!con) + g_error("failed to obtain a bus connection: %s", err->message); + + obj_path = g_strdup_printf("/io/gpiod1/chips/%s", + g_gpiosim_chip_get_name(sim)); + + cb_data.added = &added; + cb_data.obj_path = obj_path; + + manager = gpiodbus_object_manager_client_new_sync(con, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + "io.gpiod1", "/io/gpiod1/chips", NULL, &err); + if (!manager) + g_error("failed to create the object manager client: %s", + err->message); + + g_signal_connect(manager, "object-added", G_CALLBACK(on_object_added), + &cb_data); + + obj = GPIODBUS_OBJECT(g_dbus_object_manager_get_object(manager, + obj_path)); + if (obj) { + if (g_strcmp0(g_dbus_object_get_object_path(G_DBUS_OBJECT(obj)), + obj_path) == 0) + added = TRUE; + } + + timeout_id = g_timeout_add_seconds(5, on_timeout, NULL); + + while (!added) + g_main_context_iteration(NULL, TRUE); + + g_source_remove(timeout_id); +} + +GVariant *gpiodbus_test_make_empty_request_config(void) +{ + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + + return g_variant_ref_sink(g_variant_builder_end(&builder)); +} diff --git a/dbus/tests/helpers.h b/dbus/tests/helpers.h new file mode 100644 index 0000000..6f789dd --- /dev/null +++ b/dbus/tests/helpers.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +#ifndef __GPIODBUS_TEST_INTERNAL_H__ +#define __GPIODBUS_TEST_INTERNAL_H__ + +#include +#include +#include +#include + +#define __gpiodbus_test_check_gboolean_and_error(_ret, _err) \ + do { \ + g_assert_true(_ret); \ + g_assert_no_error(_err); \ + gpiod_test_return_if_failed(); \ + } while (0) + +#define __gpiodbus_test_check_nonnull_and_error(_ptr, _err) \ + do { \ + g_assert_nonnull(_ptr); \ + g_assert_no_error(_err); \ + gpiod_test_return_if_failed(); \ + } while (0) + +#define gpiodbus_test_get_chip_proxy_or_fail(_obj_path) \ + ({ \ + g_autoptr(GDBusConnection) _con = NULL; \ + g_autoptr(GError) _err = NULL; \ + g_autoptr(GPIODBusChip) _chip = NULL; \ + _con = gpiodbus_test_get_dbus_connection(); \ + _chip = gpiodbus_chip_proxy_new_sync(_con, \ + G_DBUS_PROXY_FLAGS_NONE, \ + "io.gpiod1", _obj_path, \ + NULL, &_err); \ + __gpiodbus_test_check_nonnull_and_error(_chip, _err); \ + g_object_ref(_chip); \ + }) + +#define gpiodbus_test_get_line_proxy_or_fail(_obj_path) \ + ({ \ + g_autoptr(GDBusConnection) _con = NULL; \ + g_autoptr(GError) _err = NULL; \ + g_autoptr(GPIODBusLine) _line = NULL; \ + _con = gpiodbus_test_get_dbus_connection(); \ + _line = gpiodbus_line_proxy_new_sync(_con, \ + G_DBUS_PROXY_FLAGS_NONE, \ + "io.gpiod1", _obj_path, \ + NULL, &_err); \ + __gpiodbus_test_check_nonnull_and_error(_line, _err); \ + g_object_ref(_line); \ + }) + +#define gpiodbus_test_get_request_proxy_or_fail(_obj_path) \ + ({ \ + g_autoptr(GDBusConnection) _con = NULL; \ + g_autoptr(GError) _err = NULL; \ + g_autoptr(GPIODBusRequest) _req = NULL; \ + _con = gpiodbus_test_get_dbus_connection(); \ + _req = gpiodbus_request_proxy_new_sync(_con, \ + G_DBUS_PROXY_FLAGS_NONE, \ + "io.gpiod1", _obj_path, \ + NULL, &_err); \ + __gpiodbus_test_check_nonnull_and_error(_req, _err); \ + g_object_ref(_req); \ + }) + +#define gpiodbus_test_get_chip_object_manager_or_fail() \ + ({ \ + g_autoptr(GDBusObjectManager) _manager = NULL; \ + g_autoptr(GDBusConnection) _con = NULL; \ + g_autoptr(GError) _err = NULL; \ + _con = gpiodbus_test_get_dbus_connection(); \ + _manager = gpiodbus_object_manager_client_new_sync( \ + _con, \ + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, \ + "io.gpiod1", "/io/gpiod1/chips", NULL, \ + &_err); \ + __gpiodbus_test_check_nonnull_and_error(_manager, _err); \ + g_object_ref(_manager); \ + }) + +#define gpiodbus_test_chip_call_request_lines_sync_or_fail(_chip, \ + _line_config, \ + _request_config, \ + _request_path) \ + do { \ + g_autoptr(GError) _err = NULL; \ + gboolean _ret; \ + _ret = gpiodbus_chip_call_request_lines_sync(_chip, \ + _line_config, \ + _request_config, \ + _request_path, \ + NULL, &_err); \ + __gpiodbus_test_check_gboolean_and_error(_ret, _err); \ + } while (0) + +#define gpiodbus_test_request_call_release_sync_or_fail(_request) \ + do { \ + g_autoptr(GError) _err = NULL; \ + gboolean _ret; \ + _ret = gpiodbus_request_call_release_sync(_request, NULL, \ + &_err); \ + __gpiodbus_test_check_gboolean_and_error(_ret, _err); \ + } while (0) + +GDBusConnection *gpiodbus_test_get_dbus_connection(void); +void gpiodbus_test_wait_for_sim_intf(GPIOSimChip *sim); +GVariant *gpiodbus_test_make_empty_request_config(void); + +#endif /* __GPIODBUS_TEST_INTERNAL_H__ */ + diff --git a/dbus/tests/tests-chip.c b/dbus/tests/tests-chip.c new file mode 100644 index 0000000..c01b2f5 --- /dev/null +++ b/dbus/tests/tests-chip.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include +#include +#include +#include +#include + +#include "daemon-process.h" +#include "helpers.h" + +#define GPIOD_TEST_GROUP "gpiodbus/chip" + +GPIOD_TEST_CASE(read_chip_info) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, + "label", "foobar", + NULL); + g_autoptr(GPIODBusDaemonProcess) mgr = NULL; + g_autoptr(GPIODBusChip) chip = NULL; + g_autofree gchar *obj_path = NULL; + + mgr = gpiodbus_daemon_process_new(); + gpiodbus_test_wait_for_sim_intf(sim); + + obj_path = g_strdup_printf("/io/gpiod1/chips/%s", + g_gpiosim_chip_get_name(sim)); + chip = gpiodbus_test_get_chip_proxy_or_fail(obj_path); + + g_assert_cmpstr(gpiodbus_chip_get_name(chip), ==, + g_gpiosim_chip_get_name(sim)); + g_assert_cmpstr(gpiodbus_chip_get_label(chip), ==, "foobar"); + g_assert_cmpuint(gpiodbus_chip_get_num_lines(chip), ==, 8); + g_assert_cmpstr(gpiodbus_chip_get_path(chip), ==, + g_gpiosim_chip_get_dev_path(sim)); +} + +static gboolean on_timeout(gpointer user_data) +{ + gboolean *timed_out = user_data; + + *timed_out = TRUE; + + return G_SOURCE_REMOVE; +} + +static void on_object_event(GDBusObjectManager *manager G_GNUC_UNUSED, + GPIODBusObject *object, gpointer user_data) +{ + gchar **obj_path = user_data; + + *obj_path = g_strdup(g_dbus_object_get_object_path( + G_DBUS_OBJECT(object))); +} + +GPIOD_TEST_CASE(chip_added) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + g_autoptr(GPIODBusDaemonProcess) mgr = NULL; + g_autofree gchar *sim_obj_path = NULL; + g_autoptr(GPIOSimChip) sim = NULL; + g_autofree gchar *obj_path = NULL; + gboolean timed_out = FALSE; + guint timeout_id; + + mgr = gpiodbus_daemon_process_new(); + + manager = gpiodbus_test_get_chip_object_manager_or_fail(); + + g_signal_connect(manager, "object-added", G_CALLBACK(on_object_event), + &obj_path); + timeout_id = g_timeout_add_seconds(5, on_timeout, &timed_out); + + sim = g_gpiosim_chip_new(NULL); + + while (!obj_path && !timed_out) + g_main_context_iteration(NULL, TRUE); + + if (timed_out) { + g_test_fail_printf("timeout reached waiting for chip to be added"); + return; + } + + sim_obj_path = g_strdup_printf("/io/gpiod1/chips/%s", + g_gpiosim_chip_get_name(sim)); + + g_assert_cmpstr(sim_obj_path, ==, obj_path); + + g_source_remove(timeout_id); +} + +GPIOD_TEST_CASE(chip_removed) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new(NULL); + g_autoptr(GDBusObjectManager) manager = NULL; + g_autoptr(GPIODBusDaemonProcess) mgr = NULL; + g_autofree gchar *sim_obj_path = NULL; + g_autoptr(GPIODBusChip) chip = NULL; + g_autofree gchar *obj_path = NULL; + gboolean timed_out = FALSE; + guint timeout_id; + + sim_obj_path = g_strdup_printf("/io/gpiod1/chips/%s", + g_gpiosim_chip_get_name(sim)); + + mgr = gpiodbus_daemon_process_new(); + gpiodbus_test_wait_for_sim_intf(sim); + + obj_path = g_strdup_printf("/io/gpiod1/chips/%s", + g_gpiosim_chip_get_name(sim)); + chip = gpiodbus_test_get_chip_proxy_or_fail(obj_path); + manager = gpiodbus_test_get_chip_object_manager_or_fail(); + + g_signal_connect(manager, "object-removed", G_CALLBACK(on_object_event), + &obj_path); + timeout_id = g_timeout_add_seconds(5, on_timeout, &timed_out); + + g_clear_object(&sim); + + while (!obj_path && !timed_out) + g_main_context_iteration(NULL, TRUE); + + if (timed_out) { + g_test_fail_printf("timeout reached waiting for chip to be removed"); + return; + } + + g_assert_cmpstr(sim_obj_path, ==, obj_path); + + g_source_remove(timeout_id); +} diff --git a/dbus/tests/tests-line.c b/dbus/tests/tests-line.c new file mode 100644 index 0000000..48dfd1a --- /dev/null +++ b/dbus/tests/tests-line.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include +#include +#include +#include + +#include "daemon-process.h" +#include "helpers.h" + +#define GPIOD_TEST_GROUP "gpiodbus/line" + +GPIOD_TEST_CASE(read_line_properties) +{ + static const GPIOSimLineName names[] = { + { .offset = 1, .name = "foo", }, + { .offset = 2, .name = "bar", }, + { .offset = 4, .name = "baz", }, + { .offset = 5, .name = "xyz", }, + { } + }; + + static const GPIOSimHog hogs[] = { + { + .offset = 3, + .name = "hog3", + .direction = G_GPIOSIM_DIRECTION_OUTPUT_HIGH, + }, + { + .offset = 4, + .name = "hog4", + .direction = G_GPIOSIM_DIRECTION_OUTPUT_LOW, + }, + { } + }; + + g_autoptr(GPIODBusDaemonProcess) mgr = NULL; + g_autoptr(GPIODBusLine) line4 = NULL; + g_autoptr(GPIODBusLine) line6 = NULL; + g_autofree gchar *obj_path_4 = NULL; + g_autofree gchar *obj_path_6 = NULL; + g_autoptr(GPIOSimChip) sim = NULL; + g_autoptr(GVariant) vnames = g_gpiosim_package_line_names(names); + g_autoptr(GVariant) vhogs = g_gpiosim_package_hogs(hogs); + + sim = g_gpiosim_chip_new( + "num-lines", 8, + "line-names", vnames, + "hogs", vhogs, + NULL); + + mgr = gpiodbus_daemon_process_new(); + gpiodbus_test_wait_for_sim_intf(sim); + + obj_path_4 = g_strdup_printf("/io/gpiod1/chips/%s/line4", + g_gpiosim_chip_get_name(sim)); + line4 = gpiodbus_test_get_line_proxy_or_fail(obj_path_4); + + obj_path_6 = g_strdup_printf("/io/gpiod1/chips/%s/line6", + g_gpiosim_chip_get_name(sim)); + line6 = gpiodbus_test_get_line_proxy_or_fail(obj_path_6); + + g_assert_cmpuint(gpiodbus_line_get_offset(line4), ==, 4); + g_assert_cmpstr(gpiodbus_line_get_name(line4), ==, "baz"); + g_assert_cmpstr(gpiodbus_line_get_consumer(line4), ==, "hog4"); + g_assert_true(gpiodbus_line_get_used(line4)); + g_assert_false(gpiodbus_line_get_managed(line4)); + g_assert_cmpstr(gpiodbus_line_get_direction(line4), ==, "output"); + g_assert_cmpstr(gpiodbus_line_get_edge_detection(line4), ==, "none"); + g_assert_false(gpiodbus_line_get_active_low(line4)); + g_assert_cmpstr(gpiodbus_line_get_bias(line4), ==, "unknown"); + g_assert_cmpstr(gpiodbus_line_get_drive(line4), ==, "push-pull"); + g_assert_cmpstr(gpiodbus_line_get_event_clock(line4), ==, "monotonic"); + g_assert_false(gpiodbus_line_get_debounced(line4)); + g_assert_cmpuint(gpiodbus_line_get_debounce_period_us(line4), ==, 0); + + g_assert_cmpuint(gpiodbus_line_get_offset(line6), ==, 6); + g_assert_cmpstr(gpiodbus_line_get_name(line6), ==, ""); + g_assert_cmpstr(gpiodbus_line_get_consumer(line6), ==, ""); + g_assert_false(gpiodbus_line_get_used(line6)); +} + +static gboolean on_timeout(gpointer user_data) +{ + gboolean *timed_out = user_data; + + *timed_out = TRUE; + + return G_SOURCE_REMOVE; +} + +static void +on_properties_changed(GPIODBusLine *line G_GNUC_UNUSED, + GVariant *changed_properties, + GStrv invalidated_properties G_GNUC_UNUSED, + gpointer user_data) +{ + GHashTable *changed_props = user_data; + GVariantIter iter; + GVariant *variant; + gchar *str; + + g_variant_iter_init(&iter, changed_properties); + while (g_variant_iter_next(&iter, "{sv}", &str, &variant)) { + g_hash_table_insert(changed_props, str, NULL); + g_variant_unref(variant); + } +} + +static void check_props_requested(GHashTable *props) +{ + if (!g_hash_table_contains(props, "Direction") || + !g_hash_table_contains(props, "Consumer") || + !g_hash_table_contains(props, "Used") || + !g_hash_table_contains(props, "RequestPath") || + !g_hash_table_contains(props, "Managed")) + g_test_fail_printf("Not all expected properties have changed"); +} + +static void check_props_released(GHashTable *props) +{ + if (!g_hash_table_contains(props, "RequestPath") || + !g_hash_table_contains(props, "Consumer") || + !g_hash_table_contains(props, "Used") || + !g_hash_table_contains(props, "Managed")) + g_test_fail_printf("Not all expected properties have changed"); +} + +static GVariant *make_props_changed_line_config(void) +{ + g_autoptr(GVariant) output_values = NULL; + g_autoptr(GVariant) line_settings = NULL; + g_autoptr(GVariant) line_offsets = NULL; + g_autoptr(GVariant) line_configs = NULL; + g_autoptr(GVariant) line_config = NULL; + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, g_variant_new_uint32(4)); + line_offsets = g_variant_builder_end(&builder); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, + g_variant_new("{sv}", "direction", + g_variant_new_string("output"))); + line_settings = g_variant_builder_end(&builder); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&builder, g_variant_ref(line_offsets)); + g_variant_builder_add_value(&builder, g_variant_ref(line_settings)); + line_config = g_variant_builder_end(&builder); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, g_variant_ref(line_config)); + line_configs = g_variant_builder_end(&builder); + + output_values = g_variant_new("ai", NULL); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&builder, g_variant_ref(line_configs)); + g_variant_builder_add_value(&builder, g_variant_ref(output_values)); + + return g_variant_ref_sink(g_variant_builder_end(&builder)); +} + +GPIOD_TEST_CASE(properties_changed) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODBusDaemonProcess) mgr = NULL; + g_autoptr(GHashTable) changed_props = NULL; + g_autoptr(GPIODBusRequest) request = NULL; + g_autoptr(GVariant) request_config = NULL; + g_autoptr(GVariant) line_config = NULL; + g_autofree gchar *line_obj_path = NULL; + g_autofree gchar *chip_obj_path = NULL; + g_autofree gchar *request_path = NULL; + g_autoptr(GPIODBusChip) chip = NULL; + g_autoptr(GPIODBusLine) line = NULL; + gboolean timed_out = FALSE; + guint timeout_id; + + mgr = gpiodbus_daemon_process_new(); + gpiodbus_test_wait_for_sim_intf(sim); + + line_obj_path = g_strdup_printf("/io/gpiod1/chips/%s/line4", + g_gpiosim_chip_get_name(sim)); + line = gpiodbus_test_get_line_proxy_or_fail(line_obj_path); + + chip_obj_path = g_strdup_printf("/io/gpiod1/chips/%s", + g_gpiosim_chip_get_name(sim)); + chip = gpiodbus_test_get_chip_proxy_or_fail(chip_obj_path); + + changed_props = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + NULL); + + g_signal_connect(line, "g-properties-changed", + G_CALLBACK(on_properties_changed), changed_props); + timeout_id = g_timeout_add_seconds(5, on_timeout, &timed_out); + + line_config = make_props_changed_line_config(); + request_config = gpiodbus_test_make_empty_request_config(); + + gpiodbus_test_chip_call_request_lines_sync_or_fail(chip, line_config, + request_config, + &request_path); + + while (g_hash_table_size(changed_props) < 5 && !timed_out) + g_main_context_iteration(NULL, TRUE); + + check_props_requested(changed_props); + + g_hash_table_destroy(g_hash_table_ref(changed_props)); + + request = gpiodbus_test_get_request_proxy_or_fail(request_path); + gpiodbus_test_request_call_release_sync_or_fail(request); + + while (g_hash_table_size(changed_props) < 4 && !timed_out) + g_main_context_iteration(NULL, TRUE); + + check_props_released(changed_props); + + if (timed_out) { + g_test_fail_printf("timeout reached waiting for line properties to change"); + return; + } + + g_source_remove(timeout_id); +} diff --git a/dbus/tests/tests-request.c b/dbus/tests/tests-request.c new file mode 100644 index 0000000..1af31fe --- /dev/null +++ b/dbus/tests/tests-request.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include +#include +#include +#include +#include + +#include "daemon-process.h" +#include "helpers.h" + +#define GPIOD_TEST_GROUP "gpiodbus/request" + +static GVariant *make_empty_request_config(void) +{ + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + + return g_variant_ref_sink(g_variant_builder_end(&builder)); +} + +static GVariant *make_input_lines_line_config(void) +{ + g_autoptr(GVariant) output_values = NULL; + g_autoptr(GVariant) line_settings = NULL; + g_autoptr(GVariant) line_offsets = NULL; + g_autoptr(GVariant) line_configs = NULL; + g_autoptr(GVariant) line_config = NULL; + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, g_variant_new_uint32(3)); + g_variant_builder_add_value(&builder, g_variant_new_uint32(5)); + g_variant_builder_add_value(&builder, g_variant_new_uint32(7)); + line_offsets = g_variant_builder_end(&builder); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, + g_variant_new("{sv}", "direction", + g_variant_new_string("input"))); + line_settings = g_variant_builder_end(&builder); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&builder, g_variant_ref(line_offsets)); + g_variant_builder_add_value(&builder, g_variant_ref(line_settings)); + line_config = g_variant_builder_end(&builder); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, g_variant_ref(line_config)); + line_configs = g_variant_builder_end(&builder); + + output_values = g_variant_new("ai", NULL); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&builder, g_variant_ref(line_configs)); + g_variant_builder_add_value(&builder, g_variant_ref(output_values)); + + return g_variant_ref_sink(g_variant_builder_end(&builder)); +} + +GPIOD_TEST_CASE(request_input_lines) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODBusDaemonProcess) mgr = NULL; + g_autoptr(GVariant) request_config = NULL; + g_autoptr(GVariant) line_config = NULL; + g_autofree gchar *request_path = NULL; + g_autoptr(GPIODBusChip) chip = NULL; + g_autofree gchar *obj_path = NULL; + + mgr = gpiodbus_daemon_process_new(); + gpiodbus_test_wait_for_sim_intf(sim); + + obj_path = g_strdup_printf("/io/gpiod1/chips/%s", + g_gpiosim_chip_get_name(sim)); + chip = gpiodbus_test_get_chip_proxy_or_fail(obj_path); + + line_config = make_input_lines_line_config(); + request_config = make_empty_request_config(); + + gpiodbus_test_chip_call_request_lines_sync_or_fail(chip, line_config, + request_config, + &request_path); +} + +GPIOD_TEST_CASE(release_request) +{ + g_autoptr(GPIOSimChip) sim = g_gpiosim_chip_new("num-lines", 8, NULL); + g_autoptr(GPIODBusDaemonProcess) mgr = NULL; + g_autoptr(GVariant) request_config = NULL; + g_autoptr(GPIODBusRequest) request = NULL; + g_autoptr(GVariant) line_config = NULL; + g_autofree gchar *request_path = NULL; + g_autoptr(GPIODBusChip) chip = NULL; + g_autofree gchar *obj_path = NULL; + + mgr = gpiodbus_daemon_process_new(); + gpiodbus_test_wait_for_sim_intf(sim); + + obj_path = g_strdup_printf("/io/gpiod1/chips/%s", + g_gpiosim_chip_get_name(sim)); + chip = gpiodbus_test_get_chip_proxy_or_fail(obj_path); + + line_config = make_input_lines_line_config(); + request_config = make_empty_request_config(); + + gpiodbus_test_chip_call_request_lines_sync_or_fail(chip, line_config, + request_config, + &request_path); + + request = gpiodbus_test_get_request_proxy_or_fail(request_path); + gpiodbus_test_request_call_release_sync_or_fail(request); +} From patchwork Fri Jun 28 18:58:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bartosz Golaszewski X-Patchwork-Id: 808526 Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2123C57888 for ; Fri, 28 Jun 2024 18:59:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601162; cv=none; b=cr4k/8NQUoHqijBOKHZ5ekk6x4MnIWc1Cd/tZeCjFnCoRTofgEzM5gTIOgnwpkJMrthJ9z2zrGqOFDu2iI1b5uqD6YVXMBWZ7h7xD1WkPexkAxUriGCXncs2xDgkW1/+hCWfUvwlv9qs2Sl09sgnRAN0HE/gKxb3FlxptVuTrwY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719601162; c=relaxed/simple; bh=+6ovHJ4maUmLx2sM4Ye3ZcUwqXH4d2TbldUMAp5f+Sk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=a6a7PcwqkeZX+cZ5nwiLNsZuvnLbhzYxfuMT+EqS4SOS//SaFg9ay+5mo86AWOx4orclp/+3KhI7Z6sqnBwwlL07+4X7ZhzcaXJwlbJFXz135obJ068CsH4yePUPCLVTqJeVtDHlEp1il5M8cZss+rbUMWAMYA6lYIYkOTEdK+g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl; spf=none smtp.mailfrom=bgdev.pl; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b=iHYSgmQ8; arc=none smtp.client-ip=209.85.128.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=bgdev.pl Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bgdev-pl.20230601.gappssmtp.com header.i=@bgdev-pl.20230601.gappssmtp.com header.b="iHYSgmQ8" Received: by mail-wm1-f48.google.com with SMTP id 5b1f17b1804b1-42566fb8302so7068965e9.0 for ; Fri, 28 Jun 2024 11:59:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bgdev-pl.20230601.gappssmtp.com; s=20230601; t=1719601156; x=1720205956; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=kkYJqliYrOHJL1wMXPHc7Tfn6Ytm7HjwAjyU0bvNi+A=; b=iHYSgmQ8Gej6OGu4GxxLkeMZkgBuyKoys6Bo/Y1MXykPgGDGiB4CHc9aRt0c7a9B8h b7yYAiqTfrJ++uVQI1Jjc+vatUdgkOAWvyJWlAcoCHZeaEmH33gCOOSXF6s6mMVevcSw z5Mgtapo9Ub7YxyxT2HUQN2b3OH6PEy7mFKcoUTtVp7ATVGz+64d+vk4i6d+HlGDEPvu q8ptII3G6nKvMgGDyQBkNxIem4DCFWX8jVs69oAEcPQW+QXZWUxLwkVCP2w//eVbPx2y ooTjr+WV2bYf71XwaWzuR944k1VxtYuj87MgQtHqLv2/SG/q19tcTV+fjOVcIUxqCwON LDpg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719601156; x=1720205956; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kkYJqliYrOHJL1wMXPHc7Tfn6Ytm7HjwAjyU0bvNi+A=; b=io7daTApJg+vCJLJFMOZmLIrkc1zYXfMujmf+Lcrtmhzowp72GDP99SPZhPKMtro8t NDuPyLGh3iW4dw93omF9ZXjeqponSY451yhoK5yZml7PlOdy8ZWMBQy6im25lq4vWZMW FaVhPaT+oI5shymg+CT5BL2vudaxsg2wvJlrWuobBAP23RYdPPPUmGeEU6Nckh1DhNGk iVDTrOcG6eY2trNpc03Z0Y7CuhxwrqYupKRXRQDdxqMekIVAwreWKbCErtQiS9DT2gh8 rIKlKwgFYzSyq3EZzyHDOmxn8gUDg7IoaPnWAguGmS9ZFzc+jtEMfXn4SEl/UbQBM9Un 6lqQ== X-Forwarded-Encrypted: i=1; AJvYcCUg7X6iQ96ZI7D0z1ouIZ4SywMOHJzJLQ1vsk+O05PyTUcAd1bjj4vz/ETdEDQAum2AC8Y9z2DGKIDdWQPn1dD1QBa3zuwVq8JvoQ== X-Gm-Message-State: AOJu0YxjoEi8d27/Q6d4MdXpOHWOOC+bXbMbtu1UGRL2UYdXsCZdJQGm 84YRPNP9dS7Lzi99lU7cRK52vXQVD+2eWEDsy90IR2GGAAZa9YLze8gEZBM0KGc= X-Google-Smtp-Source: AGHT+IGMVoLExVzfLKAg0hTF+nz1UcqCT8fYr1c9+Qr3xQidI83QZbzMiLh7uo8583tYguUdHzBG5A== X-Received: by 2002:a05:600c:5c4:b0:425:622f:581f with SMTP id 5b1f17b1804b1-425622f58acmr49849655e9.38.1719601156198; Fri, 28 Jun 2024 11:59:16 -0700 (PDT) Received: from [127.0.1.1] ([2a01:cb1d:dc:7e00:cb0e:590a:642a:e1f9]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3675a103d00sm3097336f8f.99.2024.06.28.11.59.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Jun 2024 11:59:15 -0700 (PDT) From: Bartosz Golaszewski Date: Fri, 28 Jun 2024 20:58:34 +0200 Subject: [PATCH RESEND libgpiod v2 15/18] dbus: add a command-line client Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240628-dbus-v2-15-c1331ac17cb8@linaro.org> References: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> In-Reply-To: <20240628-dbus-v2-0-c1331ac17cb8@linaro.org> To: Linus Walleij , Kent Gibson , Erik Schilling , Phil Howard , Andy Shevchenko , Viresh Kumar , Dan Carpenter Cc: "As advised by Dan Carpenter - I'm CC'ing dbus"@lists.freedesktop.orgto, linux-gpio@vger.kernel.org, dbus@lists.freedesktop.org, Bartosz Golaszewski X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=85823; i=bartosz.golaszewski@linaro.org; h=from:subject:message-id; bh=HVTJVUCWtlPP7Vr7MBBUCn03NBSLebeJnmEHQu4mzyk=; b=owEBbQKS/ZANAwAKARGnLqAUcddyAcsmYgBmfwfu/oGtM+Lg34Jpzj9jR+bgvJ//KxwCBCdfT JKHrzVB12aJAjMEAAEKAB0WIQQWnetsC8PEYBPSx58Rpy6gFHHXcgUCZn8H7gAKCRARpy6gFHHX cqVTD/wIvp9vbt86Z8uZPtgbF5hIsEhEC48b+lpshA+9X1hk9A64mhIM2+o/pCVijwgcAC4/7bG Bn8AASDZ8TsRVi2Y65guivAZPtEiB7ZM+7FP4mojilnpNdcV0CTB7J1yK29Jz+Bobqh6USslMzV VcW/UUc0SLmkniakD2WJF4O3B3lOeywT5sgFyZZy8x8iJJ6Td+PtY90mrQiE/iw5qYN3jFechOC 4kjjM9TsmH4dZnLqkmrwXUdsNhTcu78YnXJhP1ZiXPkxzusEktJ/+ZOBJQmUN/XpUaK1jYsNoNy rVunWDJyCdMM/n8ur0rL4SAbRcDac1KFkHVklY6OSbemopbvAilt+t4iVt7wIoymOx2I7K5rjsI j6XInPDZevVZG6I6ITl2hSi0C6YYeyiZgtCKxZALeeYiTxhdNzPY8wsbWDdNIKPBpuLXtcPa8+Q OqLdwyctKv2dYNvEeAAFjfY7OyiWiiUEVVS5cZbqoscAHoXTzID6sdoqIUfA2rF0XtLyBlu4zgj dUqGlcbcADIDwgt+ZQpWg3eLyWBKIbP0GRLq7RGZ/LOi3GIbzb/24lnWUzUXw+v9BKYy+hehbLF /1SWUDLuSz+Krl5rhKs3IEvIKoy5s6BleHagOcSN1R/6t3/Mn/8/PFTMRRV0N1Y9TjqZQgqrpMW 0CMP07qjjGNguTg== X-Developer-Key: i=bartosz.golaszewski@linaro.org; a=openpgp; fpr=169DEB6C0BC3C46013D2C79F11A72EA01471D772 From: Bartosz Golaszewski Add code for gpiocli - the command-line client for controlling GPIOs using the gpio-manager daemon. Signed-off-by: Bartosz Golaszewski --- dbus/client/common.c | 646 ++++++++++++++++++++++++++++++++++++++++++++++ dbus/client/common.h | 203 +++++++++++++++ dbus/client/detect.c | 53 ++++ dbus/client/find.c | 66 +++++ dbus/client/get.c | 210 +++++++++++++++ dbus/client/gpiocli.c | 174 +++++++++++++ dbus/client/info.c | 184 +++++++++++++ dbus/client/monitor.c | 191 ++++++++++++++ dbus/client/notify.c | 295 +++++++++++++++++++++ dbus/client/reconfigure.c | 74 ++++++ dbus/client/release.c | 62 +++++ dbus/client/request.c | 249 ++++++++++++++++++ dbus/client/requests.c | 71 +++++ dbus/client/set.c | 170 ++++++++++++ dbus/client/wait.c | 188 ++++++++++++++ 15 files changed, 2836 insertions(+) diff --git a/dbus/client/common.c b/dbus/client/common.c new file mode 100644 index 0000000..c3aae75 --- /dev/null +++ b/dbus/client/common.c @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include +#include +#include +#include +#include + +#include "common.h" + +static void print_err_msg(GError *err, const gchar *fmt, va_list va) +{ + g_printerr("%s: ", g_get_prgname()); + g_vfprintf(stderr, fmt, va); + if (err) + g_printerr(": %s", err->message); + g_printerr("\n"); +} + +void die(const gchar *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + print_err_msg(NULL, fmt, va); + va_end(va); + + exit(EXIT_FAILURE); +} + +void die_gerror(GError *err, const gchar *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + print_err_msg(err, fmt, va); + va_end(va); + + exit(EXIT_FAILURE); +} + +void die_parsing_opts(const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + print_err_msg(NULL, fmt, va); + va_end(va); + g_printerr("\nSee %s --help\n", g_get_prgname()); + + exit(EXIT_FAILURE); +} + +void parse_options(const GOptionEntry *opts, const gchar *summary, + const gchar *description, int *argc, char ***argv) +{ + g_autoptr(GOptionContext) ctx = NULL; + g_autoptr(GError) err = NULL; + gboolean ret; + + ctx = g_option_context_new(NULL); + g_option_context_set_summary(ctx, summary); + g_option_context_set_description(ctx, description); + g_option_context_add_main_entries(ctx, opts, NULL); + g_option_context_set_strict_posix(ctx, TRUE); + + ret = g_option_context_parse(ctx, argc, argv, &err); + if (!ret) { + g_printerr("%s: Option parsing failed: %s\nSee %s --help\n", + g_get_prgname(), err->message, g_get_prgname()); + exit(EXIT_FAILURE); + } +} + +void check_manager(void) +{ + g_autoptr(GDBusProxy) proxy = NULL; + g_autoptr(GVariant) result = NULL; + g_autoptr(GError) err = NULL; + + proxy = g_dbus_proxy_new_for_bus_sync( + G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, + "io.gpiod1", "/io/gpiod1", "org.freedesktop.DBus.Peer", + NULL, &err); + if (!proxy) + die_gerror(err, "Unable to create a proxy to '/io/gpiod1'"); + + result = g_dbus_proxy_call_sync(proxy, "Ping", NULL, + G_DBUS_CALL_FLAGS_NONE, -1, NULL, + &err); + if (!result) { + if (err->domain == G_DBUS_ERROR) { + switch (err->code) { + case G_DBUS_ERROR_ACCESS_DENIED: + die("Access to gpio-manager denied, check your permissions"); + case G_DBUS_ERROR_SERVICE_UNKNOWN: + die("gpio-manager not running"); + } + } + + die_gerror(err, "Failed trying to contect the gpio manager"); + } +} + +gboolean quit_main_loop_on_signal(gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit(loop); + + return G_SOURCE_REMOVE; +} + +void die_on_name_vanished(GDBusConnection *con G_GNUC_UNUSED, + const gchar *name G_GNUC_UNUSED, + gpointer user_data G_GNUC_UNUSED) +{ + die("gpio-manager exited unexpectedly"); +} + +GList *strv_to_gstring_list(GStrv lines) +{ + gsize llen = g_strv_length(lines); + GList *list = NULL; + guint i; + + for (i = 0; i < llen; i++) + list = g_list_append(list, g_string_new(lines[i])); + + return list; +} + +gint output_value_from_str(const gchar *value_str) +{ + if ((g_strcmp0(value_str, "active") == 0) || + (g_strcmp0(value_str, "1") == 0)) + return 1; + else if ((g_strcmp0(value_str, "inactive") == 0) || + (g_strcmp0(value_str, "0") == 0)) + return 0; + + die_parsing_opts("invalid output value: '%s'", value_str); +} + +static gboolean str_is_all_digits(const gchar *str) +{ + for (; *str; str++) { + if (!g_ascii_isdigit(*str)) + return FALSE; + } + + return TRUE; +} + +static gint compare_objs_by_path(GDBusObject *a, GDBusObject *b) +{ + return strverscmp(g_dbus_object_get_object_path(a), + g_dbus_object_get_object_path(b)); +} + +GDBusObjectManager *get_object_manager_client(const gchar *obj_path) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + g_autoptr(GError) err = NULL; + + manager = gpiodbus_object_manager_client_new_for_bus_sync( + G_BUS_TYPE_SYSTEM, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + "io.gpiod1", obj_path, NULL, &err); + if (!manager) + die_gerror(err, + "failed to create the object manager client for %s", + obj_path); + + return g_object_ref(manager); +} + +static gchar *make_chip_obj_path(const gchar *chip) +{ + return g_strdup_printf( + str_is_all_digits(chip) ? + "/io/gpiod1/chips/gpiochip%s" : + "/io/gpiod1/chips/%s", + chip); +} + +GPIODBusObject *get_chip_obj_by_path(const gchar *obj_path) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + g_autoptr(GPIODBusObject) chip_obj = NULL; + + manager = get_object_manager_client("/io/gpiod1/chips"); + + chip_obj = GPIODBUS_OBJECT(g_dbus_object_manager_get_object(manager, + obj_path)); + if (!chip_obj) + die("No such chip object: '%s'", obj_path); + + return g_object_ref(chip_obj); +} + +GPIODBusObject *get_chip_obj(const gchar *chip_name) +{ + g_autofree gchar *chip_path = make_chip_obj_path(chip_name); + + return get_chip_obj_by_path(chip_path); +} + +GList *get_chip_objs(GStrv chip_names) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + GList *objs = NULL; + gint i; + + manager = get_object_manager_client("/io/gpiod1/chips"); + + if (!chip_names) + return g_list_sort(g_dbus_object_manager_get_objects(manager), + (GCompareFunc)compare_objs_by_path); + + for (i = 0; chip_names[i]; i++) { + g_autofree gchar *obj_path = make_chip_obj_path(chip_names[i]); + g_autoptr(GPIODBusObject) obj = NULL; + + obj = GPIODBUS_OBJECT( + g_dbus_object_manager_get_object(manager, obj_path)); + if (!obj) + die("No such chip: '%s'", chip_names[i]); + + objs = g_list_insert_sorted(objs, g_object_ref(obj), + (GCompareFunc)compare_objs_by_path); + } + + return objs; +} + +gchar *make_request_obj_path(const gchar *request) +{ + return g_strdup_printf( + str_is_all_digits(request) ? + "/io/gpiod1/requests/request%s" : + "/io/gpiod1/requests/%s", + request); +} + +GPIODBusObject *get_request_obj(const gchar *request_name) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + g_autoptr(GPIODBusObject) req_obj = NULL; + g_autofree gchar *obj_path = NULL; + + manager = get_object_manager_client("/io/gpiod1/requests"); + obj_path = make_request_obj_path(request_name); + + req_obj = GPIODBUS_OBJECT(g_dbus_object_manager_get_object(manager, + obj_path)); + if (!req_obj) + die("No such request: '%s'", request_name); + + return g_object_ref(req_obj); +} + +GList *get_request_objs(void) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + GList *objs = NULL; + + manager = get_object_manager_client("/io/gpiod1/requests"); + objs = g_dbus_object_manager_get_objects(manager); + + return g_list_sort(objs, (GCompareFunc)compare_objs_by_path); +} + +GArray *get_request_offsets(GPIODBusRequest *request) +{ + const gchar *chip_path, *line_path, *const *line_paths; + g_autoptr(GDBusObjectManager) manager = NULL; + g_autoptr(GArray) offsets = NULL; + GPIODBusLine *line; + guint i, offset; + + chip_path = gpiodbus_request_get_chip_path(request); + line_paths = gpiodbus_request_get_line_paths(request); + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + manager = get_object_manager_client(chip_path); + + for (i = 0, line_path = line_paths[i]; + line_path; + line_path = line_paths[++i]) { + g_autoptr(GDBusObject) line_obj = NULL; + + line_obj = g_dbus_object_manager_get_object(manager, line_path); + line = gpiodbus_object_peek_line(GPIODBUS_OBJECT(line_obj)); + offset = gpiodbus_line_get_offset(line); + g_array_append_val(offsets, offset); + } + + return g_array_ref(offsets); +} + +gboolean get_line_obj_by_name(const gchar *name, GPIODBusObject **line_obj, + GPIODBusObject **chip_obj) +{ + g_autolist(GPIODBusObject) chip_objs = NULL; + GList *pos; + + if (str_is_all_digits(name)) + die("Refusing to use line offsets if chip is not specified"); + + chip_objs = get_chip_objs(NULL); + + for (pos = g_list_first(chip_objs); pos; pos = g_list_next(pos)) { + *line_obj = get_line_obj_by_name_for_chip(pos->data, name); + if (*line_obj) { + if (chip_obj) + *chip_obj = g_object_ref(pos->data); + return TRUE; + } + } + + return FALSE; +} + +GPIODBusObject * +get_line_obj_by_name_for_chip(GPIODBusObject *chip_obj, const gchar *line_name) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + g_autolist(GPIODBusObject) line_objs = NULL; + const gchar *chip_path; + GPIODBusLine *line; + guint64 offset; + GList *pos; + + chip_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(chip_obj)); + manager = get_object_manager_client(chip_path); + line_objs = g_dbus_object_manager_get_objects(manager); + + for (pos = g_list_first(line_objs); pos; pos = g_list_next(pos)) { + line = gpiodbus_object_peek_line(pos->data); + + if (g_strcmp0(gpiodbus_line_get_name(line), line_name) == 0) + return g_object_ref(pos->data); + + if (str_is_all_digits(line_name)) { + offset = g_ascii_strtoull(line_name, NULL, 10); + if (offset == gpiodbus_line_get_offset(line)) + return g_object_ref(pos->data); + } + } + + return NULL; +} + +GList *get_all_line_objs_for_chip(GPIODBusObject *chip_obj) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + const gchar *chip_path; + + chip_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(chip_obj)); + manager = get_object_manager_client(chip_path); + + return g_list_sort(g_dbus_object_manager_get_objects(manager), + (GCompareFunc)compare_objs_by_path); +} + +static gchar *sanitize_str(const gchar *str) +{ + if (!strlen(str)) + return NULL; + + return g_strdup(str); +} + +static const gchar *sanitize_direction(const gchar *direction) +{ + if ((g_strcmp0(direction, "input") == 0) || + (g_strcmp0(direction, "output") == 0)) + return direction; + + die("invalid direction value received from manager: '%s'", direction); +} + +static const gchar *sanitize_drive(const gchar *drive) +{ + if ((g_strcmp0(drive, "push-pull") == 0) || + (g_strcmp0(drive, "open-source") == 0) || + (g_strcmp0(drive, "open-drain") == 0)) + return drive; + + die("invalid drive value received from manager: '%s'", drive); +} + +static const gchar *sanitize_bias(const gchar *bias) +{ + if ((g_strcmp0(bias, "pull-up") == 0) || + (g_strcmp0(bias, "pull-down") == 0) || + (g_strcmp0(bias, "disabled") == 0)) + return bias; + + if (g_strcmp0(bias, "unknown") == 0) + return NULL; + + die("invalid bias value received from manager: '%s'", bias); +} + +static const gchar *sanitize_edge(const gchar *edge) +{ + if ((g_strcmp0(edge, "rising") == 0) || + (g_strcmp0(edge, "falling") == 0) || + (g_strcmp0(edge, "both") == 0)) + return edge; + + if (g_strcmp0(edge, "none") == 0) + return NULL; + + die("invalid edge value received from manager: '%s'", edge); +} + +static const gchar *sanitize_clock(const gchar *event_clock) +{ + if ((g_strcmp0(event_clock, "monotonic") == 0) || + (g_strcmp0(event_clock, "realtime") == 0) || + (g_strcmp0(event_clock, "hte") == 0)) + return event_clock; + + die("invalid clock value received from manager: '%s'", event_clock); +} + +gchar *sanitize_object_path(const gchar *path) +{ + if (g_strcmp0(path, "/") == 0) + return g_strdup("N/A"); + + return g_path_get_basename(path); +} + +LineProperties *get_line_properties(GPIODBusLine *line) +{ + LineProperties *props; + + props = g_malloc0(sizeof(*props)); + props->name = sanitize_str(gpiodbus_line_get_name(line)); + props->offset = gpiodbus_line_get_offset(line); + props->used = gpiodbus_line_get_used(line); + props->consumer = sanitize_str(gpiodbus_line_get_consumer(line)); + props->direction = sanitize_direction( + gpiodbus_line_get_direction(line)); + props->drive = sanitize_drive(gpiodbus_line_get_drive(line)); + props->bias = sanitize_bias(gpiodbus_line_get_bias(line)); + props->active_low = gpiodbus_line_get_active_low(line); + props->edge = sanitize_edge(gpiodbus_line_get_edge_detection(line)); + props->debounced = gpiodbus_line_get_debounced(line); + props->debounce_period = gpiodbus_line_get_debounce_period_us(line); + props->event_clock = sanitize_clock( + gpiodbus_line_get_event_clock(line)); + props->managed = gpiodbus_line_get_managed(line); + props->request_name = sanitize_object_path( + gpiodbus_line_get_request_path(line)); + + return props; +} + +void free_line_properties(LineProperties *props) +{ + g_free(props->name); + g_free(props->consumer); + g_free(props->request_name); + g_free(props); +} + +void validate_line_config_opts(LineConfigOpts *opts) +{ + gint counter; + + if (opts->input && opts->output) + die_parsing_opts("--input and --output are mutually exclusive"); + + if (opts->both_edges) + opts->rising_edge = opts->falling_edge = TRUE; + + if (!opts->input && (opts->rising_edge || opts->falling_edge)) + die_parsing_opts("monitoring edges is only possible in input mode"); + + counter = 0; + if (opts->push_pull) + counter++; + if (opts->open_drain) + counter++; + if (opts->open_source) + counter++; + + if (counter > 1) + die_parsing_opts("--push-pull, --open-drain and --open-source are mutually exclusive"); + + if (!opts->output && (counter > 0)) + die_parsing_opts("--push-pull, --open-drain and --open-source are only available in output mode"); + + counter = 0; + if (opts->pull_up) + counter++; + if (opts->pull_down) + counter++; + if (opts->bias_disabled) + counter++; + + if (counter > 1) + die_parsing_opts("--pull-up, --pull-down and --bias-disabled are mutually exclusive"); + + counter = 0; + if (opts->clock_monotonic) + counter++; + if (opts->clock_realtime) + counter++; + if (opts->clock_hte) + counter++; + + if (counter > 1) + die_parsing_opts("--clock-monotonic, --clock-realtime and --clock-hte are mutually exclusive"); + + if (counter > 0 && (!opts->rising_edge && !opts->falling_edge)) + die_parsing_opts("--clock-monotonic, --clock-realtime and --clock-hte can only be used with edge detection enabled"); + + if (opts->debounce_period && (!opts->rising_edge && !opts->falling_edge)) + die_parsing_opts("--debounce-period can only be used with edge-detection enabled"); +} + +GVariant *make_line_config(GArray *offsets, LineConfigOpts *opts) +{ + const char *direction, *edge = NULL, *bias = NULL, *drive = NULL, + *clock = NULL; + g_autoptr(GVariant) output_values = NULL; + g_autoptr(GVariant) line_settings = NULL; + g_autoptr(GVariant) line_offsets = NULL; + g_autoptr(GVariant) line_configs = NULL; + g_autoptr(GVariant) line_config = NULL; + GVariantBuilder builder; + guint i; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + for (i = 0; i < offsets->len; i++) + g_variant_builder_add_value(&builder, + g_variant_new_uint32(g_array_index(offsets, guint, i))); + line_offsets = g_variant_builder_end(&builder); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + + if (opts->input) + direction = "input"; + else if (opts->output) + direction = "output"; + else + direction = "as-is"; + + if (direction) + g_variant_builder_add_value(&builder, + g_variant_new("{sv}", "direction", + g_variant_new_string(direction))); + + if (opts->rising_edge && opts->falling_edge) + edge = "both"; + else if (opts->falling_edge) + edge = "falling"; + else if (opts->rising_edge) + edge = "rising"; + + if (edge) + g_variant_builder_add_value(&builder, + g_variant_new("{sv}", "edge", + g_variant_new_string(edge))); + + if (opts->pull_up) + bias = "pull-up"; + else if (opts->pull_down) + bias = "pull-down"; + else if (opts->bias_disabled) + bias = "disabled"; + + if (bias) + g_variant_builder_add_value(&builder, + g_variant_new("{sv}", "bias", + g_variant_new_string(bias))); + + if (opts->push_pull) + drive = "push-pull"; + else if (opts->open_drain) + drive = "open-drain"; + else if (opts->open_source) + drive = "open-source"; + + if (drive) + g_variant_builder_add_value(&builder, + g_variant_new("{sv}", "drive", + g_variant_new_string(drive))); + + if (opts->active_low) + g_variant_builder_add_value(&builder, + g_variant_new("{sv}", "active-low", + g_variant_new_boolean(TRUE))); + + if (opts->debounce_period) + g_variant_builder_add_value(&builder, + g_variant_new("{sv}", "debounce-period", + g_variant_new_int64(opts->debounce_period))); + + if (opts->clock_monotonic) + clock = "monotonic"; + else if (opts->clock_realtime) + clock = "realtime"; + else if (opts->clock_hte) + clock = "hte"; + + if (clock) + g_variant_builder_add_value(&builder, + g_variant_new("{sv}", "event-clock", + g_variant_new_string(clock))); + + line_settings = g_variant_builder_end(&builder); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&builder, g_variant_ref(line_offsets)); + g_variant_builder_add_value(&builder, g_variant_ref(line_settings)); + line_config = g_variant_builder_end(&builder); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, g_variant_ref(line_config)); + line_configs = g_variant_builder_end(&builder); + + if (opts->output_values) { + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + for (i = 0; i < opts->output_values->len; i++) { + g_variant_builder_add(&builder, "i", + g_array_index(opts->output_values, + gint, i)); + } + output_values = g_variant_builder_end(&builder); + } else { + output_values = g_variant_new("ai", opts->output_values); + } + + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); + g_variant_builder_add_value(&builder, g_variant_ref(line_configs)); + g_variant_builder_add_value(&builder, g_variant_ref(output_values)); + + return g_variant_ref_sink(g_variant_builder_end(&builder)); +} diff --git a/dbus/client/common.h b/dbus/client/common.h new file mode 100644 index 0000000..9e8564b --- /dev/null +++ b/dbus/client/common.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski */ + +#ifndef __GPIOCLI_COMMON_H__ +#define __GPIOCLI_COMMON_H__ + +#include +#include +#include + +void die(const gchar *fmt, ...) G_NORETURN G_GNUC_PRINTF(1, 2); +void +die_gerror(GError *err, const gchar *fmt, ...) G_NORETURN G_GNUC_PRINTF(2, 3); +void die_parsing_opts(const char *fmt, ...) G_NORETURN G_GNUC_PRINTF(1, 2); + +void parse_options(const GOptionEntry *opts, const gchar *summary, + const gchar *description, int *argc, char ***argv); +void check_manager(void); + +gboolean quit_main_loop_on_signal(gpointer user_data); +void die_on_name_vanished(GDBusConnection *con, const gchar *name, + gpointer user_data); + +GList *strv_to_gstring_list(GStrv lines); +gint output_value_from_str(const gchar *value_str); + +GDBusObjectManager *get_object_manager_client(const gchar *obj_path); +GPIODBusObject *get_chip_obj_by_path(const gchar *obj_path); +GPIODBusObject *get_chip_obj(const gchar *chip_name); +GList *get_chip_objs(GStrv chip_names); +gchar *make_request_obj_path(const gchar *request); +GPIODBusObject *get_request_obj(const gchar *request_name); +GList *get_request_objs(void); +GArray *get_request_offsets(GPIODBusRequest *request); +gboolean get_line_obj_by_name(const gchar *name, GPIODBusObject **line_obj, + GPIODBusObject **chip_obj); +GPIODBusObject * +get_line_obj_by_name_for_chip(GPIODBusObject *chip_obj, const gchar *name_line); +GList *get_all_line_objs_for_chip(GPIODBusObject *chip_obj); + +gchar *sanitize_object_path(const gchar *path); + +typedef struct { + gchar *name; + guint offset; + gboolean used; + gchar *consumer; + const gchar *direction; + const gchar *drive; + const gchar *bias; + gboolean active_low; + const gchar *edge; + gboolean debounced; + guint64 debounce_period; + const gchar *event_clock; + gboolean managed; + gchar *request_name; +} LineProperties; + +LineProperties *get_line_properties(GPIODBusLine *line); +void free_line_properties(LineProperties *props); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(LineProperties, free_line_properties); + +typedef struct { + gboolean input; + gboolean output; + gboolean active_low; + gboolean rising_edge; + gboolean falling_edge; + gboolean both_edges; + gboolean push_pull; + gboolean open_source; + gboolean open_drain; + gboolean pull_up; + gboolean pull_down; + gboolean bias_disabled; + gboolean clock_monotonic; + gboolean clock_realtime; + gboolean clock_hte; + GTimeSpan debounce_period; + GArray *output_values; +} LineConfigOpts; + +#define LINE_CONFIG_OPTIONS(opts) \ + { \ + .long_name = "input", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->input, \ + .description = "Set direction to input.", \ + }, \ + { \ + .long_name = "output", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->output, \ + .description = "Set direction to output.", \ + }, \ + { \ + .long_name = "rising-edge", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->rising_edge, \ + .description = "Monitor rising edges." \ + }, \ + { \ + .long_name = "falling-edge", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->falling_edge, \ + .description = "Monitor falling edges." \ + }, \ + { \ + .long_name = "both-edges", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->both_edges, \ + .description = "Monitor rising and falling edges." \ + }, \ + { \ + .long_name = "push-pull", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->push_pull, \ + .description = "Drive the line in push-pull mode.", \ + }, \ + { \ + .long_name = "open-drain", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->open_drain, \ + .description = "Drive the line in open-drain mode.", \ + }, \ + { \ + .long_name = "open-source", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->open_source, \ + .description = "Drive the line in open-source mode.", \ + }, \ + { \ + .long_name = "pull-up", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->pull_up, \ + .description = "Enable internal pull-up bias.", \ + }, \ + { \ + .long_name = "pull-down", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->pull_down, \ + .description = "Enable internal pull-down bias.", \ + }, \ + { \ + .long_name = "bias-disabled", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->bias_disabled, \ + .description = "Disable internal pull-up/down bias.", \ + }, \ + { \ + .long_name = "active-low", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->active_low, \ + .description = "Treat the lines as active low.", \ + }, \ + { \ + .long_name = "debounce-period", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_INT64, \ + .arg_data = &(opts)->debounce_period, \ + .arg_description = "", \ + .description = "Enable debouncing and set the period", \ + }, \ + { \ + .long_name = "clock-monotonic", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->clock_monotonic, \ + .description = "Use monotonic clock for edge event timestamps", \ + }, \ + { \ + .long_name = "clock-realtime", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->clock_realtime, \ + .description = "Use realtime clock for edge event timestamps", \ + }, \ + { \ + .long_name = "clock-hte", \ + .flags = G_OPTION_FLAG_NONE, \ + .arg = G_OPTION_ARG_NONE, \ + .arg_data = &(opts)->clock_hte, \ + .description = "Use HTE clock (if available) for edge event timestamps", \ + } + +void validate_line_config_opts(LineConfigOpts *opts); +GVariant *make_line_config(GArray *offsets, LineConfigOpts *cfg_opts); + +#endif /* __GPIOCLI_COMMON_H__ */ diff --git a/dbus/client/detect.c b/dbus/client/detect.c new file mode 100644 index 0000000..7797864 --- /dev/null +++ b/dbus/client/detect.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include + +#include "common.h" + +static void show_chip(gpointer elem, gpointer user_data G_GNUC_UNUSED) +{ + GPIODBusObject *chip_obj = elem; + GPIODBusChip *chip; + + chip = gpiodbus_object_peek_chip(chip_obj); + + g_print("%s [%s] (%u lines)\n", + gpiodbus_chip_get_name(chip), + gpiodbus_chip_get_label(chip), + gpiodbus_chip_get_num_lines(chip)); +} + +int gpiocli_detect_main(int argc, char **argv) +{ + static const gchar *const summary = +"List GPIO chips, print their labels and number of GPIO lines."; + + static const gchar *const description = +"Chips may be identified by name or number. e.g. '0' and 'gpiochip0' refer to\n" +"the same chip.\n" +"\n" +"If no chips are specified - display information for all chips in the system."; + + g_autolist(GPIODBusObject) chip_objs = NULL; + g_auto(GStrv) chip_names = NULL; + + const GOptionEntry opts[] = { + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &chip_names, + .arg_description = "[chip]...", + }, + { } + }; + + parse_options(opts, summary, description, &argc, &argv); + check_manager(); + + chip_objs = get_chip_objs(chip_names); + g_list_foreach(chip_objs, show_chip, NULL); + + return EXIT_SUCCESS; +} diff --git a/dbus/client/find.c b/dbus/client/find.c new file mode 100644 index 0000000..572314b --- /dev/null +++ b/dbus/client/find.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include + +#include "common.h" + +static void find_line_in_chip(gpointer elem, gpointer user_data) +{ + g_autoptr(GPIODBusObject) line_obj = NULL; + GPIODBusObject *chip_obj = elem; + const gchar *name = user_data; + GPIODBusChip *chip; + GPIODBusLine *line; + + line_obj = get_line_obj_by_name_for_chip(chip_obj, name); + if (!line_obj) + return; + + chip = gpiodbus_object_peek_chip(chip_obj); + line = gpiodbus_object_peek_line(line_obj); + + g_print("%s %u\n", + gpiodbus_chip_get_name(chip), + gpiodbus_line_get_offset(line)); + + exit(EXIT_SUCCESS); +} + +int gpiocli_find_main(int argc, char **argv) +{ + static const gchar *const summary = +"Gicen a line name, find the name of the parent chip and offset of the line within that chip."; + + static const gchar *const description = +"As line names are not guaranteed to be unique, this command finds the first line with given name."; + + g_autolist(GPIODBusObject) objs = NULL; + g_auto(GStrv) line_name = NULL; + + const GOptionEntry opts[] = { + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &line_name, + .arg_description = "", + }, + { } + }; + + parse_options(opts, summary, description, &argc, &argv); + check_manager(); + + if (!line_name) + die_parsing_opts("line name must be specified"); + if (g_strv_length(line_name) != 1) + die_parsing_opts("only one line can be mapped"); + + objs = get_chip_objs(NULL); + g_list_foreach(objs, find_line_in_chip, line_name[0]); + + /* If we got here, the line was not found. */ + die("line '%s' not found", line_name[0]); + return EXIT_FAILURE; +} diff --git a/dbus/client/get.c b/dbus/client/get.c new file mode 100644 index 0000000..92f4b0b --- /dev/null +++ b/dbus/client/get.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2024 Bartosz Golaszewski + +#include + +#include "common.h" + +int gpiocli_get_main(int argc, char **argv) +{ + static const gchar *const summary = +"Get values of one or more GPIO lines."; + + static const gchar *const description = +"If -r/--request is specified then all the lines must belong to the same\n" +"request (and - by extension - the same chip).\n" +"\n" +"If no lines are specified but -r/--request was passed then all lines within\n" +"the request will be used."; + + const gchar *request_name = NULL, *chip_path, *req_path; + gboolean ret, unquoted = FALSE, numeric = FALSE; + g_autoptr(GPIODBusObject) chip_obj = NULL; + g_autoptr(GPIODBusObject) req_obj = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + g_auto(GStrv) lines = NULL; + GPIODBusRequest *request; + GVariantBuilder builder; + GPIODBusLine *line; + gsize num_lines, i; + GVariantIter iter; + guint offset; + gint value; + + const GOptionEntry opts[] = { + { + .long_name = "request", + .short_name = 'r', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING, + .arg_data = &request_name, + .description = "restrict scope to a particular request", + .arg_description = "", + }, + { + .long_name = "unquoted", + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_NONE, + .arg_data = &unquoted, + .description = "don't quote line names", + }, + { + .long_name = "numeric", + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_NONE, + .arg_data = &numeric, + .description = "display line values as '0' (inactive) or '1' (active)", + }, + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &lines, + .arg_description = "[line0] [line1]...", + }, + { } + }; + + parse_options(opts, summary, description, &argc, &argv); + check_manager(); + + if (!lines && !request_name) + die_parsing_opts("either at least one line or the request must be specified"); + + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + num_lines = lines ? g_strv_length(lines) : 0; + + if (!request_name) { + /* + * TODO Limit the number of DBus calls by gathering the requests + * and their relevant lines into a container of some kind first. + */ + + values = g_array_sized_new(FALSE, TRUE, sizeof(gint), + num_lines); + + for (i = 0; i < num_lines; i++) { + g_autoptr(GPIODBusRequest) req_proxy = NULL; + g_autoptr(GPIODBusObject) line_obj = NULL; + g_autoptr(GVariant) arg_offsets = NULL; + g_autoptr(GVariant) arg_values = NULL; + + ret = get_line_obj_by_name(lines[i], &line_obj, NULL); + if (!ret) + die("Line not found: %s\n", lines[i]); + + line = gpiodbus_object_peek_line(line_obj); + req_path = gpiodbus_line_get_request_path(line); + + if (!gpiodbus_line_get_managed(line)) + die("Line '%s' not managed by gpio-manager, must be requested first", + lines[i]); + + req_proxy = gpiodbus_request_proxy_new_for_bus_sync( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + "io.gpiod1", req_path, + NULL, &err); + if (err) + die_gerror(err, + "Failed to get DBus proxy for '%s'", + req_path); + + offset = gpiodbus_line_get_offset(line); + g_array_append_val(offsets, offset); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add(&builder, "u", offset); + arg_offsets = g_variant_ref_sink( + g_variant_builder_end(&builder)); + + ret = gpiodbus_request_call_get_values_sync(req_proxy, + arg_offsets, + &arg_values, + NULL, &err); + if (!ret) + die_gerror(err, "Failed to get line values"); + + g_variant_iter_init(&iter, arg_values); + while (g_variant_iter_next(&iter, "i", &value)) + g_array_append_val(values, value); + } + } else { + g_autoptr(GVariant) arg_offsets = NULL; + g_autoptr(GVariant) arg_values = NULL; + + req_obj = get_request_obj(request_name); + request = gpiodbus_object_peek_request(req_obj); + chip_path = gpiodbus_request_get_chip_path(request); + chip_obj = get_chip_obj_by_path(chip_path); + + if (lines) { + for (i = 0; i < num_lines; i++) { + g_autoptr(GPIODBusObject) line_obj = NULL; + + line_obj = get_line_obj_by_name_for_chip( + chip_obj, lines[i]); + if (!line_obj) + die("Line not found: %s\n", lines[i]); + + line = gpiodbus_object_peek_line(line_obj); + + if (!gpiodbus_line_get_managed(line)) + die("Line '%s' not managed by gpio-manager, must be requested first", + lines[i]); + + offset = gpiodbus_line_get_offset(line); + g_array_append_val(offsets, offset); + } + } else { + offsets = get_request_offsets(request); + num_lines = offsets->len; + } + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + for (i = 0; i < offsets->len; i++) + g_variant_builder_add(&builder, "u", + g_array_index(offsets, guint, i)); + arg_offsets = g_variant_ref_sink( + g_variant_builder_end(&builder)); + + ret = gpiodbus_request_call_get_values_sync(request, + arg_offsets, + &arg_values, NULL, + &err); + if (!ret) + die_gerror(err, "Failed to get line values"); + + values = g_array_sized_new(FALSE, TRUE, sizeof(gint), + g_variant_n_children(arg_values)); + + g_variant_iter_init(&iter, arg_values); + while (g_variant_iter_next(&iter, "i", &value)) + g_array_append_val(values, value); + } + + for (i = 0; i < num_lines; i++) { + if (!unquoted) + g_print("\""); + + if (lines) + g_print("%s", lines[i]); + else + g_print("%u", g_array_index(offsets, guint, i)); + + if (!unquoted) + g_print("\""); + + g_print("=%s", g_array_index(values, guint, i) ? + numeric ? "1" : "active" : + numeric ? "0" : "inactive"); + + if (i != (num_lines - 1)) + g_print(" "); + } + g_print("\n"); + + return EXIT_SUCCESS; +} diff --git a/dbus/client/gpiocli.c b/dbus/client/gpiocli.c new file mode 100644 index 0000000..fbd1bbe --- /dev/null +++ b/dbus/client/gpiocli.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2022-2023 Bartosz Golaszewski + +#include + +#include "common.h" + +typedef struct { + gchar *name; + int (*main_func)(int argc, char **argv); + gchar *descr; +} GPIOCliCmd; + +int gpiocli_detect_main(int argc, char **argv); +int gpiocli_find_main(int argc, char **argv); +int gpiocli_info_main(int argc, char **argv); +int gpiocli_get_main(int argc, char **argv); +int gpiocli_monitor_main(int argc, char **argv); +int gpiocli_notify_main(int argc, char **argv); +int gpiocli_reconfigure_main(int argc, char **argv); +int gpiocli_release_main(int argc, char **argv); +int gpiocli_request_main(int argc, char **argv); +int gpiocli_requests_main(int argc, char **argv); +int gpiocli_set_main(int argc, char **argv); +int gpiocli_wait_main(int argc, char **argv); + +static const GPIOCliCmd cli_cmds[] = { + { + .name = "detect", + .main_func = gpiocli_detect_main, + .descr = "list GPIO chips and print their properties", + }, + { + .name = "find", + .main_func = gpiocli_find_main, + .descr = "take a line name and find its parent chip's name and offset within it", + }, + { + .name = "info", + .main_func = gpiocli_info_main, + .descr = "print information about GPIO lines", + }, + { + .name = "get", + .main_func = gpiocli_get_main, + .descr = "get values of GPIO lines", + }, + { + .name = "monitor", + .main_func = gpiocli_monitor_main, + .descr = "notify the user about edge events", + }, + { + .name = "notify", + .main_func = gpiocli_notify_main, + .descr = "notify the user about line property changes", + }, + { + .name = "reconfigure", + .main_func = gpiocli_reconfigure_main, + .descr = "change the line configuration for an existing request", + }, + { + .name = "release", + .main_func = gpiocli_release_main, + .descr = "release one of the line requests controlled by the manager", + }, + { + .name = "request", + .main_func = gpiocli_request_main, + .descr = "request a set of GPIO lines for exclusive usage by the manager", + }, + { + .name = "requests", + .main_func = gpiocli_requests_main, + .descr = "list all line requests controlled by the manager", + }, + { + .name = "set", + .main_func = gpiocli_set_main, + .descr = "set values of GPIO lines", + }, + { + .name = "wait", + .main_func = gpiocli_wait_main, + .descr = "wait for the gpio-manager interface to appear", + }, + { } +}; + +static GHashTable *make_cmd_table(void) +{ + GHashTable *cmd_table = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, NULL); + const GPIOCliCmd *cmd; + + for (cmd = &cli_cmds[0]; cmd->name; cmd++) + g_hash_table_insert(cmd_table, cmd->name, cmd->main_func); + + return cmd_table; +} + +static gchar *make_description(void) +{ + g_autoptr(GString) descr = g_string_new("Available commands:\n"); + const GPIOCliCmd *cmd; + + for (cmd = &cli_cmds[0]; cmd->name; cmd++) + g_string_append_printf(descr, " %s - %s\n", + cmd->name, cmd->descr); + + g_string_truncate(descr, descr->len - 1); + return g_strdup(descr->str); +} + +static void show_version_and_exit(void) +{ + g_print("gpiocli v%s\n", GPIOD_VERSION_STR); + + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + static const gchar *const summary = +"Simple command-line client for controlling gpio-manager."; + + g_autoptr(GHashTable) cmd_table = make_cmd_table(); + g_autofree gchar *description = make_description(); + g_autofree gchar *basename = NULL; + g_autofree gchar *cmd_name = NULL; + gint (*cmd_func)(gint, gchar **); + g_auto(GStrv) cmd_args = NULL; + gboolean show_version = FALSE; + + const GOptionEntry opts[] = { + { + .long_name = "version", + .short_name = 'v', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_NONE, + .arg_data = &show_version, + .description = "Show version and exit.", + }, + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &cmd_args, + .arg_description = "CMD [ARGS?] ...", + }, + { } + }; + + basename = g_path_get_basename(argv[0]); + g_set_prgname(basename); + + parse_options(opts, summary, description, &argc, &argv); + + if (show_version) + show_version_and_exit(); + + if (!cmd_args) + die_parsing_opts("Command must be specified."); + + cmd_func = g_hash_table_lookup(cmd_table, cmd_args[0]); + if (!cmd_func) + die_parsing_opts("Unknown command: %s.", cmd_args[0]); + + cmd_name = g_strdup_printf("%s %s", basename, cmd_args[0]); + g_set_prgname(cmd_name); + + return cmd_func(g_strv_length(cmd_args), cmd_args); +} diff --git a/dbus/client/info.c b/dbus/client/info.c new file mode 100644 index 0000000..ce549a0 --- /dev/null +++ b/dbus/client/info.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include + +#include "common.h" + +static gchar *make_line_name(const gchar *name) +{ + if (!name) + return g_strdup("unnamed"); + + return g_strdup_printf("\"%s\"", name); +} + +static void do_print_line_info(GPIODBusObject *line_obj, + GPIODBusObject *chip_obj) +{ + g_autoptr(LineProperties) props = NULL; + g_autoptr(GString) attributes = NULL; + g_autofree gchar *line_name = NULL; + GPIODBusChip *chip; + + props = get_line_properties(gpiodbus_object_peek_line(line_obj)); + line_name = make_line_name(props->name); + + attributes = g_string_new("["); + + if (props->used) + g_string_append_printf(attributes, "used,consumer=\"%s\",", + props->consumer); + + if (props->managed) + g_string_append_printf(attributes, "managed=\"%s\",", + props->request_name); + + if (props->edge) { + g_string_append_printf(attributes, "edges=%s,event-clock=%s,", + props->edge, props->event_clock); + if (props->debounced) + g_string_append_printf(attributes, + "debounce-period=%lu,", + props->debounce_period); + } + + if (props->bias) + g_string_append_printf(attributes, "bias=%s,", props->bias); + + if (props->active_low) + attributes = g_string_append(attributes, "active-low,"); + + g_string_append_printf(attributes, "%s", props->direction); + + if (g_strcmp0(props->direction, "output") == 0) + g_string_append_printf(attributes, ",%s", props->drive); + + attributes = g_string_append(attributes, "]"); + + if (chip_obj) { + chip = gpiodbus_object_peek_chip(chip_obj); + g_print("%s ", gpiodbus_chip_get_name(chip)); + } else { + g_print("\tline "); + } + + g_print("%3u:\t%s\t\t%s\n", props->offset, line_name, attributes->str); +} + +static void print_line_info(gpointer elem, gpointer user_data G_GNUC_UNUSED) +{ + GPIODBusObject *line_obj = elem; + + do_print_line_info(line_obj, NULL); +} + +static void do_show_chip(GPIODBusObject *chip_obj) +{ + GPIODBusChip *chip = gpiodbus_object_peek_chip(chip_obj); + g_autolist(GPIODBusObject) line_objs = NULL; + + g_print("%s - %u lines:\n", + gpiodbus_chip_get_name(chip), + gpiodbus_chip_get_num_lines(chip)); + + line_objs = get_all_line_objs_for_chip(chip_obj); + g_list_foreach(line_objs, print_line_info, NULL); +} + +static void show_chip(gpointer elem, gpointer user_data G_GNUC_UNUSED) +{ + GPIODBusObject *chip_obj = elem; + + do_show_chip(chip_obj); +} + +static void show_line_with_chip(gpointer elem, gpointer user_data) +{ + g_autoptr(GPIODBusObject) line_obj = NULL; + GPIODBusObject *chip_obj = user_data; + g_autofree gchar *chip_name = NULL; + GString *line_name = elem; + + line_obj = get_line_obj_by_name_for_chip(chip_obj, line_name->str); + if (!line_obj) { + chip_name = g_path_get_basename( + g_dbus_object_get_object_path(G_DBUS_OBJECT(chip_obj))); + die("no line '%s' on chip '%s'", line_name->str, chip_name); + } + + do_print_line_info(line_obj, chip_obj); +} + +static void show_line(gpointer elem, gpointer user_data G_GNUC_UNUSED) +{ + g_autoptr(GPIODBusObject) line_obj = NULL; + g_autoptr(GPIODBusObject) chip_obj = NULL; + GString *line_name = elem; + gboolean ret; + + ret = get_line_obj_by_name(line_name->str, &line_obj, &chip_obj); + if (!ret) + die("line '%s' not found", line_name->str); + + do_print_line_info(line_obj, chip_obj); +} + +int gpiocli_info_main(int argc, char **argv) +{ + static const gchar *const summary = +"Print information about GPIO lines."; + + static const gchar *const description = +"Lines are specified by name, or optionally by offset if the chip option\n" +"is provided.\n"; + + g_autolist(GPIODBusObject) chip_objs = NULL; + g_autolist(GString) line_name_list = NULL; + g_autoptr(GPIODBusObject) chip_obj = NULL; + g_auto(GStrv) line_names = NULL; + const gchar *chip_name = NULL; + + const GOptionEntry opts[] = { + { + .long_name = "chip", + .short_name = 'c', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING, + .arg_data = &chip_name, + .description = "restrict scope to a particular chip", + .arg_description = "", + }, + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &line_names, + .arg_description = "[line1] [line2] ...", + }, + { } + }; + + parse_options(opts, summary, description, &argc, &argv); + check_manager(); + + if (chip_name) + chip_obj = get_chip_obj(chip_name); + + if (line_names) { + line_name_list = strv_to_gstring_list(line_names); + if (chip_obj) + g_list_foreach(line_name_list, show_line_with_chip, + chip_obj); + else + g_list_foreach(line_name_list, show_line, NULL); + } else if (chip_obj) { + do_show_chip(chip_obj); + } else { + chip_objs = get_chip_objs(NULL); + g_list_foreach(chip_objs, show_chip, NULL); + } + + return EXIT_SUCCESS; +} diff --git a/dbus/client/monitor.c b/dbus/client/monitor.c new file mode 100644 index 0000000..39024b3 --- /dev/null +++ b/dbus/client/monitor.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2024 Bartosz Golaszewski + +#include +#include +#include +#include + +#include "common.h" + +typedef struct { + GList *lines; +} MonitorData; + +static void on_edge_event(GPIODBusLine *line, GVariant *args, + gpointer user_data G_GNUC_UNUSED) +{ + const char *name = gpiodbus_line_get_name(line); + gulong global_seqno, line_seqno; + guint64 timestamp; + gint edge; + + g_variant_get(args, "(ittt)", &edge, ×tamp, + &global_seqno, &line_seqno); + + g_print("%lu %s ", timestamp, edge ? "rising " : "falling"); + if (strlen(name)) + g_print("\"%s\"\n", name); + else + g_print("%u\n", gpiodbus_line_get_offset(line)); +} + +static void connect_edge_event(gpointer elem, gpointer user_data) +{ + GPIODBusObject *line_obj = elem; + MonitorData *data = user_data; + g_autoptr(GError) err = NULL; + const gchar *line_obj_path; + GPIODBusLine *line; + + line_obj_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(line_obj)); + + line = gpiodbus_line_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + "io.gpiod1", line_obj_path, + NULL, &err); + if (err) + die_gerror(err, "Failed to get DBus proxy for '%s'", + line_obj_path); + + if (!gpiodbus_line_get_managed(line)) + die("Line must be managed by gpio-manager in order to be monitored"); + + if (g_strcmp0(gpiodbus_line_get_edge_detection(line), "none") == 0) + die("Edge detection must be enabled for monitored lines"); + + data->lines = g_list_append(data->lines, line); + + g_signal_connect(line, "edge-event", G_CALLBACK(on_edge_event), NULL); +} + +int gpiocli_monitor_main(int argc, char **argv) +{ + static const gchar *const summary = +"Get values of one or more GPIO lines."; + + static const gchar *const description = +"If -r/--request is specified then all the lines must belong to the same\n" +"request (and - by extension - the same chip).\n" +"\n" +"If no lines are specified but -r/--request was passed then all lines within\n" +"the request will be used."; + + g_autoptr(GDBusObjectManager) manager = NULL; + const gchar *request_name = NULL, *chip_path; + g_autolist(GPIODBusObject) line_objs = NULL; + g_autoptr(GPIODBusObject) chip_obj = NULL; + g_autoptr(GPIODBusObject) req_obj = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GMainLoop) loop = NULL; + g_auto(GStrv) lines = NULL; + GPIODBusRequest *request; + MonitorData data = { }; + gsize num_lines, i; + guint watch_id; + gboolean ret; + + const GOptionEntry opts[] = { + { + .long_name = "request", + .short_name = 'r', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING, + .arg_data = &request_name, + .description = "restrict scope to a particular request", + .arg_description = "", + }, + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &lines, + .arg_description = "[line0] [line1]...", + }, + { } + }; + + parse_options(opts, summary, description, &argc, &argv); + + watch_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM, "io.gpiod1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + NULL, die_on_name_vanished, NULL, NULL); + check_manager(); + + if (!lines && !request_name) + die_parsing_opts("either at least one line or the request must be specified"); + + if (request_name) { + req_obj = get_request_obj(request_name); + request = gpiodbus_object_peek_request(req_obj); + chip_path = gpiodbus_request_get_chip_path(request); + chip_obj = get_chip_obj_by_path(chip_path); + offsets = g_array_new(FALSE, TRUE, sizeof(guint)); + + if (lines) { + num_lines = g_strv_length(lines); + + for (i = 0; i < num_lines; i++) { + g_autoptr(GPIODBusObject) line_obj = NULL; + + line_obj = get_line_obj_by_name_for_chip( + chip_obj, lines[i]); + if (!line_obj) + die("Line not found: %s\n", lines[i]); + + line_objs = g_list_append(line_objs, + g_object_ref(line_obj)); + } + } else { + offsets = get_request_offsets(request); + manager = get_object_manager_client(chip_path); + + for (i = 0; i < offsets->len; i++) { + g_autoptr(GPIODBusObject) line_obj = NULL; + g_autofree char *obj_path = NULL; + + obj_path = g_strdup_printf("%s/line%u", + chip_path, + g_array_index( + offsets, + guint, i)); + + line_obj = GPIODBUS_OBJECT( + g_dbus_object_manager_get_object( + manager, + obj_path)); + if (!line_obj) + die("Line not found: %u\n", + g_array_index(offsets, guint, i)); + + line_objs = g_list_append(line_objs, + g_object_ref(line_obj)); + } + } + } else { + num_lines = g_strv_length(lines); + + for (i = 0; i < num_lines; i++) { + g_autoptr(GPIODBusObject) line_obj = NULL; + + ret = get_line_obj_by_name(lines[i], &line_obj, NULL); + if (!ret) + die("Line not found: %s\n", lines[i]); + + line_objs = g_list_append(line_objs, + g_object_ref(line_obj)); + } + } + + g_list_foreach(line_objs, connect_edge_event, &data); + + loop = g_main_loop_new(NULL, FALSE); + g_unix_signal_add(SIGTERM, quit_main_loop_on_signal, loop); + g_unix_signal_add(SIGINT, quit_main_loop_on_signal, loop); + + g_main_loop_run(loop); + + g_bus_unwatch_name(watch_id); + + return EXIT_SUCCESS; +} diff --git a/dbus/client/notify.c b/dbus/client/notify.c new file mode 100644 index 0000000..3926fc4 --- /dev/null +++ b/dbus/client/notify.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include +#include +#include + +#include "common.h" + +/* + * Used to keep line proxies and chip interfaces alive for the duration of the + * program, which is required for signals to work. + */ +typedef struct { + GList *lines; + GList *chips; + GPIODBusObject *scoped_chip; +} NotifyData; + +static void clear_notify_data(NotifyData *data) +{ + g_list_free_full(data->lines, g_object_unref); + g_list_free_full(data->chips, g_object_unref); + + if (data->scoped_chip) + g_clear_object(&data->scoped_chip); +} + +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(NotifyData, clear_notify_data); + +static const gchar *bool_to_str(gboolean val) +{ + return val ? "True" : "False"; +} + +static const gchar *bool_variant_to_str(GVariant *val) +{ + return bool_to_str(g_variant_get_boolean(val)); +} + +static void +on_properties_changed(GPIODBusLine *line, GVariant *changed_properties, + GStrv invalidated_properties G_GNUC_UNUSED, + gpointer user_data) +{ + GPIODBusChip *chip = user_data; + g_autofree gchar *name = NULL; + const gchar *consumer, *tmp; + GVariantIter iter; + GVariant *v; + gsize len; + gchar *k; + + if (g_variant_n_children(changed_properties) == 0) + return; + + tmp = gpiodbus_line_get_name(line); + name = tmp ? g_strdup_printf("\"%s\"", tmp) : g_strdup("unnamed"); + + g_variant_iter_init(&iter, changed_properties); + while (g_variant_iter_next(&iter, "{sv}", &k, &v)) { + g_autoptr(GString) change = g_string_new(NULL); + g_autofree gchar *req_name = NULL; + g_autoptr(GVariant) val = v; + g_autofree gchar *key = k; + + if (g_strcmp0(key, "Consumer") == 0) { + consumer = g_variant_get_string(val, &len); + g_string_printf(change, "consumer=>\"%s\"", + len ? consumer : "unused"); + } else if (g_strcmp0(key, "Used") == 0) { + g_string_printf(change, "used=>%s", + bool_variant_to_str(val)); + } else if (g_strcmp0(key, "Debounced") == 0) { + g_string_printf(change, "debounced=>%s", + bool_variant_to_str(val)); + } else if (g_strcmp0(key, "ActiveLow") == 0) { + g_string_printf(change, "active-low=>%s", + bool_variant_to_str(val)); + } else if (g_strcmp0(key, "Direction") == 0) { + g_string_printf(change, "direction=>%s", + g_variant_get_string(val, NULL)); + } else if (g_strcmp0(key, "Drive") == 0) { + g_string_printf(change, "drive=>%s", + g_variant_get_string(val, NULL)); + } else if (g_strcmp0(key, "Bias") == 0) { + g_string_printf(change, "bias=>%s", + g_variant_get_string(val, NULL)); + } else if (g_strcmp0(key, "EdgeDetection") == 0) { + g_string_printf(change, "edge=>%s", + g_variant_get_string(val, NULL)); + } else if (g_strcmp0(key, "EventClock") == 0) { + g_string_printf(change, "event-clock=>%s", + g_variant_get_string(val, NULL)); + } else if (g_strcmp0(key, "DebouncePeriodUs") == 0) { + g_string_printf(change, "debounce-period=>%ld", + g_variant_get_uint64(val)); + } else if (g_strcmp0(key, "Managed") == 0) { + g_string_printf(change, "managed=>%s", + bool_variant_to_str(val)); + } else if (g_strcmp0(key, "RequestPath") == 0) { + req_name = sanitize_object_path( + g_variant_get_string(val, NULL)); + g_string_printf(change, "request=>%s", + req_name); + } else { + die("unexpected property update received from manager: '%s'", + key); + } + + g_print("%s - %u (%s): [%s]\n", gpiodbus_chip_get_name(chip), + gpiodbus_line_get_offset(line), name ?: "unnamed", + change->str); + } +} + +static void print_line_info(GPIODBusLine *line, GPIODBusChip *chip) +{ + g_autoptr(LineProperties) props = get_line_properties(line); + g_autoptr(GString) attrs = g_string_new(props->direction); + g_autofree gchar *name = NULL; + + if (props->used) + g_string_append(attrs, ",used"); + + if (props->consumer) + g_string_append_printf(attrs, ",consumer=\"%s\"", + props->consumer); + + if (props->drive && g_strcmp0(props->direction, "output") == 0) + g_string_append_printf(attrs, ",%s", props->drive); + + if (props->bias) { + if (g_strcmp0(props->bias, "disabled") == 0) + g_string_append(attrs, ",bias-disabled"); + else + g_string_append_printf(attrs, ",%s", props->bias); + } + + if (props->active_low) + g_string_append(attrs, ",active-low"); + + if (props->edge) { + if (g_strcmp0(props->edge, "both") == 0) + g_string_append(attrs, ",both-edges"); + else + g_string_append_printf(attrs, ",%s-edge", props->edge); + + g_string_append_printf(attrs, ",%s-clock", props->event_clock); + + if (props->debounced) + g_string_append_printf(attrs, + "debounced,debounce-period=%lu", + props->debounce_period); + } + + if (props->managed) + g_string_append_printf(attrs, ",managed,request=\"%s\"", + props->request_name); + + name = props->name ? g_strdup_printf("\"%s\"", props->name) : + g_strdup("unnamed"); + + g_print("%s - %u (%s): [%s]\n", gpiodbus_chip_get_name(chip), + props->offset, name ?: "unnamed", attrs->str); +} + +static void connect_line(gpointer elem, gpointer user_data) +{ + g_autoptr(GPIODBusObject) line_obj = NULL; + g_autoptr(GPIODBusObject) chip_obj = NULL; + g_autoptr(GPIODBusLine) line = NULL; + g_autoptr(GPIODBusChip) chip = NULL; + g_autofree gchar *chip_name = NULL; + g_autoptr(GError) err = NULL; + NotifyData *data = user_data; + const gchar *line_obj_path; + GString *line_name = elem; + gboolean ret; + + if (data->scoped_chip) { + chip_obj = g_object_ref(data->scoped_chip); + line_obj = get_line_obj_by_name_for_chip(chip_obj, + line_name->str); + if (!line_obj) { + chip_name = g_path_get_basename( + g_dbus_object_get_object_path( + G_DBUS_OBJECT(chip_obj))); + die("no line '%s' on chip '%s'", + line_name->str, chip_name); + } + } else { + ret = get_line_obj_by_name(line_name->str, + &line_obj, &chip_obj); + if (!ret) + die("line '%s' not found", line_name->str); + } + + line_obj_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(line_obj)); + + line = gpiodbus_line_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + "io.gpiod1", line_obj_path, + NULL, &err); + if (err) + die_gerror(err, "Failed to get DBus proxy for '%s'", + line_obj_path); + + data->lines = g_list_append(data->lines, g_object_ref(line)); + + if (data->scoped_chip) { + if (g_list_length(data->chips) == 0) { + chip = gpiodbus_object_get_chip(chip_obj); + data->chips = g_list_append(data->chips, + g_object_ref(chip)); + } else { + chip = g_list_first(data->chips)->data; + } + } else { + chip = gpiodbus_object_get_chip(chip_obj); + data->chips = g_list_append(data->chips, g_object_ref(chip)); + } + + print_line_info(line, chip); + + g_signal_connect(line, "g-properties-changed", + G_CALLBACK(on_properties_changed), chip); +} + +int gpiocli_notify_main(int argc, char **argv) +{ + static const gchar *const summary = +"Monitor a set of lines for property changes."; + + static const gchar *const description = +"Lines are specified by name, or optionally by offset if the chip option\n" +"is provided.\n"; + + g_autolist(GString) line_name_list = NULL; + g_autoptr(GMainLoop) loop = NULL; + g_auto(GStrv) line_names = NULL; + const gchar *chip_name = NULL; + /* + * FIXME: data internals must be freed but there's some issue with + * unrefing the GPIODBusObject here. For now it's leaking memory. + */ + NotifyData data = { }; + guint watch_id; + + const GOptionEntry opts[] = { + { + .long_name = "chip", + .short_name = 'c', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING, + .arg_data = &chip_name, + .description = "restrict scope to a particular chip", + .arg_description = "", + }, + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &line_names, + .arg_description = " [line2] ...", + }, + { } + }; + + parse_options(opts, summary, description, &argc, &argv); + + watch_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM, "io.gpiod1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + NULL, die_on_name_vanished, NULL, NULL); + check_manager(); + + if (!line_names) + die_parsing_opts("at least one line must be specified"); + + if (chip_name) + data.scoped_chip = get_chip_obj(chip_name); + + line_name_list = strv_to_gstring_list(line_names); + g_list_foreach(line_name_list, connect_line, &data); + + loop = g_main_loop_new(NULL, FALSE); + g_unix_signal_add(SIGTERM, quit_main_loop_on_signal, loop); + g_unix_signal_add(SIGINT, quit_main_loop_on_signal, loop); + + g_main_loop_run(loop); + + g_bus_unwatch_name(watch_id); + + return EXIT_SUCCESS; +} diff --git a/dbus/client/reconfigure.c b/dbus/client/reconfigure.c new file mode 100644 index 0000000..441c821 --- /dev/null +++ b/dbus/client/reconfigure.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2024 Bartosz Golaszewski + +#include + +#include "common.h" + +int gpiocli_reconfigure_main(int argc, char **argv) +{ + static const gchar *const summary = +"Change the line configuration for an existing request."; + + g_autoptr(GPIODBusObject) req_obj = NULL; + g_autoptr(GVariant) line_config = NULL; + g_autoptr(GArray) output_values = NULL; + LineConfigOpts line_cfg_opts = { }; + g_autoptr(GArray) offsets = NULL; + g_auto(GStrv) remaining = NULL; + g_autoptr(GError) err = NULL; + GPIODBusRequest *request; + gsize num_values; + gboolean ret; + gint val; + guint i; + + const GOptionEntry opts[] = { + LINE_CONFIG_OPTIONS(&line_cfg_opts), + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &remaining, + .arg_description = " [value1] [value2]...", + }, + { } + }; + + parse_options(opts, summary, NULL, &argc, &argv); + validate_line_config_opts(&line_cfg_opts); + + if (!remaining || g_strv_length(remaining) == 0) + die_parsing_opts("Exactly one request to reconfigure must be specified."); + + num_values = g_strv_length(remaining) - 1; + + check_manager(); + + req_obj = get_request_obj(remaining[0]); + request = gpiodbus_object_peek_request(req_obj); + offsets = get_request_offsets(request); + + if (num_values) { + if (num_values != offsets->len) + die_parsing_opts("The number of output values must correspond to the number of lines in the request"); + + output_values = g_array_sized_new(FALSE, TRUE, sizeof(gint), + num_values); + + for (i = 0; i < num_values; i++) { + val = output_value_from_str(remaining[i + 1]); + g_array_append_val(output_values, val); + } + } + + line_cfg_opts.output_values = output_values; + line_config = make_line_config(offsets, &line_cfg_opts); + + ret = gpiodbus_request_call_reconfigure_lines_sync(request, line_config, + NULL, &err); + if (!ret) + die_gerror(err, "Failed to reconfigure lines"); + + return EXIT_SUCCESS; +} diff --git a/dbus/client/release.c b/dbus/client/release.c new file mode 100644 index 0000000..dcca33b --- /dev/null +++ b/dbus/client/release.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include + +#include "common.h" + +int gpiocli_release_main(int argc, char **argv) +{ + static const gchar *const summary = +"Release one of the line requests controlled by the manager."; + + g_autoptr(GDBusObjectManager) manager = NULL; + g_autoptr(GPIODBusObject) obj = NULL; + g_autofree gchar *obj_path = NULL; + g_auto(GStrv) remaining = NULL; + g_autoptr(GError) err = NULL; + const gchar *request_name; + GPIODBusRequest *request; + gboolean ret; + + const GOptionEntry opts[] = { + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &remaining, + .arg_description = "", + }, + { } + }; + + parse_options(opts, summary, NULL, &argc, &argv); + + if (!remaining || g_strv_length(remaining) != 1) + die_parsing_opts("Exactly one request to release must be specified."); + + check_manager(); + + request_name = remaining[0]; + + obj_path = make_request_obj_path(request_name); + manager = get_object_manager_client("/io/gpiod1/requests"); + obj = GPIODBUS_OBJECT(g_dbus_object_manager_get_object(manager, + obj_path)); + if (!obj) + goto no_request; + + request = gpiodbus_object_peek_request(obj); + if (!request) + goto no_request; + + ret = gpiodbus_request_call_release_sync(request, NULL, &err); + if (!ret) + die_gerror(err, "Failed to release request '%s': %s", + request_name, err->message); + + return EXIT_SUCCESS; + +no_request: + die("No such request: '%s'", request_name); +} diff --git a/dbus/client/request.c b/dbus/client/request.c new file mode 100644 index 0000000..0e93ed9 --- /dev/null +++ b/dbus/client/request.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023-2024 Bartosz Golaszewski + +#include + +#include "common.h" + +typedef struct { + LineConfigOpts line_cfg_opts; + const gchar *consumer; +} RequestOpts; + +typedef struct { + const gchar *request_path; + gboolean done; +} RequestWaitData; + +static GVariant *make_request_config(RequestOpts *opts) +{ + GVariantBuilder builder; + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, + g_variant_new("{sv}", "consumer", + g_variant_new_string(opts->consumer))); + + return g_variant_ref_sink(g_variant_builder_end(&builder)); +} + +static gboolean on_timeout(gpointer user_data G_GNUC_UNUSED) +{ + die("wait for request to appear timed out!"); +} + +static void obj_match_request_path(GPIODBusObject *obj, RequestWaitData *data) +{ + if (g_strcmp0(g_dbus_object_get_object_path(G_DBUS_OBJECT(obj)), + data->request_path) == 0) + data->done = TRUE; +} + +static void match_request_path(gpointer elem, gpointer user_data) +{ + RequestWaitData *data = user_data; + GPIODBusObject *obj = elem; + + obj_match_request_path(obj, data); +} + +static void on_object_added(GDBusObjectManager *manager G_GNUC_UNUSED, + GPIODBusObject *obj, gpointer user_data) +{ + RequestWaitData *data = user_data; + + obj_match_request_path(GPIODBUS_OBJECT(obj), data); +} + +static void wait_for_request(const gchar *request_path) +{ + RequestWaitData data = { .request_path = request_path }; + g_autoptr(GDBusObjectManager) manager = NULL; + g_autolist(GPIODBusObject) objs = NULL; + + manager = get_object_manager_client("/io/gpiod1/requests"); + + g_signal_connect(manager, "object-added", + G_CALLBACK(on_object_added), &data); + + objs = g_dbus_object_manager_get_objects(manager); + g_list_foreach(objs, match_request_path, &data); + + g_timeout_add(5000, on_timeout, NULL); + + while (!data.done) + g_main_context_iteration(NULL, TRUE); +} + +static int +request_lines(GList *line_names, const gchar *chip_name, RequestOpts *req_opts) +{ + g_autoptr(GPIODBusObject) chip_obj = NULL; + g_autoptr(GVariant) request_config = NULL; + g_autoptr(GVariant) line_config = NULL; + g_autofree gchar *request_path = NULL; + g_autofree gchar *request_name = NULL; + g_autofree gchar *dyn_name = NULL; + g_autoptr(GArray) offsets = NULL; + g_autoptr(GError) err = NULL; + GPIODBusLine *line; + GPIODBusChip *chip; + GString *line_name; + guint i, *offset; + gboolean ret; + GList *pos; + gsize llen; + + llen = g_list_length(line_names); + offsets = g_array_sized_new(FALSE, TRUE, sizeof(guint), llen); + g_array_set_size(offsets, llen); + + if (chip_name) + chip_obj = get_chip_obj(chip_name); + + for (i = 0, pos = g_list_first(line_names); + i < llen; + i++, pos = g_list_next(pos)) { + g_autoptr(GPIODBusObject) line_obj = NULL; + + line_name = pos->data; + + if (chip_obj) { + line_obj = get_line_obj_by_name_for_chip(chip_obj, + line_name->str); + if (!line_obj) { + if (dyn_name) { + ret = get_line_obj_by_name( + line_name->str, + &line_obj, NULL); + if (ret) + /* + * This means the line exists + * but on a different chip. + */ + die("all requested lines must belong to the same chip"); + } + + die("no line '%s' on chip '%s'", + line_name->str, chip_name); + } + } else { + ret = get_line_obj_by_name(line_name->str, &line_obj, + &chip_obj); + if (!ret) + die("line '%s' not found", line_name->str); + + dyn_name = g_path_get_basename( + g_dbus_object_get_object_path( + G_DBUS_OBJECT(chip_obj))); + chip_name = dyn_name; + } + + line = gpiodbus_object_peek_line(line_obj); + offset = &g_array_index(offsets, guint, i); + *offset = gpiodbus_line_get_offset(line); + } + + chip = gpiodbus_object_peek_chip(chip_obj); + line_config = make_line_config(offsets, &req_opts->line_cfg_opts); + request_config = make_request_config(req_opts); + + ret = gpiodbus_chip_call_request_lines_sync(chip, line_config, + request_config, + &request_path, NULL, &err); + if (err) + die_gerror(err, "failed to request lines from chip '%s'", + chip_name); + + wait_for_request(request_path); + + request_name = g_path_get_basename(request_path); + g_print("%s\n", request_name); + + return EXIT_SUCCESS; +} + +int gpiocli_request_main(int argc, char **argv) +{ + static const gchar *const summary = +"Request a set of GPIO lines for exclusive usage by the gpio-manager."; + + g_autoptr(GArray) output_values = NULL; + g_autolist(GString) line_names = NULL; + const gchar *chip_name = NULL; + g_auto(GStrv) lines = NULL; + RequestOpts req_opts = {}; + gsize llen; + gint val; + guint i; + + const GOptionEntry opts[] = { + { + .long_name = "chip", + .short_name = 'c', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING, + .arg_data = &chip_name, + .description = +"Explicitly specify the chip_name on which to resolve the lines which allows to use raw offsets instead of line names.", + .arg_description = "", + }, + { + .long_name = "consumer", + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING, + .arg_data = &req_opts.consumer, + .description = "Consumer string (defaults to program name)", + .arg_description = "", + }, + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &lines, + .arg_description = "[=value1] [line2[=value2]] ...", + }, + LINE_CONFIG_OPTIONS(&req_opts.line_cfg_opts), + { } + }; + + parse_options(opts, summary, NULL, &argc, &argv); + validate_line_config_opts(&req_opts.line_cfg_opts); + + if (!lines) + die_parsing_opts("At least one line must be specified"); + + if (!req_opts.consumer) + req_opts.consumer = "gpio-manager"; + + for (i = 0, llen = g_strv_length(lines); i < llen; i++) { + g_auto(GStrv) tokens = NULL; + + tokens = g_strsplit(lines[i], "=", 2); + line_names = g_list_append(line_names, g_string_new(tokens[0])); + if (g_strv_length(tokens) == 2) { + if (!req_opts.line_cfg_opts.output) + die_parsing_opts("Output values can only be set in output mode"); + + if (!output_values) + output_values = g_array_sized_new(FALSE, TRUE, + sizeof(gint), + llen); + val = output_value_from_str(tokens[1]); + g_array_append_val(output_values, val); + } + } + + if (output_values && req_opts.line_cfg_opts.input) + die_parsing_opts("cannot set output values in input mode"); + + if (output_values && + (g_list_length(line_names) != output_values->len)) + die_parsing_opts("if values are set, they must be set for all lines"); + + req_opts.line_cfg_opts.output_values = output_values; + + check_manager(); + + return request_lines(line_names, chip_name, &req_opts); +} diff --git a/dbus/client/requests.c b/dbus/client/requests.c new file mode 100644 index 0000000..c1394d8 --- /dev/null +++ b/dbus/client/requests.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include + +#include "common.h" + +static void show_request(gpointer elem, gpointer user_data G_GNUC_UNUSED) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + g_autofree gchar *request_name = NULL; + g_autofree gchar *offsets_str = NULL; + g_autoptr(GVariant) voffsets = NULL; + g_autofree gchar *chip_name = NULL; + g_autoptr(GArray) offsets = NULL; + GPIODBusObject *obj = elem; + GPIODBusRequest *request; + GVariantBuilder builder; + const gchar *chip_path; + gsize i; + + request_name = g_path_get_basename( + g_dbus_object_get_object_path(G_DBUS_OBJECT(obj))); + request = gpiodbus_object_peek_request(obj); + chip_path = gpiodbus_request_get_chip_path(request); + manager = get_object_manager_client(chip_path); + /* FIXME: Use chip proxy? */ + chip_name = g_path_get_basename(chip_path); + + offsets = get_request_offsets(request); + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + for (i = 0; i < offsets->len; i++) + g_variant_builder_add(&builder, "u", + g_array_index(offsets, guint, i)); + voffsets = g_variant_ref_sink(g_variant_builder_end(&builder)); + offsets_str = g_variant_print(voffsets, FALSE); + + g_print("%s (%s) Offsets: %s\n", + request_name, chip_name, offsets_str); +} + +int gpiocli_requests_main(int argc, char **argv) +{ + static const gchar *const summary = +"List all line requests controlled by the manager."; + + g_autolist(GPIODBusObject) request_objs = NULL; + g_auto(GStrv) remaining = NULL; + + const GOptionEntry opts[] = { + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &remaining, + .arg_description = NULL, + }, + { } + }; + + parse_options(opts, summary, NULL, &argc, &argv); + check_manager(); + + if (remaining) + die_parsing_opts("command doesn't take additional arguments"); + + request_objs = get_request_objs(); + g_list_foreach(request_objs, show_request, NULL); + + return EXIT_SUCCESS; +} diff --git a/dbus/client/set.c b/dbus/client/set.c new file mode 100644 index 0000000..4a70b9e --- /dev/null +++ b/dbus/client/set.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2024 Bartosz Golaszewski + +#include + +#include "common.h" + +static void free_str(gpointer data) +{ + GString *str = data; + + g_string_free(str, TRUE); +} + +int gpiocli_set_main(int argc, char **argv) +{ + static const gchar *const summary = +"Set values of one or more GPIO lines."; + + static const gchar *const description = +"If -r/--request is specified then all the lines must belong to the same\n" +"request (and - by extension - the same chip)."; + + const gchar *request_name = NULL, *chip_path, *req_path; + g_autoptr(GPIODBusObject) chip_obj = NULL; + g_autoptr(GPIODBusObject) req_obj = NULL; + g_autoptr(GPtrArray) line_names = NULL; + g_autoptr(GArray) values = NULL; + g_autoptr(GError) err = NULL; + g_auto(GStrv) lines = NULL; + GPIODBusRequest *request; + GVariantBuilder builder; + GPIODBusLine *line; + gsize num_lines, i; + GString *line_name; + gboolean ret; + guint offset; + gint val; + + const GOptionEntry opts[] = { + { + .long_name = "request", + .short_name = 'r', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING, + .arg_data = &request_name, + .description = "restrict scope to a particular request", + .arg_description = "", + }, + { + .long_name = G_OPTION_REMAINING, + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING_ARRAY, + .arg_data = &lines, + .arg_description = " [line2=value2] ...", + }, + { } + }; + + parse_options(opts, summary, description, &argc, &argv); + + if (!lines) + die_parsing_opts("at least one line value must be specified"); + + num_lines = g_strv_length(lines); + line_names = g_ptr_array_new_full(num_lines, free_str); + values = g_array_sized_new(FALSE, TRUE, sizeof(gint), num_lines); + + for (i = 0; i < num_lines; i++) { + g_auto(GStrv) tokens = NULL; + + tokens = g_strsplit(lines[i], "=", 2); + if (g_strv_length(tokens) != 2) + die_parsing_opts("line must have a single value assigned"); + + g_ptr_array_add(line_names, g_string_new(tokens[0])); + val = output_value_from_str(tokens[1]); + g_array_append_val(values, val); + } + + check_manager(); + + if (request_name) { + g_autoptr(GVariant) arg_values = NULL; + g_autoptr(GArray) offsets = NULL; + + req_obj = get_request_obj(request_name); + request = gpiodbus_object_peek_request(req_obj); + chip_path = gpiodbus_request_get_chip_path(request); + chip_obj = get_chip_obj_by_path(chip_path); + offsets = g_array_sized_new(FALSE, TRUE, sizeof(guint), + num_lines); + + for (i = 0; i < num_lines; i++) { + g_autoptr(GPIODBusObject) line_obj = NULL; + + line_name = g_ptr_array_index(line_names, i); + + line_obj = get_line_obj_by_name_for_chip(chip_obj, + line_name->str); + if (!line_obj) + die("Line not found: %s\n", line_name->str); + + line = gpiodbus_object_peek_line(line_obj); + offset = gpiodbus_line_get_offset(line); + g_array_append_val(offsets, offset); + } + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + for (i = 0; i < num_lines; i++) { + g_variant_builder_add(&builder, "{ui}", + g_array_index(offsets, guint, i), + g_array_index(values, gint, i)); + } + + arg_values = g_variant_ref_sink( + g_variant_builder_end(&builder)); + + ret = gpiodbus_request_call_set_values_sync(request, arg_values, + NULL, &err); + if (!ret) + die_gerror(err, "Failed to set line values"); + + return EXIT_SUCCESS; + } + + for (i = 0; i < num_lines; i++) { + g_autoptr(GPIODBusRequest) req_proxy = NULL; + g_autoptr(GPIODBusObject) line_obj = NULL; + g_autoptr(GVariant) arg_values = NULL; + + line_name = g_ptr_array_index(line_names, i); + + ret = get_line_obj_by_name(line_name->str, &line_obj, NULL); + if (!ret) + die("Line not found: %s\n", line_name->str); + + line = gpiodbus_object_peek_line(line_obj); + req_path = gpiodbus_line_get_request_path(line); + + if (!gpiodbus_line_get_managed(line)) + die("Line '%s' not managed by gpio-manager, must be requested first", + line_name->str); + + req_proxy = gpiodbus_request_proxy_new_for_bus_sync( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + "io.gpiod1", req_path, + NULL, &err); + if (err) + die_gerror(err, "Failed to get DBus proxy for '%s'", + req_path); + + offset = gpiodbus_line_get_offset(line); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add(&builder, "{ui}", offset, + g_array_index(values, gint, i)); + arg_values = g_variant_ref_sink( + g_variant_builder_end(&builder)); + + ret = gpiodbus_request_call_set_values_sync(req_proxy, + arg_values, + NULL, &err); + if (!ret) + die_gerror(err, "Failed to set line values"); + } + + return EXIT_SUCCESS; +} diff --git a/dbus/client/wait.c b/dbus/client/wait.c new file mode 100644 index 0000000..f63930d --- /dev/null +++ b/dbus/client/wait.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: 2023 Bartosz Golaszewski + +#include + +#include "common.h" + +typedef struct { + gboolean name_done; + gboolean chip_done; + const gchar *label; +} WaitData; + +static void obj_match_label(GPIODBusObject *chip_obj, WaitData *data) +{ + GPIODBusChip *chip = gpiodbus_object_peek_chip(chip_obj); + + if (g_strcmp0(gpiodbus_chip_get_label(chip), data->label) == 0) + data->chip_done = TRUE; +} + +static void check_label(gpointer elem, gpointer user_data) +{ + WaitData *data = user_data; + GPIODBusObject *obj = elem; + + obj_match_label(obj, data); +} + +static void on_object_added(GDBusObjectManager *manager G_GNUC_UNUSED, + GPIODBusObject *obj, gpointer user_data) +{ + WaitData *data = user_data; + + obj_match_label(GPIODBUS_OBJECT(obj), data); +} + +static void wait_for_chip(WaitData *data) +{ + g_autoptr(GDBusObjectManager) manager = NULL; + g_autolist(GPIODBusObject) objs = NULL; + + manager = get_object_manager_client("/io/gpiod1/chips"); + + g_signal_connect(manager, "object-added", + G_CALLBACK(on_object_added), data); + + objs = g_dbus_object_manager_get_objects(manager); + g_list_foreach(objs, check_label, data); + + while (!data->chip_done) + g_main_context_iteration(NULL, TRUE); +} + +static void on_name_appeared(GDBusConnection *con G_GNUC_UNUSED, + const gchar *name G_GNUC_UNUSED, + const gchar *name_owner G_GNUC_UNUSED, + gpointer user_data) +{ + WaitData *data = user_data; + + data->name_done = TRUE; +} + +static void on_name_vanished(GDBusConnection *con G_GNUC_UNUSED, + const gchar *name G_GNUC_UNUSED, + gpointer user_data) +{ + WaitData *data = user_data; + + if (data->label && data->chip_done) + die("gpio-manager vanished while waiting for chip"); +} + +static gboolean on_timeout(gpointer user_data G_GNUC_UNUSED) +{ + die("wait timed out!"); +} + +static guint schedule_timeout(const gchar *timeout) +{ + gint64 period, multiplier = 0; + gchar *end; + + period = g_ascii_strtoll(timeout, &end, 10); + + switch (*end) { + case 'm': + multiplier = 1; + end++; + break; + case 's': + multiplier = 1000; + break; + case '\0': + break; + default: + goto invalid_timeout; + } + + if (multiplier) { + if (*end != 's') + goto invalid_timeout; + + end++; + } else { + /* Default to miliseconds. */ + multiplier = 1; + } + + period *= multiplier; + if (period > G_MAXUINT) + die("timeout must not exceed %u miliseconds\n", G_MAXUINT); + + return g_timeout_add(period, on_timeout, NULL); + +invalid_timeout: + die("invalid timeout value: %s", timeout); +} + +int gpiocli_wait_main(int argc, char **argv) +{ + static const gchar *const summary = +"Wait for the gpio-manager interface to appear."; + + static const gchar *const description = +"Timeout period defaults to miliseconds but can be given in seconds or miliseconds\n" +"explicitly .e.g: --timeout=1000, --timeout=1000ms and --timeout=1s all specify\n" +"the same period."; + + const gchar *timeout_str = NULL; + guint watch_id, timeout_id = 0; + g_auto(GStrv) remaining = NULL; + WaitData data = {}; + + const GOptionEntry opts[] = { + { + .long_name = "chip", + .short_name = 'c', + .flags = G_OPTION_FLAG_NONE, + .arg = G_OPTION_ARG_STRING, + .arg_data = &data.label, + .description = "Wait for a specific chip to appear.", + .arg_description = "