691 lines
18 KiB
C
691 lines
18 KiB
C
/*
|
|
* Copyright (c) 2018 Intel Corporation
|
|
* Copyright (c) 2021 Dennis Ruffer <daruffer@gmail.com>
|
|
* Copyright (c) 2023 Nick Ward <nix.ward@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/shell/shell.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#define ARGV_DEV 1
|
|
#define ARGV_PIN 2
|
|
#define ARGV_CONF 3
|
|
#define ARGV_VALUE 3
|
|
#define ARGV_VENDOR_SPECIFIC 4
|
|
|
|
#define NGPIOS_UNKNOWN -1
|
|
#define PIN_NOT_FOUND UINT8_MAX
|
|
|
|
/* Pin syntax maximum length */
|
|
#define PIN_SYNTAX_MAX 32
|
|
#define PIN_NUM_MAX 4
|
|
|
|
struct gpio_ctrl {
|
|
const struct device *dev;
|
|
int8_t ngpios;
|
|
gpio_port_pins_t reserved_mask;
|
|
const char **line_names;
|
|
uint8_t line_names_len;
|
|
const union shell_cmd_entry *subcmd;
|
|
};
|
|
|
|
struct sh_gpio {
|
|
const struct device *dev;
|
|
gpio_pin_t pin;
|
|
};
|
|
/*
|
|
* Find idx-th pin reference from the set of non reserved
|
|
* pin numbers and provided line names.
|
|
*/
|
|
static void port_pin_get(gpio_port_pins_t reserved_mask, const char **line_names,
|
|
uint8_t line_names_len, size_t idx, struct shell_static_entry *entry)
|
|
{
|
|
static char pin_syntax[PIN_SYNTAX_MAX];
|
|
static char pin_num[PIN_NUM_MAX];
|
|
const char *name;
|
|
gpio_pin_t pin;
|
|
bool reserved;
|
|
|
|
entry->handler = NULL;
|
|
|
|
/* Find allowed numeric pin reference */
|
|
for (pin = 0; pin < GPIO_MAX_PINS_PER_PORT; pin++) {
|
|
reserved = ((BIT64(pin) & reserved_mask) != 0);
|
|
if (!reserved) {
|
|
if (idx == 0) {
|
|
break;
|
|
}
|
|
idx--;
|
|
}
|
|
}
|
|
|
|
if (pin < GPIO_MAX_PINS_PER_PORT) {
|
|
sprintf(pin_num, "%u", pin);
|
|
if ((pin < line_names_len) && (strlen(line_names[pin]) > 0)) {
|
|
/* pin can be specified by line name */
|
|
name = line_names[pin];
|
|
for (int i = 0; i < (sizeof(pin_syntax) - 1); i++) {
|
|
/*
|
|
* For line-name tab completion to work replace any
|
|
* space characters with '_'.
|
|
*/
|
|
pin_syntax[i] = (name[i] != ' ') ? name[i] : '_';
|
|
if (name[i] == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
pin_syntax[sizeof(pin_syntax) - 1] = '\0';
|
|
entry->syntax = pin_syntax;
|
|
entry->help = pin_num;
|
|
} else {
|
|
/* fallback to pin specified by pin number */
|
|
entry->syntax = pin_num;
|
|
entry->help = NULL;
|
|
}
|
|
} else {
|
|
/* No more pins */
|
|
entry->syntax = NULL;
|
|
entry->help = NULL;
|
|
}
|
|
}
|
|
|
|
#define GPIO_DT_RESERVED_RANGES_NGPIOS_SHELL(node_id) \
|
|
COND_CODE_1(DT_NODE_HAS_PROP(node_id, ngpios), \
|
|
(GPIO_DT_RESERVED_RANGES_NGPIOS(node_id, DT_PROP(node_id, ngpios))), \
|
|
(GPIO_MAX_PINS_PER_PORT))
|
|
|
|
#define GPIO_CTRL_PIN_GET_FN(node_id) \
|
|
static const char *node_id##line_names[] = DT_PROP_OR(node_id, gpio_line_names, {NULL}); \
|
|
\
|
|
static void node_id##cmd_gpio_pin_get(size_t idx, struct shell_static_entry *entry); \
|
|
\
|
|
SHELL_DYNAMIC_CMD_CREATE(node_id##sub_gpio_pin, node_id##cmd_gpio_pin_get); \
|
|
\
|
|
static void node_id##cmd_gpio_pin_get(size_t idx, struct shell_static_entry *entry) \
|
|
{ \
|
|
gpio_port_pins_t reserved_mask = GPIO_DT_RESERVED_RANGES_NGPIOS_SHELL(node_id); \
|
|
uint8_t line_names_len = DT_PROP_LEN_OR(node_id, gpio_line_names, 0); \
|
|
\
|
|
port_pin_get(reserved_mask, node_id##line_names, line_names_len, idx, entry); \
|
|
entry->subcmd = NULL; \
|
|
}
|
|
|
|
#define IS_GPIO_CTRL_PIN_GET(node_id) \
|
|
COND_CODE_1(DT_PROP(node_id, gpio_controller), (GPIO_CTRL_PIN_GET_FN(node_id)), ())
|
|
|
|
DT_FOREACH_STATUS_OKAY_NODE(IS_GPIO_CTRL_PIN_GET)
|
|
|
|
#define GPIO_CTRL_LIST_ENTRY(node_id) \
|
|
{ \
|
|
.dev = DEVICE_DT_GET(node_id), \
|
|
.ngpios = DT_PROP_OR(node_id, ngpios, NGPIOS_UNKNOWN), \
|
|
.reserved_mask = GPIO_DT_RESERVED_RANGES_NGPIOS_SHELL(node_id), \
|
|
.line_names = node_id##line_names, \
|
|
.line_names_len = DT_PROP_LEN_OR(node_id, gpio_line_names, 0), \
|
|
.subcmd = &node_id##sub_gpio_pin, \
|
|
},
|
|
|
|
#define IS_GPIO_CTRL_LIST(node_id) \
|
|
COND_CODE_1(DT_PROP(node_id, gpio_controller), (GPIO_CTRL_LIST_ENTRY(node_id)), ())
|
|
|
|
static const struct gpio_ctrl gpio_list[] = {DT_FOREACH_STATUS_OKAY_NODE(IS_GPIO_CTRL_LIST)};
|
|
|
|
static const struct gpio_ctrl *get_gpio_ctrl_helper(const struct device *dev)
|
|
{
|
|
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;
|
|
}
|
|
|
|
int line_cmp(const char *input, const char *line_name)
|
|
{
|
|
int i = 0;
|
|
|
|
while (true) {
|
|
if ((input[i] == '_') && (line_name[i] == ' ')) {
|
|
/* Allow input underscore to match line_name space */
|
|
} else if (input[i] != line_name[i]) {
|
|
return (input[i] > line_name[i]) ? 1 : -1;
|
|
} else if (line_name[i] == '\0') {
|
|
return 0;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
static int get_gpio_pin(const struct shell *sh, const struct gpio_ctrl *ctrl, char *line_name)
|
|
{
|
|
gpio_pin_t pin = PIN_NOT_FOUND;
|
|
gpio_pin_t i;
|
|
int result;
|
|
|
|
for (i = 0; i < ctrl->ngpios; i++) {
|
|
result = line_cmp(line_name, ctrl->line_names[i]);
|
|
if (result == 0) {
|
|
if ((BIT64(i) & ctrl->reserved_mask) != 0) {
|
|
shell_error(sh, "Reserved pin");
|
|
return -EACCES;
|
|
} else if (pin == PIN_NOT_FOUND) {
|
|
pin = i;
|
|
} else {
|
|
shell_error(sh, "Line name ambiguous");
|
|
return -EFAULT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pin == PIN_NOT_FOUND) {
|
|
shell_error(sh, "Line name not found: '%s'", line_name);
|
|
return -ENOENT;
|
|
}
|
|
|
|
return pin;
|
|
}
|
|
|
|
static int get_sh_gpio(const struct shell *sh, char **argv, struct sh_gpio *gpio)
|
|
{
|
|
const struct gpio_ctrl *ctrl;
|
|
int ret = 0;
|
|
int pin;
|
|
|
|
ctrl = get_gpio_ctrl(argv[ARGV_DEV]);
|
|
if (ctrl == NULL) {
|
|
shell_error(sh, "unknown gpio controller: %s", argv[ARGV_DEV]);
|
|
return -EINVAL;
|
|
}
|
|
gpio->dev = ctrl->dev;
|
|
pin = shell_strtoul(argv[ARGV_PIN], 0, &ret);
|
|
if (ret != 0) {
|
|
pin = get_gpio_pin(sh, ctrl, argv[ARGV_PIN]);
|
|
if (pin < 0) {
|
|
return pin;
|
|
}
|
|
} else if ((BIT64(pin) & ctrl->reserved_mask) != 0) {
|
|
shell_error(sh, "Reserved pin");
|
|
return -EACCES;
|
|
}
|
|
gpio->pin = pin;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_gpio_conf(const struct shell *sh, size_t argc, char **argv, void *data)
|
|
{
|
|
gpio_flags_t flags = 0;
|
|
gpio_flags_t vendor_specific;
|
|
struct sh_gpio gpio;
|
|
int ret = 0;
|
|
|
|
ret = get_sh_gpio(sh, argv, &gpio);
|
|
if (ret != 0) {
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
for (int i = 0; i < strlen(argv[ARGV_CONF]); i++) {
|
|
switch (argv[ARGV_CONF][i]) {
|
|
case 'i':
|
|
flags |= GPIO_INPUT;
|
|
break;
|
|
case 'o':
|
|
flags |= GPIO_OUTPUT;
|
|
break;
|
|
case 'u':
|
|
flags |= GPIO_PULL_UP;
|
|
break;
|
|
case 'd':
|
|
flags |= GPIO_PULL_DOWN;
|
|
break;
|
|
case 'h':
|
|
flags |= GPIO_ACTIVE_HIGH;
|
|
break;
|
|
case 'l':
|
|
flags |= GPIO_ACTIVE_LOW;
|
|
break;
|
|
case '0':
|
|
flags |= GPIO_OUTPUT_INIT_LOGICAL | GPIO_OUTPUT_INIT_LOW;
|
|
break;
|
|
case '1':
|
|
flags |= GPIO_OUTPUT_INIT_LOGICAL | GPIO_OUTPUT_INIT_HIGH;
|
|
break;
|
|
default:
|
|
shell_error(sh, "Unknown: '%c'", argv[ARGV_CONF][i]);
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
}
|
|
|
|
if (((flags & GPIO_INPUT) != 0) == ((flags & GPIO_OUTPUT) != 0)) {
|
|
shell_error(sh, "must be either input or output");
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
if (((flags & GPIO_PULL_UP) != 0) && ((flags & GPIO_PULL_DOWN) != 0)) {
|
|
shell_error(sh, "cannot be pull up and pull down");
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
if (((flags & GPIO_ACTIVE_LOW) != 0) && ((flags & GPIO_ACTIVE_HIGH) != 0)) {
|
|
shell_error(sh, "cannot be active low and active high");
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
if ((flags & GPIO_OUTPUT) != 0) {
|
|
/* Default to active high if not specified */
|
|
if ((flags & (GPIO_ACTIVE_LOW | GPIO_ACTIVE_HIGH)) == 0) {
|
|
flags |= GPIO_ACTIVE_HIGH;
|
|
}
|
|
/* Default to initialisation to logic 0 if not specified */
|
|
if ((flags & GPIO_OUTPUT_INIT_LOGICAL) == 0) {
|
|
flags |= GPIO_OUTPUT_INIT_LOGICAL | GPIO_OUTPUT_INIT_LOW;
|
|
}
|
|
}
|
|
|
|
if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT_INIT_LOGICAL) != 0)) {
|
|
shell_error(sh, "an input cannot be initialised to a logic level");
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
if (((flags & GPIO_OUTPUT_INIT_LOW) != 0) && ((flags & GPIO_OUTPUT_INIT_HIGH) != 0)) {
|
|
shell_error(sh, "cannot initialise to logic 0 and logic 1");
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
if (argc == 5) {
|
|
vendor_specific = shell_strtoul(argv[ARGV_VENDOR_SPECIFIC], 0, &ret);
|
|
if ((ret == 0) && ((vendor_specific & ~(0xFF00U)) == 0)) {
|
|
flags |= vendor_specific;
|
|
} else {
|
|
/*
|
|
* See include/zephyr/dt-bindings/gpio/ for the
|
|
* available flags for your vendor.
|
|
*/
|
|
shell_error(sh, "vendor specific flags must be within "
|
|
"the mask 0xFF00");
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
}
|
|
|
|
ret = gpio_pin_configure(gpio.dev, gpio.pin, flags);
|
|
if (ret != 0) {
|
|
shell_error(sh, "error: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_gpio_get(const struct shell *sh, size_t argc, char **argv)
|
|
{
|
|
struct sh_gpio gpio;
|
|
int value;
|
|
int ret;
|
|
|
|
ret = get_sh_gpio(sh, argv, &gpio);
|
|
if (ret != 0) {
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
value = gpio_pin_get(gpio.dev, gpio.pin);
|
|
if (value >= 0) {
|
|
shell_print(sh, "%u", value);
|
|
} else {
|
|
shell_error(sh, "error: %d", value);
|
|
return value;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_gpio_set(const struct shell *sh, size_t argc, char **argv)
|
|
{
|
|
struct sh_gpio gpio;
|
|
unsigned long value;
|
|
int ret = 0;
|
|
|
|
ret = get_sh_gpio(sh, argv, &gpio);
|
|
if (ret != 0) {
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
value = shell_strtoul(argv[ARGV_VALUE], 0, &ret);
|
|
if (ret != 0) {
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
ret = gpio_pin_set(gpio.dev, gpio.pin, value != 0);
|
|
if (ret != 0) {
|
|
shell_error(sh, "error: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cmd_gpio_toggle(const struct shell *sh, size_t argc, char **argv)
|
|
{
|
|
struct sh_gpio gpio;
|
|
int ret = 0;
|
|
|
|
ret = get_sh_gpio(sh, argv, &gpio);
|
|
if (ret != 0) {
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
ret = gpio_pin_toggle(gpio.dev, gpio.pin);
|
|
if (ret != 0) {
|
|
shell_error(sh, "error: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
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 != NULL && 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
|
|
|
|
static int cmd_gpio_blink(const struct shell *sh, size_t argc, char **argv)
|
|
{
|
|
bool msg_one_shot = true;
|
|
struct sh_gpio gpio;
|
|
size_t count;
|
|
char data;
|
|
int ret;
|
|
|
|
ret = get_sh_gpio(sh, argv, &gpio);
|
|
if (ret != 0) {
|
|
shell_help(sh);
|
|
return SHELL_CMD_HELP_PRINTED;
|
|
}
|
|
|
|
/* dummy read to clear any pending input */
|
|
(void)sh->iface->api->read(sh->iface, &data, sizeof(data), &count);
|
|
|
|
while (true) {
|
|
(void)sh->iface->api->read(sh->iface, &data, sizeof(data), &count);
|
|
if (count != 0) {
|
|
break;
|
|
}
|
|
ret = gpio_pin_toggle(gpio.dev, gpio.pin);
|
|
if (ret != 0) {
|
|
shell_error(sh, "%d", ret);
|
|
break;
|
|
} else if (msg_one_shot) {
|
|
msg_one_shot = false;
|
|
shell_print(sh, "Hit any key to exit");
|
|
}
|
|
k_msleep(SLEEP_TIME_MS);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void device_name_get(size_t idx, struct shell_static_entry *entry)
|
|
{
|
|
if (idx >= ARRAY_SIZE(gpio_list)) {
|
|
entry->syntax = NULL;
|
|
return;
|
|
}
|
|
|
|
entry->syntax = gpio_list[idx].dev->name;
|
|
entry->handler = NULL;
|
|
entry->help = "Device";
|
|
entry->subcmd = gpio_list[idx].subcmd;
|
|
}
|
|
|
|
SHELL_DYNAMIC_CMD_CREATE(sub_gpio_dev, device_name_get);
|
|
|
|
struct pin_info {
|
|
const struct device *dev;
|
|
bool reserved;
|
|
gpio_pin_t pin;
|
|
const char *line_name;
|
|
};
|
|
|
|
struct pin_order_user_data {
|
|
const struct shell *sh;
|
|
struct pin_info prev;
|
|
struct pin_info next;
|
|
};
|
|
|
|
typedef void (*pin_foreach_func_t)(const struct pin_info *info, void *user_data);
|
|
|
|
static void print_gpio_ctrl_info(const struct shell *sh, const struct gpio_ctrl *ctrl)
|
|
{
|
|
gpio_pin_t pin;
|
|
bool reserved;
|
|
const char *line_name;
|
|
|
|
shell_print(sh, " ngpios: %u", ctrl->ngpios);
|
|
shell_print(sh, " Reserved pin mask: 0x%08X", ctrl->reserved_mask);
|
|
|
|
shell_print(sh, "");
|
|
|
|
shell_print(sh, " Reserved Pin Line Name");
|
|
for (pin = 0; pin < ctrl->ngpios; pin++) {
|
|
reserved = (BIT64(pin) & ctrl->reserved_mask) != 0;
|
|
if (pin < ctrl->line_names_len) {
|
|
line_name = ctrl->line_names[pin];
|
|
} else {
|
|
line_name = "";
|
|
}
|
|
shell_print(sh, " %c %2u %s", reserved ? '*' : ' ', pin, line_name);
|
|
}
|
|
}
|
|
|
|
static void foreach_pin(pin_foreach_func_t func, void *user_data)
|
|
{
|
|
gpio_port_pins_t reserved_mask;
|
|
struct pin_info info;
|
|
gpio_pin_t pin;
|
|
size_t i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(gpio_list); i++) {
|
|
for (pin = 0; pin < gpio_list[i].ngpios; pin++) {
|
|
info.dev = gpio_list[i].dev;
|
|
reserved_mask = gpio_list[i].reserved_mask;
|
|
info.reserved = (BIT64(pin) & reserved_mask) != 0;
|
|
info.pin = pin;
|
|
if (pin < gpio_list[i].line_names_len) {
|
|
info.line_name = gpio_list[i].line_names[pin];
|
|
} else {
|
|
info.line_name = "";
|
|
}
|
|
func(&info, user_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int pin_cmp(const struct pin_info *a, const struct pin_info *b)
|
|
{
|
|
int result = strcmp(a->line_name, b->line_name);
|
|
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
result = strcmp(a->dev->name, b->dev->name);
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
result = (int)a->pin - (int)b->pin;
|
|
|
|
return result;
|
|
}
|
|
|
|
static void pin_get_next(const struct pin_info *info, void *user_data)
|
|
{
|
|
struct pin_order_user_data *data = user_data;
|
|
int result;
|
|
|
|
if (data->prev.line_name != NULL) {
|
|
result = pin_cmp(info, &data->prev);
|
|
} else {
|
|
result = 1;
|
|
}
|
|
if (result > 0) {
|
|
if (data->next.line_name == NULL) {
|
|
data->next = *info;
|
|
return;
|
|
}
|
|
result = pin_cmp(info, &data->next);
|
|
if (result < 0) {
|
|
data->next = *info;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void pin_ordered(const struct pin_info *info, void *user_data)
|
|
{
|
|
struct pin_order_user_data *data = user_data;
|
|
|
|
ARG_UNUSED(info);
|
|
|
|
foreach_pin(pin_get_next, data);
|
|
|
|
shell_print(data->sh, " %-12s %-8c %-16s %2u",
|
|
data->next.line_name,
|
|
data->next.reserved ? '*' : ' ',
|
|
data->next.dev->name,
|
|
data->next.pin);
|
|
|
|
data->prev = data->next;
|
|
data->next.line_name = NULL;
|
|
}
|
|
|
|
static void print_ordered_info(const struct shell *sh)
|
|
{
|
|
struct pin_order_user_data data = {0};
|
|
|
|
data.sh = sh;
|
|
|
|
shell_print(sh, " %-12s %-8s %-16s %-3s",
|
|
"Line", "Reserved", "Device", "Pin");
|
|
|
|
foreach_pin(pin_ordered, &data);
|
|
}
|
|
|
|
static int cmd_gpio_info(const struct shell *sh, size_t argc, char **argv)
|
|
{
|
|
const struct gpio_ctrl *ctrl = get_gpio_ctrl(argv[ARGV_DEV]);
|
|
|
|
if (ctrl == NULL) {
|
|
/* No device specified */
|
|
print_ordered_info(sh);
|
|
return 0;
|
|
}
|
|
|
|
print_gpio_ctrl_info(sh, ctrl);
|
|
|
|
return 0;
|
|
}
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(sub_gpio,
|
|
SHELL_CMD_ARG(conf, &sub_gpio_dev,
|
|
"Configure GPIO pin\n"
|
|
"Usage: gpio conf <device> <pin> <configuration <i|o>[u|d][h|l][0|1]> [vendor specific]\n"
|
|
"<i|o> - input|output\n"
|
|
"[u|d] - pull up|pull down, otherwise open\n"
|
|
"[h|l] - active high|active low, otherwise defaults to active high\n"
|
|
"[0|1] - initialise to logic 0|logic 1, otherwise defaults to logic 0\n"
|
|
"[vendor specific] - configuration flags within the mask 0xFF00\n"
|
|
" see include/zephyr/dt-bindings/gpio/",
|
|
cmd_gpio_conf, 4, 1),
|
|
SHELL_CMD_ARG(get, &sub_gpio_dev,
|
|
"Get GPIO pin value\n"
|
|
"Usage: gpio get <device> <pin>", cmd_gpio_get, 3, 0),
|
|
SHELL_CMD_ARG(set, &sub_gpio_dev,
|
|
"Set GPIO pin value\n"
|
|
"Usage: gpio set <device> <pin> <level 0|1>", cmd_gpio_set, 4, 0),
|
|
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),
|
|
SHELL_COND_CMD_ARG(CONFIG_GPIO_SHELL_INFO_CMD, info, &sub_gpio_dev,
|
|
"GPIO Information\n"
|
|
"Usage: gpio info [device]", cmd_gpio_info, 1, 1),
|
|
SHELL_SUBCMD_SET_END /* Array terminated. */
|
|
);
|
|
|
|
SHELL_CMD_REGISTER(gpio, &sub_gpio, "GPIO commands", NULL);
|