1
0
mirror of https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git synced 2026-10-24 00:48:53 +03:00
Files
libgpiod/tools/gpioinfo.c
Bartosz Golaszewski c152accf91 tools: gpioinfo: don't implicity unquote unnamed lines
Consider the following output of `gpioinfo`:

gpiochip1 - 2 lines:
        line   0:       "foo"                   output
        line   1:       unnamed                 output

Now let's run:

    gpioset --chip=gpiochip1 0=active 1=active

The output of `gpioinfo --unquoted` is correct:

gpiochip1 - 2 lines:
        line   0:       foo                     output consumer=gpioset
        line   1:       unnamed                 output consumer=gpioset

However, without the `unquoted` switch, it's inconsistent:

gpiochip1 - 2 lines:
        line   0:       "foo"                   output consumer="gpioset"
        line   1:       unnamed                 output consumer=gpioset

This is because gpioinfo drops the quotes for all subsequent attribute
strings when it encounters an unnamed line. We should instead keep a
separate instance of the `unquoted` flag just for the line name and pass
the original value of `unquoted_strings` down to
print_line_attributes().

Fixes: 8ffb648928 ("tools: line name focussed rework")
Reviewed-by: Kent Gibson <warthog618@gmail.com>
Link: https://lore.kernel.org/r/20250321-gpioinfo-unnamed-quotes-v1-1-a806e9027adf@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
2025-03-24 10:39:14 +01:00

273 lines
6.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com>
// SPDX-FileCopyrightText: 2022 Kent Gibson <warthog618@gmail.com>
#include <getopt.h>
#include <gpiod.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tools-common.h"
struct config {
bool by_name;
bool strict;
bool unquoted_strings;
const char *chip_id;
};
static void print_help(void)
{
printf("Usage: %s [OPTIONS] [line]...\n", get_prog_name());
printf("\n");
printf("Print information about GPIO lines.\n");
printf("\n");
printf("Lines are specified by name, or optionally by offset if the chip option\n");
printf("is provided.\n");
printf("\n");
printf("If no lines are specified then all lines are displayed.\n");
printf("\n");
printf("Options:\n");
printf(" --by-name\t\ttreat lines as names even if they would parse as an offset\n");
printf(" -c, --chip <chip>\trestrict scope to a particular chip\n");
printf(" -h, --help\t\tdisplay this help and exit\n");
printf(" -s, --strict\t\tcheck all lines - don't assume line names are unique\n");
printf(" --unquoted\tdon't quote line or consumer names\n");
printf(" -v, --version\t\toutput version information and exit\n");
print_chip_help();
}
static int parse_config(int argc, char **argv, struct config *cfg)
{
static const struct option longopts[] = {
{ "by-name", no_argument, NULL, 'B' },
{ "chip", required_argument, NULL, 'c' },
{ "help", no_argument, NULL, 'h' },
{ "strict", no_argument, NULL, 's' },
{ "unquoted", no_argument, NULL, 'Q' },
{ "version", no_argument, NULL, 'v' },
{ GETOPT_NULL_LONGOPT },
};
static const char *const shortopts = "+c:hsv";
int opti, optc;
memset(cfg, 0, sizeof(*cfg));
for (;;) {
optc = getopt_long(argc, argv, shortopts, longopts, &opti);
if (optc < 0)
break;
switch (optc) {
case 'B':
cfg->by_name = true;
break;
case 'c':
cfg->chip_id = optarg;
break;
case 's':
cfg->strict = true;
break;
case 'h':
print_help();
exit(EXIT_SUCCESS);
case 'Q':
cfg->unquoted_strings = true;
break;
case 'v':
print_version();
exit(EXIT_SUCCESS);
case '?':
die("try %s --help", get_prog_name());
case 0:
break;
default:
abort();
}
}
return optind;
}
/*
* Minimal version similar to tools-common that indicates if a line should be
* printed rather than storing details into the resolver.
* Does not die on non-unique lines.
*/
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)) {
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)) {
line->resolved = true;
line->offset = offset;
line->chip_num = chip_num;
resolved = true;
}
}
return resolved;
}
static void print_line_info(struct gpiod_line_info *info, bool unquoted_strings)
{
bool unquoted_name = unquoted_strings;
char quoted_name[17];
const char *name;
int len;
name = gpiod_line_info_get_name(info);
if (!name) {
name = "unnamed";
unquoted_name = true;
}
if (unquoted_name) {
printf("%-16s\t", name);
} else {
len = strlen(name);
if (len <= 14) {
quoted_name[0] = '"';
memcpy(&quoted_name[1], name, len);
quoted_name[len + 1] = '"';
quoted_name[len + 2] = '\0';
printf("%-16s\t", quoted_name);
} else {
printf("\"%s\"\t", name);
}
}
print_line_attributes(info, unquoted_strings);
}
/*
* based on resolve_lines, but prints lines immediately rather than collecting
* details in the resolver.
*/
static void list_lines(struct line_resolver *resolver, struct gpiod_chip *chip,
int chip_num, struct config *cfg)
{
struct gpiod_chip_info *chip_info;
struct gpiod_line_info *info;
int offset, num_lines;
chip_info = gpiod_chip_get_info(chip);
if (!chip_info)
die_perror("unable to read info from chip %s",
gpiod_chip_get_path(chip));
num_lines = gpiod_chip_info_get_num_lines(chip_info);
if ((chip_num == 0) && (cfg->chip_id && !cfg->by_name))
resolve_lines_by_offset(resolver, num_lines);
for (offset = 0; ((offset < num_lines) &&
!(resolver->num_lines && resolve_done(resolver)));
offset++) {
info = gpiod_chip_get_line_info(chip, offset);
if (!info)
die_perror("unable to read info for line %d from %s",
offset, gpiod_chip_info_get_name(chip_info));
if (resolver->num_lines &&
!resolve_line(resolver, info, chip_num)) {
gpiod_line_info_free(info);
continue;
}
if (resolver->num_lines) {
printf("%s %u", gpiod_chip_info_get_name(chip_info),
offset);
} else {
if (offset == 0)
printf("%s - %u lines:\n",
gpiod_chip_info_get_name(chip_info),
num_lines);
printf("\tline %3u:", offset);
}
fputc('\t', stdout);
print_line_info(info, cfg->unquoted_strings);
fputc('\n', stdout);
gpiod_line_info_free(info);
resolver->num_found++;
}
gpiod_chip_info_free(chip_info);
}
int main(int argc, char **argv)
{
struct line_resolver *resolver = NULL;
int num_chips, i, ret = EXIT_SUCCESS;
struct gpiod_chip *chip;
struct config cfg;
char **paths;
set_prog_name(argv[0]);
i = parse_config(argc, argv, &cfg);
argc -= i;
argv += i;
if (!cfg.chip_id)
cfg.by_name = true;
num_chips = chip_paths(cfg.chip_id, &paths);
if (cfg.chip_id && (num_chips == 0))
die("cannot find GPIO chip character device '%s'", cfg.chip_id);
resolver = resolver_init(argc, argv, num_chips, cfg.strict,
cfg.by_name);
for (i = 0; i < num_chips; i++) {
chip = gpiod_chip_open(paths[i]);
if (chip) {
list_lines(resolver, chip, i, &cfg);
gpiod_chip_close(chip);
} else {
print_perror("unable to open chip '%s'", paths[i]);
if (cfg.chip_id)
return EXIT_FAILURE;
ret = EXIT_FAILURE;
}
free(paths[i]);
}
free(paths);
validate_resolution(resolver, cfg.chip_id);
if (argc && resolver->num_found != argc)
ret = EXIT_FAILURE;
free(resolver);
return ret;
}