Linux GPIO subsystem development
 help / color / mirror / Atom feed
From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
To: Linus Walleij <linus.walleij@linaro.org>,
	Kent Gibson <warthog618@gmail.com>,
	Vincent Fazio <vfazio@xes-inc.com>,
	Alexander Dahl <post@lespocky.de>
Cc: brgl@kernel.org, linux-gpio@vger.kernel.org,
	Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Subject: [PATCH 1/2] tools: provide the libgpiotools shared library
Date: Wed, 13 May 2026 13:47:12 +0200	[thread overview]
Message-ID: <20260513-tools-common-shared-lib-v1-1-58ac28a9f1d5@oss.qualcomm.com> (raw)
In-Reply-To: <20260513-tools-common-shared-lib-v1-0-58ac28a9f1d5@oss.qualcomm.com>

In order to enable users of the project to reuse the high-level functions
provided to gpio-tools via the static tools-common library (for
instance: for line lookup by name), expose a sub-set of its
functionality in new shared library: libgpiotools, accompanied by a new
public header and providing a stable API/ABI.

Add a new rst page documenting the public API of the libgpiotools shared
library and integrate it into the sphinx build.

Closes: https://github.com/brgl/libgpiod/discussions/178
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
 docs/Doxyfile           |   1 +
 docs/gpio_tools.rst     |  16 ++
 docs/gpio_tools_lib.rst |  12 ++
 meson.build             |   3 +
 tools/gpiotools.c       | 437 ++++++++++++++++++++++++++++++++++++++++++++++++
 tools/gpiotools.h       | 236 ++++++++++++++++++++++++++
 tools/meson.build       |  20 +++
 7 files changed, 725 insertions(+)

diff --git a/docs/Doxyfile b/docs/Doxyfile
index 8c5b5dff54721fe4ccc9f3049e6c2c3f484cb911..3ee98ab21c0412a6d62d50686981c82cb3c8f464 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -4,6 +4,7 @@
 PROJECT_NAME		= libgpiod
 OUTPUT_DIRECTORY	= doxygen-output
 INPUT			= ../include/gpiod.h \
+			  ../tools/gpiotools.h \
 			  ../bindings/cxx/gpiod.hpp \
 			  ../bindings/cxx/gpiodcxx/
 GENERATE_XML		= YES
diff --git a/docs/gpio_tools.rst b/docs/gpio_tools.rst
index e4bf5847315ce93859bd4179df306a43a0445fec..2876970e4028451b05f7b20a551fc97d0a1685c2 100644
--- a/docs/gpio_tools.rst
+++ b/docs/gpio_tools.rst
@@ -33,6 +33,22 @@ There are currently six command-line tools available:
   changes to watch for, how many events to process before exiting, or if the
   events should be reported to the console
 
+Shared library
+--------------
+
+A number of functions used to implement **gpio-tools** has been made available
+in a shared library separate from the low-level, core libgpiod API in the form
+of libgpiotools.
+
+.. note::
+   The libgpiotools library does not get nearly as much attention and care as
+   the core libgpiod C library so your mileage may vary.
+
+.. toctree::
+   :maxdepth: 1
+
+   libgpiotools API documentation<gpio_tools_lib>
+
 .. toctree::
    :maxdepth: 1
    :caption: Manual entries
diff --git a/docs/gpio_tools_lib.rst b/docs/gpio_tools_lib.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2931f950d4b60c4e445e6d8c9d29dad1a4975214
--- /dev/null
+++ b/docs/gpio_tools_lib.rst
@@ -0,0 +1,12 @@
+..
+   SPDX-License-Identifier: CC-BY-SA-4.0
+   SPDX-FileCopyrightText: 2026 Qualcomm Technologies, Inc. and/or its subsidiaries
+
+..
+   This file is part of libgpiod.
+
+libgpiotools API
+================
+
+.. doxygengroup:: gpiotools
+   :members:
diff --git a/meson.build b/meson.build
index 4ec1e575820ff64245b52823312b57e8e09127d9..0bf970eb155178394410c4fbd680eb7544834637 100644
--- a/meson.build
+++ b/meson.build
@@ -33,6 +33,9 @@ libgpiosim_version      = '1.0.1'
 # ... and another one for GLib bindings:
 libgpiod_glib_soversion = 1
 libgpiod_glib_version   = '1.0.0'
+# ... and another for the libgpiotools shared library:
+libgpiotools_soversion  = 1
+libgpiotools_version    = '1.0.0'
 
 add_project_arguments(
   '-D_GNU_SOURCE',
diff --git a/tools/gpiotools.c b/tools/gpiotools.c
new file mode 100644
index 0000000000000000000000000000000000000000..dda36da888b94098231e977380aa38af2aa5069c
--- /dev/null
+++ b/tools/gpiotools.c
@@ -0,0 +1,437 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com>
+// SPDX-FileCopyrightText: 2022 Kent Gibson <warthog618@gmail.com>
+// SPDX-FileCopyrightText: 2026 Qualcomm Technologies, Inc. and/or its subsidiaries
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <gpiod.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "gpiotools.h"
+
+#define GT_API __attribute__((visibility("default")))
+
+static bool isuint(const char *str)
+{
+	for (; *str && isdigit(*str); str++)
+		;
+
+	return *str == '\0';
+}
+
+static int parse_uint(const char *option)
+{
+	unsigned long o;
+	char *end;
+
+	o = strtoul(option, &end, 10);
+	if (*end == '\0' && o <= INT_MAX)
+		return o;
+
+	return -1;
+}
+
+static int chip_dir_filter(const struct dirent *entry)
+{
+	struct stat sb;
+	char *path;
+	int ret;
+
+	ret = asprintf(&path, "/dev/%s", entry->d_name);
+	if (ret < 0)
+		return 0;
+
+	ret = 0;
+	if (lstat(path, &sb) == 0 && !S_ISLNK(sb.st_mode) &&
+	    gpiod_is_gpiochip_device(path))
+		ret = 1;
+
+	free(path);
+
+	return ret;
+}
+
+GT_API char *gpiotools_chip_path_lookup(const char *id)
+{
+	char *path;
+	int ret;
+
+	if (isuint(id))
+		ret = asprintf(&path, "/dev/gpiochip%s", id);
+	else if (strchr(id, '/'))
+		ret = asprintf(&path, "%s", id);
+	else
+		ret = asprintf(&path, "/dev/%s", id);
+
+	if (ret < 0)
+		return NULL;
+
+	if (!gpiod_is_gpiochip_device(path)) {
+		free(path);
+		return NULL;
+	}
+
+	return path;
+}
+
+GT_API int gpiotools_all_chip_paths(char ***paths_ptr)
+{
+	int i, j, num_chips, ret;
+	struct dirent **entries;
+	char **paths;
+
+	num_chips = scandir("/dev/", &entries, chip_dir_filter, versionsort);
+	if (num_chips < 0)
+		return -1;
+
+	paths = calloc(num_chips, sizeof(*paths));
+	if (!paths) {
+		for (i = 0; i < num_chips; i++)
+			free(entries[i]);
+		free(entries);
+		return -1;
+	}
+
+	for (i = 0; i < num_chips; i++) {
+		ret = asprintf(&paths[i], "/dev/%s", entries[i]->d_name);
+		if (ret < 0) {
+			for (j = 0; j < i; j++)
+				free(paths[j]);
+			free(paths);
+			for (j = 0; j < num_chips; j++)
+				free(entries[j]);
+			free(entries);
+			return -1;
+		}
+	}
+
+	*paths_ptr = paths;
+
+	for (i = 0; i < num_chips; i++)
+		free(entries[i]);
+	free(entries);
+
+	return num_chips;
+}
+
+GT_API int gpiotools_chip_paths(const char *id, char ***paths_ptr)
+{
+	char **paths, *path;
+
+	if (!id)
+		return gpiotools_all_chip_paths(paths_ptr);
+
+	path = gpiotools_chip_path_lookup(id);
+	if (!path)
+		return 0;
+
+	paths = malloc(sizeof(*paths));
+	if (!paths) {
+		free(path);
+		return -1;
+	}
+
+	paths[0] = path;
+	*paths_ptr = paths;
+
+	return 1;
+}
+
+GT_API struct gpiotools_line_resolver *
+gpiotools_resolver_init(int num_lines, char **lines, int num_chips,
+			bool strict, bool by_name)
+{
+	struct gpiotools_line_resolver *resolver;
+	struct gpiotools_resolved_line *line;
+	size_t resolver_size;
+	int i;
+
+	resolver_size = sizeof(*resolver) + num_lines * sizeof(*line);
+	resolver = malloc(resolver_size);
+	if (!resolver)
+		return NULL;
+
+	memset(resolver, 0, resolver_size);
+
+	resolver->chips = calloc(num_chips,
+				 sizeof(struct gpiotools_resolved_chip));
+	if (!resolver->chips) {
+		free(resolver);
+		return NULL;
+	}
+
+	resolver->num_lines = num_lines;
+	resolver->strict = strict;
+
+	for (i = 0; i < num_lines; i++) {
+		line = &resolver->lines[i];
+		line->id = lines[i];
+		line->id_as_offset = by_name ? -1 : parse_uint(lines[i]);
+		line->chip_num = -1;
+	}
+
+	return resolver;
+}
+
+GT_API bool
+gpiotools_resolve_lines_by_offset(struct gpiotools_line_resolver *resolver,
+				  unsigned int num_lines)
+{
+	struct gpiotools_resolved_line *line;
+	bool used = false;
+	int i;
+
+	for (i = 0; i < resolver->num_lines; i++) {
+		line = &resolver->lines[i];
+
+		if ((line->id_as_offset != -1) &&
+		    (line->id_as_offset < (int)num_lines)) {
+			line->chip_num = 0;
+			line->offset = line->id_as_offset;
+			line->resolved = true;
+			used = true;
+		}
+	}
+
+	return used;
+}
+
+GT_API bool gpiotools_resolve_done(struct gpiotools_line_resolver *resolver)
+{
+	return (!resolver->strict &&
+		resolver->num_found >= resolver->num_lines);
+}
+
+/*
+ * Try to match a single line_info against all requested lines in the resolver.
+ * Returns true if the info was claimed by at least one line, false otherwise.
+ * On strict-mode uniqueness violation sets errno to EEXIST and returns false.
+ */
+static bool resolve_line(struct gpiotools_line_resolver *resolver,
+			 struct gpiod_line_info *info, int chip_num)
+{
+	struct gpiotools_resolved_line *line;
+	bool resolved = false;
+	unsigned int offset;
+	const char *name;
+	int i;
+
+	offset = gpiod_line_info_get_offset(info);
+	for (i = 0; i < resolver->num_lines; i++) {
+		line = &resolver->lines[i];
+
+		/* already resolved by offset? */
+		if (line->resolved && (line->offset == offset) &&
+		    (line->chip_num == chip_num)) {
+			line->info = info;
+			resolver->num_found++;
+			resolved = true;
+		}
+
+		if (line->resolved && !resolver->strict)
+			continue;
+
+		/* else resolve by name */
+		name = gpiod_line_info_get_name(info);
+		if (name && strcmp(line->id, name) == 0) {
+			if (resolver->strict && line->resolved) {
+				line->not_unique = true;
+				return false;
+			}
+
+			line->offset = offset;
+			line->info = info;
+			line->chip_num = resolver->num_chips;
+			line->resolved = true;
+			resolver->num_found++;
+			resolved = true;
+		}
+	}
+
+	return resolved;
+}
+
+GT_API struct gpiotools_line_resolver *
+gpiotools_resolve_lines(int num_lines, char **lines, const char *chip_id,
+			bool strict, bool by_name)
+{
+	struct gpiotools_line_resolver *resolver;
+	struct gpiod_chip_info *chip_info;
+	struct gpiod_line_info *line_info;
+	int num_chips, i, offset;
+	struct gpiod_chip *chip;
+	bool chip_used;
+	char **paths;
+
+	if (!chip_id)
+		by_name = true;
+
+	num_chips = gpiotools_chip_paths(chip_id, &paths);
+	if (num_chips < 0)
+		return NULL;
+	if (chip_id && (num_chips == 0)) {
+		errno = ENODEV;
+		return NULL;
+	}
+
+	resolver = gpiotools_resolver_init(num_lines, lines, num_chips,
+					   strict, by_name);
+	if (!resolver)
+		goto err_free_paths;
+
+	for (i = 0; (i < num_chips) && !gpiotools_resolve_done(resolver); i++) {
+		chip_used = false;
+		chip = gpiod_chip_open(paths[i]);
+		if (!chip) {
+			if (errno == EACCES && !chip_id) {
+				free(paths[i]);
+				paths[i] = NULL;
+				continue;
+			}
+
+			goto err_free_resolver;
+		}
+
+		chip_info = gpiod_chip_get_info(chip);
+		if (!chip_info) {
+			gpiod_chip_close(chip);
+			goto err_free_resolver;
+		}
+
+		num_lines = gpiod_chip_info_get_num_lines(chip_info);
+
+		if (i == 0 && chip_id && !by_name)
+			chip_used = gpiotools_resolve_lines_by_offset(resolver,
+								      num_lines);
+
+		for (offset = 0;
+		     (offset < num_lines) && !gpiotools_resolve_done(resolver);
+		     offset++) {
+			line_info = gpiod_chip_get_line_info(chip, offset);
+			if (!line_info) {
+				gpiod_chip_info_free(chip_info);
+				gpiod_chip_close(chip);
+				goto err_free_resolver;
+			}
+
+			if (resolve_line(resolver, line_info, i)) {
+				chip_used = true;
+			} else {
+				gpiod_line_info_free(line_info);
+			}
+		}
+
+		gpiod_chip_close(chip);
+
+		if (chip_used) {
+			resolver->chips[resolver->num_chips].info = chip_info;
+			resolver->chips[resolver->num_chips].path = paths[i];
+			paths[i] = NULL;
+			resolver->num_chips++;
+		} else {
+			gpiod_chip_info_free(chip_info);
+			free(paths[i]);
+			paths[i] = NULL;
+		}
+	}
+
+	free(paths);
+
+	return resolver;
+
+err_free_resolver:
+	gpiotools_free_line_resolver(resolver);
+err_free_paths:
+	for (i = 0; i < num_chips; i++)
+		free(paths[i]);
+	free(paths);
+
+	return NULL;
+}
+
+GT_API void gpiotools_free_line_resolver(struct gpiotools_line_resolver *resolver)
+{
+	int i;
+
+	if (!resolver)
+		return;
+
+	for (i = 0; i < resolver->num_lines; i++)
+		gpiod_line_info_free(resolver->lines[i].info);
+
+	for (i = 0; i < resolver->num_chips; i++) {
+		gpiod_chip_info_free(resolver->chips[i].info);
+		free(resolver->chips[i].path);
+	}
+
+	free(resolver->chips);
+	free(resolver);
+}
+
+GT_API int
+gpiotools_get_line_offsets_and_values(struct gpiotools_line_resolver *resolver,
+				      int chip_num, unsigned int *offsets,
+				      enum gpiod_line_value *values)
+{
+	struct gpiotools_resolved_line *line;
+	int i, num_lines = 0;
+
+	for (i = 0; i < resolver->num_lines; i++) {
+		line = &resolver->lines[i];
+
+		if (line->chip_num == chip_num) {
+			offsets[num_lines] = line->offset;
+			if (values)
+				values[num_lines] = line->value;
+
+			num_lines++;
+		}
+	}
+
+	return num_lines;
+}
+
+GT_API const char *
+gpiotools_get_chip_name(struct gpiotools_line_resolver *resolver,
+			int chip_num)
+{
+	return gpiod_chip_info_get_name(resolver->chips[chip_num].info);
+}
+
+GT_API const char *
+gpiotools_get_line_name(struct gpiotools_line_resolver *resolver,
+			int chip_num, unsigned int offset)
+{
+	struct gpiotools_resolved_line *line;
+	int i;
+
+	for (i = 0; i < resolver->num_lines; i++) {
+		line = &resolver->lines[i];
+
+		if (line->info && line->offset == offset &&
+		    line->chip_num == chip_num)
+			return gpiod_line_info_get_name(resolver->lines[i].info);
+	}
+
+	return NULL;
+}
+
+GT_API void gpiotools_set_line_values(struct gpiotools_line_resolver *resolver,
+				      int chip_num,
+				      enum gpiod_line_value *values)
+{
+	int i, j;
+
+	for (i = 0, j = 0; i < resolver->num_lines; i++) {
+		if (resolver->lines[i].chip_num == chip_num) {
+			resolver->lines[i].value = values[j];
+			j++;
+		}
+	}
+}
diff --git a/tools/gpiotools.h b/tools/gpiotools.h
new file mode 100644
index 0000000000000000000000000000000000000000..f1c35c436fe5acf837e4b0487cea16925112c2e1
--- /dev/null
+++ b/tools/gpiotools.h
@@ -0,0 +1,236 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com> */
+/* SPDX-FileCopyrightText: 2022 Kent Gibson <warthog618@gmail.com> */
+/* SPDX-FileCopyrightText: 2026 Qualcomm Technologies, Inc. and/or its subsidiaries */
+
+/**
+ * @file gpiotools.h
+ */
+
+#ifndef __GPIOTOOLS_H__
+#define __GPIOTOOLS_H__
+
+#include <gpiod.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/**
+ * @defgroup gpiotools GPIO tools helpers
+ * @{
+ *
+ * Reusable chip and line resolution helpers used by the suite of GPIO
+ * command-line tools. These functions allow users to build their own programs
+ * on top of libgpiod using the same high-level abstractions as the gpio-tools.
+ *
+ * Unlike libgpiod core C API, public structures exposed by libgpiotools are
+ * not opaque by design. The goal is to allow easy access to members without
+ * providing a large number of very similar accessors. We leave some padding
+ * for potential future extensions.
+ *
+ * Error handling: functions typically return negative integer values or NULL
+ * pointers on failure and set errno to indicate the cause unless otherwise
+ * noted.
+ */
+
+/**
+ * @brief Descriptor for a single resolved GPIO line.
+ */
+struct gpiotools_resolved_line {
+	/** Identifier string from the command line. */
+	const char *id;
+	/**
+	 * ID parsed as an integer offset, or -1 if the line must be resolved
+	 * by name.
+	 */
+	int id_as_offset;
+	/** True once the line has been located on a chip. */
+	bool resolved;
+	/** True if strict mode found the name on more than one chip. */
+	bool not_unique;
+	/** Line info object; only valid once resolved. */
+	struct gpiod_line_info *info;
+	/** Index of the owning chip in the resolver's chips array. */
+	int chip_num;
+	/** Offset of the line on its chip. */
+	unsigned int offset;
+	/** Line value used by gpioget/gpioset. */
+	int value;
+	/** @cond INTERNAL */
+	/* Reserved for future extensions. */
+	uint32_t padding[5];
+	/** @endcond */
+};
+
+/**
+ * @brief Descriptor for a single GPIO chip referenced by a resolver.
+ */
+struct gpiotools_resolved_chip {
+	/** Chip info object. */
+	struct gpiod_chip_info *info;
+	/** Path to the chip device file. */
+	char *path;
+	/** @cond INTERNAL */
+	/* Reserved for future extensions. */
+	uint32_t padding[5];
+	/** @endcond */
+};
+
+/**
+ * @brief Resolver mapping requested line names or offsets to GPIO lines.
+ */
+struct gpiotools_line_resolver {
+	/** Number of chips the lines span; also the number of entries in chips. */
+	int num_chips;
+	/** Number of entries in lines. */
+	int num_lines;
+	/** Number of lines that have been found so far. */
+	int num_found;
+	/** If true, perform an exhaustive search to verify line name uniqueness. */
+	bool strict;
+	/** Array of chip descriptors for the chips that own the requested lines. */
+	struct gpiotools_resolved_chip *chips;
+	/** @cond INTERNAL */
+	/* Reserved for future extensions. */
+	uint32_t padding[10];
+	/** @endcond */
+	/** Flexible array of line descriptors for the requested lines. */
+	struct gpiotools_resolved_line lines[];
+};
+
+/**
+ * @brief Look up the path to a GPIO chip device.
+ * @param id Chip identifier: a number ("0"), a name ("gpiochip0"), or a path
+ *           ("/dev/gpiochip0").
+ * @return Pointer to a newly allocated string containing the filesystem path
+ *         to the GPIO chip or NULL on failure. The user is responsible for
+ *         calling free() on the pointer returned on success.
+ */
+char *gpiotools_chip_path_lookup(const char *id);
+
+/**
+ * @brief Get the paths of GPIO chip devices matching an identifier.
+ * @param id Chip identifier, or NULL to return all chips.
+ * @param paths_ptr On success, set to a newly allocated array of path strings.
+ *                  The caller must free() each element and the array itself.
+ * @return Number of chips found (0 if none), or negative errno on error.
+ */
+int gpiotools_chip_paths(const char *id, char ***paths_ptr);
+
+/**
+ * @brief Get the paths of all GPIO chip devices on the system.
+ * @param paths_ptr On success, set to a newly allocated array of path strings.
+ *                  The caller must free() each element and the array itself.
+ * @return Number of chips found (0 if none), or negative errno on error.
+ */
+int gpiotools_all_chip_paths(char ***paths_ptr);
+
+/**
+ * @brief Allocate and initialise a line resolver.
+ * @param num_lines Number of lines to resolve.
+ * @param lines Array of line identifiers (names or offset strings).
+ * @param num_chips Number of chips to allocate space for.
+ * @param strict If true, perform an exhaustive search to verify that line
+ *               names are unique.
+ * @param by_name If true, treat all identifiers as names; if false, try to
+ *                parse them as numeric offsets first.
+ * @return Pointer to the new resolver, or NULL on failure.
+ */
+struct gpiotools_line_resolver *
+gpiotools_resolver_init(int num_lines, char **lines, int num_chips,
+			bool strict, bool by_name);
+
+/**
+ * @brief Resolve lines by numeric offset.
+ * @param resolver Resolver to update.
+ * @param num_lines Number of lines on the chip.
+ *
+ * Marks lines whose identifier was successfully parsed as a numeric offset as
+ * resolved. Only applies to the first chip (chip_num == 0).
+ *
+ * @return True if any line was resolved, false otherwise.
+ */
+bool gpiotools_resolve_lines_by_offset(struct gpiotools_line_resolver *resolver,
+				       unsigned int num_lines);
+
+/**
+ * @brief Check whether line resolution is complete.
+ * @param resolver Resolver to check.
+ *
+ * In non-strict mode, resolution is considered done when all requested lines
+ * have been found. In strict mode this always returns false so that the
+ * caller performs an exhaustive search.
+ *
+ * @return True if resolution is complete, false otherwise.
+ */
+bool gpiotools_resolve_done(struct gpiotools_line_resolver *resolver);
+
+/**
+ * @brief Resolve line names or offsets to GPIO lines on the system.
+ * @param num_lines Number of lines to resolve.
+ * @param lines Array of line identifiers (names or offset strings).
+ * @param chip_id Chip identifier to restrict the search, or NULL to search
+ *                all chips.
+ * @param strict If true, verify that line names are unique across all chips.
+ * @param by_name If true, treat all identifiers as names.
+ * @return Pointer to the populated resolver, or NULL on error with errno set.
+ */
+struct gpiotools_line_resolver *
+gpiotools_resolve_lines(int num_lines, char **lines, const char *chip_id,
+			bool strict, bool by_name);
+
+/**
+ * @brief Free a line resolver and all memory it owns.
+ * @param resolver Resolver to free.  May be NULL.
+ */
+void gpiotools_free_line_resolver(struct gpiotools_line_resolver *resolver);
+
+/**
+ * @brief Extract offsets and optionally values for lines on a specific chip.
+ * @param resolver Resolver containing the resolved lines.
+ * @param chip_num Index of the chip to query.
+ * @param offsets Pre-allocated array to receive the line offsets.
+ * @param values Pre-allocated array to receive the line values, or NULL.
+ * @return Number of lines belonging to the specified chip.
+ * @note offsets and values must be large enough to hold the number of lines
+ *       stored in the resolver
+ */
+int
+gpiotools_get_line_offsets_and_values(struct gpiotools_line_resolver *resolver,
+				      int chip_num, unsigned int *offsets,
+				      enum gpiod_line_value *values);
+
+/**
+ * @brief Get the name of a chip referenced by the resolver.
+ * @param resolver Resolver containing the chip.
+ * @param chip_num Index of the chip.
+ * @return Pointer to the chip name string owned by the resolver.
+ */
+const char *gpiotools_get_chip_name(struct gpiotools_line_resolver *resolver,
+				    int chip_num);
+
+/**
+ * @brief Get the name of a resolved line.
+ * @param resolver Resolver containing the line.
+ * @param chip_num Index of the chip the line belongs to.
+ * @param offset Offset of the line on the chip.
+ * @return Pointer to the line name string owned by the resolver, or NULL if
+ *         the line has no name.
+ */
+const char *gpiotools_get_line_name(struct gpiotools_line_resolver *resolver,
+				    int chip_num, unsigned int offset);
+
+/**
+ * @brief Update line values stored in the resolver for a specific chip.
+ * @param resolver Resolver to update.
+ * @param chip_num Index of the chip whose lines should be updated.
+ * @param values Array of values to store, one per line on the chip in the
+ *               order they appear in the resolver.
+ */
+void gpiotools_set_line_values(struct gpiotools_line_resolver *resolver,
+			       int chip_num, enum gpiod_line_value *values);
+
+/**
+ * @}
+ */
+
+#endif /* __GPIOTOOLS_H__ */
diff --git a/tools/meson.build b/tools/meson.build
index b26718ba69679d03d210562768f71ea02bc3ed01..d755167cfbd7c2a7400d28cf5e5d5f9eece699d7 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -1,6 +1,26 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 # SPDX-FileCopyrightText: 2026 Qualcomm Technologies, Inc. and/or its subsidiaries
 
+libgpiotools_lib = library('gpiotools',
+  sources: ['gpiotools.c'],
+  include_directories: libgpiod_inc,
+  c_args: ['-fvisibility=hidden'],
+  dependencies: [libgpiod_dep],
+  version: libgpiotools_version,
+  soversion: libgpiotools_soversion,
+  install: true,
+)
+
+install_headers('gpiotools.h')
+
+pkgconfig = import('pkgconfig')
+pkgconfig.generate(libgpiotools_lib,
+  name: 'libgpiotools',
+  description: 'Collection of high-level functions for building programs using libgpiod',
+  url: project_url,
+  version: meson.project_version(),
+)
+
 tools_common_lib = static_library('tools-common',
   sources: ['tools-common.c'],
   include_directories: libgpiod_inc,

-- 
2.47.3


  reply	other threads:[~2026-05-13 11:47 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-13 11:47 [PATCH 0/2] tools: provide a shared library with high-level functions used by gpio-tools Bartosz Golaszewski
2026-05-13 11:47 ` Bartosz Golaszewski [this message]
2026-05-13 11:47 ` [PATCH 2/2] tools: reuse libgpiotools Bartosz Golaszewski
2026-05-13 15:18 ` [PATCH 0/2] tools: provide a shared library with high-level functions used by gpio-tools Kent Gibson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260513-tools-common-shared-lib-v1-1-58ac28a9f1d5@oss.qualcomm.com \
    --to=bartosz.golaszewski@oss.qualcomm.com \
    --cc=brgl@kernel.org \
    --cc=linus.walleij@linaro.org \
    --cc=linux-gpio@vger.kernel.org \
    --cc=post@lespocky.de \
    --cc=vfazio@xes-inc.com \
    --cc=warthog618@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox