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 2/2] tools: reuse libgpiotools
Date: Wed, 13 May 2026 13:47:13 +0200 [thread overview]
Message-ID: <20260513-tools-common-shared-lib-v1-2-58ac28a9f1d5@oss.qualcomm.com> (raw)
In-Reply-To: <20260513-tools-common-shared-lib-v1-0-58ac28a9f1d5@oss.qualcomm.com>
Reuse as much of the functionality provided by the new libgpiotools
shared library in tools-common while retaining the functions used to
validate command-line arguments, print help text or bail-out on errors.
We keep the existing interfaces to gpio-tools and use them to wrap the
API exposed by libgpiotools as we want to keep bailing out on errors
without requiring tools to do a lot of repeated error checking.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
tools/meson.build | 8 +-
tools/tools-common.c | 316 ++++++---------------------------------------------
tools/tools-common.h | 63 +---------
3 files changed, 47 insertions(+), 340 deletions(-)
diff --git a/tools/meson.build b/tools/meson.build
index d755167cfbd7c2a7400d28cf5e5d5f9eece699d7..5ecf7dc5d221d52243478f9a126e6512d2408981 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -21,9 +21,15 @@ pkgconfig.generate(libgpiotools_lib,
version: meson.project_version(),
)
+libgpiotools_dep = declare_dependency(
+ link_with: libgpiotools_lib,
+ include_directories: include_directories('.'),
+)
+
tools_common_lib = static_library('tools-common',
sources: ['tools-common.c'],
include_directories: libgpiod_inc,
+ dependencies: [libgpiotools_dep],
pic: true,
)
@@ -33,7 +39,7 @@ tools_common_dep = declare_dependency(
)
tools_c_args = []
-tools_deps = [libgpiod_dep, tools_common_dep]
+tools_deps = [libgpiod_dep, libgpiotools_dep, tools_common_dep]
if opt_gpioset_interactive.allowed() and libedit_dep.found()
tools_c_args += ['-DGPIOSET_INTERACTIVE']
diff --git a/tools/tools-common.c b/tools/tools-common.c
index 5aa51197c521a373b421054459c8ef38f3a35a1e..56ed90f639790ec54a3d6e9eb6ad594104474d7e 100644
--- a/tools/tools-common.c
+++ b/tools/tools-common.c
@@ -4,8 +4,6 @@
/* Common code for GPIO tools. */
-#include <ctype.h>
-#include <dirent.h>
#include <errno.h>
#include <gpiod.h>
#include <inttypes.h>
@@ -14,7 +12,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/stat.h>
#include <time.h>
#include "tools-common.h"
@@ -431,154 +428,39 @@ void print_line_vals(struct line_resolver *resolver, bool is_unquoted,
printf("\n");
}
-static int chip_dir_filter(const struct dirent *entry)
-{
- struct stat sb;
- int ret = 0;
- char *path;
-
- if (asprintf(&path, "/dev/%s", entry->d_name) < 0)
- return 0;
-
- if ((lstat(path, &sb) == 0) && (!S_ISLNK(sb.st_mode)) &&
- gpiod_is_gpiochip_device(path))
- ret = 1;
-
- free(path);
-
- return ret;
-}
-
-static bool isuint(const char *str)
-{
- for (; *str && isdigit(*str); str++)
- ;
-
- return *str == '\0';
-}
-
bool chip_path_lookup(const char *id, char **path_ptr)
{
char *path;
- if (isuint(id)) {
- /* by number */
- if (asprintf(&path, "/dev/gpiochip%s", id) < 0)
- return false;
- } else if (strchr(id, '/')) {
- /* by path */
- if (asprintf(&path, "%s", id) < 0)
- return false;
- } else {
- /* by device name */
- if (asprintf(&path, "/dev/%s", id) < 0)
- return false;
- }
-
- if (!gpiod_is_gpiochip_device(path)) {
- free(path);
- return false;
- }
-
- *path_ptr = path;
+ path = gpiotools_chip_path_lookup(id);
+ if (path)
+ *path_ptr = path;
- return true;
+ return !!path;
}
int chip_paths(const char *id, char ***paths_ptr)
{
- char **paths;
- char *path;
+ int ret;
- if (id == NULL)
- return all_chip_paths(paths_ptr);
-
- if (!chip_path_lookup(id, &path))
- return 0;
-
- paths = malloc(sizeof(*paths));
- if (paths == NULL)
+ ret = gpiotools_chip_paths(id, paths_ptr);
+ if (ret < 0)
die_oom();
- paths[0] = path;
- *paths_ptr = paths;
-
- return 1;
+ return ret;
}
int all_chip_paths(char ***paths_ptr)
{
- int i, j, num_chips, ret = 0;
- struct dirent **entries;
- char **paths;
+ int ret;
- num_chips = scandir("/dev/", &entries, chip_dir_filter, versionsort);
- if (num_chips < 0)
+ ret = gpiotools_all_chip_paths(paths_ptr);
+ if (ret < 0)
die_perror("unable to scan /dev");
- paths = calloc(num_chips, sizeof(*paths));
- if (paths == NULL)
- die_oom();
-
- for (i = 0; i < num_chips; i++) {
- if (asprintf(&paths[i], "/dev/%s", entries[i]->d_name) < 0) {
- for (j = 0; j < i; j++)
- free(paths[j]);
-
- free(paths);
- return 0;
- }
- }
-
- *paths_ptr = paths;
- ret = num_chips;
-
- for (i = 0; i < num_chips; i++)
- free(entries[i]);
-
- free(entries);
return ret;
}
-static bool resolve_line(struct line_resolver *resolver,
- struct gpiod_line_info *info, int chip_num)
-{
- struct 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)
- die("line '%s' is not unique", line->id);
- line->offset = offset;
- line->info = info;
- line->chip_num = resolver->num_chips;
- line->resolved = true;
- resolver->num_found++;
- resolved = true;
- }
- }
-
- return resolved;
-}
-
/*
* check for lines that can be identified by offset
*
@@ -588,57 +470,23 @@ static bool resolve_line(struct line_resolver *resolver,
bool resolve_lines_by_offset(struct line_resolver *resolver,
unsigned int num_lines)
{
- struct 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;
+ return gpiotools_resolve_lines_by_offset(resolver, num_lines);
}
bool resolve_done(struct line_resolver *resolver)
{
- return (!resolver->strict &&
- resolver->num_found >= resolver->num_lines);
+ return gpiotools_resolve_done(resolver);
}
struct line_resolver *resolver_init(int num_lines, char **lines, int num_chips,
bool strict, bool by_name)
{
struct line_resolver *resolver;
- struct resolved_line *line;
- size_t resolver_size;
- int i;
-
- resolver_size = sizeof(*resolver) + num_lines * sizeof(*line);
- resolver = malloc(resolver_size);
- if (resolver == NULL)
- die_oom();
- memset(resolver, 0, resolver_size);
-
- resolver->chips = calloc(num_chips, sizeof(struct resolved_chip));
- if (resolver->chips == NULL)
+ resolver = gpiotools_resolver_init(num_lines, lines, num_chips,
+ strict, by_name);
+ if (!resolver)
die_oom();
- memset(resolver->chips, 0, num_chips * sizeof(struct resolved_chip));
-
- 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;
}
@@ -647,72 +495,23 @@ struct line_resolver *resolve_lines(int num_lines, char **lines,
const char *chip_id, bool strict,
bool by_name)
{
- struct gpiod_chip_info *chip_info;
- struct gpiod_line_info *line_info;
struct line_resolver *resolver;
- int num_chips, i, offset;
- struct gpiod_chip *chip;
- bool chip_used;
- char **paths;
-
- if (chip_id == NULL)
- by_name = true;
-
- num_chips = chip_paths(chip_id, &paths);
- if (chip_id && (num_chips == 0))
- die("cannot find GPIO chip character device '%s'", chip_id);
-
- resolver = resolver_init(num_lines, lines, num_chips, strict, by_name);
-
- for (i = 0; (i < num_chips) && !resolve_done(resolver); i++) {
- chip_used = false;
- chip = gpiod_chip_open(paths[i]);
- if (!chip) {
- if ((errno == EACCES) && (chip_id == NULL)) {
- free(paths[i]);
- continue;
- }
-
- die_perror("unable to open chip '%s'", paths[i]);
- }
-
- chip_info = gpiod_chip_get_info(chip);
- if (!chip_info)
- die_perror("unable to get info for '%s'", paths[i]);
-
- num_lines = gpiod_chip_info_get_num_lines(chip_info);
-
- if (i == 0 && chip_id && !by_name)
- chip_used = resolve_lines_by_offset(resolver, num_lines);
-
- for (offset = 0;
- (offset < num_lines) && !resolve_done(resolver);
- offset++) {
- line_info = gpiod_chip_get_line_info(chip, offset);
- if (!line_info)
- die_perror("unable to read the info for line %d from %s",
- offset,
- gpiod_chip_info_get_name(chip_info));
-
- if (resolve_line(resolver, line_info, i))
- chip_used = true;
- else
- gpiod_line_info_free(line_info);
-
- }
+ int i;
- gpiod_chip_close(chip);
+ resolver = gpiotools_resolve_lines(num_lines, lines, chip_id,
+ strict, by_name);
+ if (!resolver) {
+ if (errno == ENODEV)
+ die("cannot find GPIO chip character device '%s'",
+ chip_id);
+ else
+ die_perror("error resolving lines");
+ }
- if (chip_used) {
- resolver->chips[resolver->num_chips].info = chip_info;
- resolver->chips[resolver->num_chips].path = paths[i];
- resolver->num_chips++;
- } else {
- gpiod_chip_info_free(chip_info);
- free(paths[i]);
- }
+ for (i = 0; i < resolver->num_lines; i++) {
+ if (resolver->lines[i].not_unique)
+ die("line '%s' is not unique", resolver->lines[i].id);
}
- free(paths);
return resolver;
}
@@ -752,75 +551,30 @@ void validate_resolution(struct line_resolver *resolver, const char *chip_id)
void free_line_resolver(struct 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);
+ gpiotools_free_line_resolver(resolver);
}
int get_line_offsets_and_values(struct line_resolver *resolver, int chip_num,
unsigned int *offsets,
enum gpiod_line_value *values)
{
- struct 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;
+ return gpiotools_get_line_offsets_and_values(resolver, chip_num,
+ offsets, values);
}
const char *get_chip_name(struct line_resolver *resolver, int chip_num)
{
- return gpiod_chip_info_get_name(resolver->chips[chip_num].info);
+ return gpiotools_get_chip_name(resolver, chip_num);
}
const char *get_line_name(struct line_resolver *resolver, int chip_num,
unsigned int offset)
{
- struct 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 0;
+ return gpiotools_get_line_name(resolver, chip_num, offset);
}
void set_line_values(struct 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++;
- }
- }
+ gpiotools_set_line_values(resolver, chip_num, values);
}
diff --git a/tools/tools-common.h b/tools/tools-common.h
index 165c250f8d664687d06232426941020e0c8a4ae7..85ee765255bbba991cd2e1fbe6b727a9e998d47e 100644
--- a/tools/tools-common.h
+++ b/tools/tools-common.h
@@ -7,6 +7,8 @@
#include <gpiod.h>
+#include "gpiotools.h"
+
/*
* Various helpers for the GPIO tools.
*
@@ -19,64 +21,9 @@
#define GETOPT_NULL_LONGOPT NULL, 0, NULL, 0
-struct resolved_line {
- /* from the command line */
- const char *id;
-
- /*
- * id parsed as int, if that is an option, or -1 if line must be
- * resolved by name
- */
- int id_as_offset;
-
- /* line has been located on a chip */
- bool resolved;
-
- /* remaining fields only valid once resolved... */
-
- /* info for the line */
- struct gpiod_line_info *info;
-
- /* num of relevant chip in line_resolver */
- int chip_num;
-
- /* offset of line on chip */
- unsigned int offset;
-
- /* line value for gpioget/set */
- int value;
-};
-
-struct resolved_chip {
- /* info of the relevant chips */
- struct gpiod_chip_info *info;
-
- /* path to the chip */
- char *path;
-};
-
-/* a resolver from requested line names/offsets to lines on the system */
-struct line_resolver {
- /*
- * number of chips the lines span, and number of entries in chips
- */
- int num_chips;
-
- /* number of lines in lines */
- int num_lines;
-
- /* number of lines found */
- int num_found;
-
- /* perform exhaustive search to check line names are unique */
- bool strict;
-
- /* details of the relevant chips */
- struct resolved_chip *chips;
-
- /* descriptors for the requested lines */
- struct resolved_line lines[];
-};
+#define resolved_line gpiotools_resolved_line
+#define resolved_chip gpiotools_resolved_chip
+#define line_resolver gpiotools_line_resolver
void set_prog_name(const char *name);
const char *get_prog_name(void);
--
2.47.3
next prev parent 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 ` [PATCH 1/2] tools: provide the libgpiotools shared library Bartosz Golaszewski
2026-05-13 11:47 ` Bartosz Golaszewski [this message]
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-2-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