gpio: add support for node label based lookup in the shell

Use some new kernel features to make the experience of finding and
dealing with GPIO devices much more ergonomic by allowing the use of
devicetree node labels to identify GPIO devices by default.

Users who wish to avoid the associated footprint penalty can set
CONFIG_DEVICE_DT_METADATA=n by hand, but I think the convenience is
worth the price as a default. If we're running a shell, then we've
already paid a heavy footprint penalty.

Example output for qemu_cortex_m3:

    uart:~$ gpio devices
    Device           Other names
    gpio@40004000    gpio0
    gpio@40005000    gpio1
    gpio@40006000    gpio2
    gpio@40007000    gpio3
    gpio@40024000    gpio4
    gpio@40025000    gpio5
    gpio@40026000    gpio6

Signed-off-by: Martí Bolívar <mbolivar@amperecomputing.com>
This commit is contained in:
Martí Bolívar 2024-04-25 00:05:42 -06:00 committed by Alberto Escolar
parent 15c9d3711c
commit 48a14dc17e
2 changed files with 62 additions and 2 deletions

View file

@ -17,6 +17,7 @@ source "subsys/logging/Kconfig.template.log_config"
config GPIO_SHELL
bool "GPIO Shell"
depends on SHELL
imply DEVICE_DT_METADATA
help
Enable GPIO Shell for testing.

View file

@ -134,16 +134,43 @@ DT_FOREACH_STATUS_OKAY_NODE(IS_GPIO_CTRL_PIN_GET)
static const struct gpio_ctrl gpio_list[] = {DT_FOREACH_STATUS_OKAY_NODE(IS_GPIO_CTRL_LIST)};
static const struct gpio_ctrl *get_gpio_ctrl(char *name)
static const struct gpio_ctrl *get_gpio_ctrl_helper(const struct device *dev)
{
const struct device *dev = device_get_binding(name);
size_t i;
if (dev == NULL) {
return NULL;
}
for (i = 0; i < ARRAY_SIZE(gpio_list); i++) {
if (gpio_list[i].dev == dev) {
return &gpio_list[i];
}
}
return NULL;
}
/* Look up a device by some human-readable string identifier. We
* always search among device names. If the feature is available, we
* search by node label as well.
*/
static const struct gpio_ctrl *get_gpio_ctrl(char *id)
{
const struct gpio_ctrl *ctrl;
ctrl = get_gpio_ctrl_helper(device_get_binding(id));
if (ctrl != NULL) {
return ctrl;
}
#ifdef CONFIG_DEVICE_DT_METADATA
ctrl = get_gpio_ctrl_helper(device_get_by_dt_nodelabel(id));
if (ctrl != NULL) {
return ctrl;
}
#endif /* CONFIG_DEVICE_DT_METADATA */
return NULL;
}
@ -401,6 +428,35 @@ static int cmd_gpio_toggle(const struct shell *sh, size_t argc, char **argv)
return 0;
}
static int cmd_gpio_devices(const struct shell *sh, size_t argc, char **argv)
{
size_t i;
shell_fprintf(sh, SHELL_NORMAL, "%-16s Other names\n", "Device");
for (i = 0; i < ARRAY_SIZE(gpio_list); i++) {
const struct device *dev = gpio_list[i].dev;
shell_fprintf(sh, SHELL_NORMAL, "%-16s", dev->name);
#ifdef CONFIG_DEVICE_DT_METADATA
const struct device_dt_nodelabels *nl = device_get_dt_nodelabels(dev);
if (nl->num_nodelabels > 0) {
for (size_t j = 0; j < nl->num_nodelabels; j++) {
const char *nodelabel = nl->nodelabels[j];
shell_fprintf(sh, SHELL_NORMAL, " %s", nodelabel);
}
}
#endif
shell_fprintf(sh, SHELL_NORMAL, "\n");
}
return 0;
}
/* 500 msec = 1/2 sec */
#define SLEEP_TIME_MS 500
@ -618,6 +674,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_gpio,
SHELL_COND_CMD_ARG(CONFIG_GPIO_SHELL_TOGGLE_CMD, toggle, &sub_gpio_dev,
"Toggle GPIO pin\n"
"Usage: gpio toggle <device> <pin>", cmd_gpio_toggle, 3, 0),
SHELL_CMD(devices, NULL,
"List all GPIO devices\n"
"Usage: gpio devices", cmd_gpio_devices),
SHELL_COND_CMD_ARG(CONFIG_GPIO_SHELL_BLINK_CMD, blink, &sub_gpio_dev,
"Blink GPIO pin\n"
"Usage: gpio blink <device> <pin>", cmd_gpio_blink, 3, 0),