Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion include/zephyr/shell/shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,44 @@ struct shell_static_args {
* start with this text. Pass null if no prefix match is required.
*/
const struct device *shell_device_lookup(size_t idx,
const char *prefix);
const char *prefix);

/**
* @brief Get by index a device that matches .
*
* This can be used, for example, to identify I2C_1 as the second I2C
* device.
*
* Devices that failed to initialize - or deferred to be - are included
* from the candidates for a match, minus the ones who do not have
* a non-empty name.
*
* @param idx the device number starting from zero.
*
* @param prefix optional name prefix used to restrict candidate
* devices. Indexing is done relative to devices with names that
* start with this text. Pass null if no prefix match is required.
*/
const struct device *shell_device_lookup_all(size_t idx,
const char *prefix);

/**
* @brief Get by index a non-initialized device that matches .
*
* This can be used, for example, to identify I2C_1 as the second I2C
* device.
*
* Devices that initialized successfully or do not have a non-empty name
* are excluded.
*
* @param idx the device number starting from zero.
*
* @param prefix optional name prefix used to restrict candidate
* devices. Indexing is done relative to devices with names that
* start with this text. Pass null if no prefix match is required.
*/
const struct device *shell_device_lookup_non_ready(size_t idx,
const char *prefix);

/**
* @brief Filter callback type, for use with shell_device_lookup_filter
Expand Down Expand Up @@ -181,6 +218,23 @@ const struct device *shell_device_filter(size_t idx,
*/
const struct device *shell_device_get_binding(const char *name);

/**
* @brief Get a @ref device reference from its @ref device.name field or label.
*
* This function iterates through the devices on the system. If a device with
* the given @p name field is found, this function returns a pointer to the
* device.
*
* If no device has the given @p name, this function returns `NULL`.
*
* @param name device name to search for. A null pointer, or a pointer to an
* empty string, will cause NULL to be returned.
*
* @return pointer to device structure with the given name; `NULL` if the device
* is not found.
*/
const struct device *shell_device_get_binding_all(const char *name);

/**
* @brief Shell command handler prototype.
*
Expand Down
44 changes: 43 additions & 1 deletion subsys/shell/modules/device_service.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2016 Intel Corporation
* Copyright (C) 2025 Bang & Olufsen A/S, Denmark
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -117,6 +118,33 @@ static int cmd_device_list(const struct shell *sh,
return 0;
}

static int cmd_device_init(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
int ret;

dev = device_get_binding(argv[1]);
if (dev == NULL) {
shell_error(sh, "Device unknown (%s)", argv[1]);
return -ENODEV;
}

if (device_is_ready(dev)) {
shell_info(sh, "Device %s is already initialized", argv[1]);
return 0;
}

ret = device_init(dev);
if (ret != 0) {
shell_error(sh, "Device %s initalization failed with err=%d",
argv[1], ret);
} else {
shell_info(sh, "Device %s inialized successfully", argv[1]);
}

return ret;
}

#ifdef CONFIG_PM_DEVICE_RUNTIME
static int cmd_device_pm_toggle(const struct shell *sh,
size_t argc, char **argv)
Expand Down Expand Up @@ -156,10 +184,24 @@ static int cmd_device_pm_toggle(const struct shell *sh,
#define PM_SHELL_CMD
#endif /* CONFIG_PM_DEVICE_RUNTIME */

static void device_name_get_non_ready(size_t idx,
struct shell_static_entry *entry)
{
const struct device *dev = shell_device_lookup_non_ready(idx, NULL);

entry->syntax = dev != NULL ? dev->name : NULL;
entry->handler = NULL;
entry->help = "device";
entry->subcmd = NULL;
}

SHELL_DYNAMIC_CMD_CREATE(dsub_device_name_non_ready, device_name_get_non_ready);

SHELL_STATIC_SUBCMD_SET_CREATE(sub_device,
SHELL_CMD(list, NULL, "List configured devices", cmd_device_list),
SHELL_CMD(list, &dsub_device_name_all,
"List configured devices", cmd_device_list),
SHELL_CMD_ARG(init, &dsub_device_name_non_ready,
"Manually initialize a device", cmd_device_init, 2, 0),
PM_SHELL_CMD
SHELL_SUBCMD_SET_END /* Array terminated. */
);
Expand Down
51 changes: 47 additions & 4 deletions subsys/shell/shell_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,17 +496,28 @@ void z_shell_cmd_trim(const struct shell *sh)
sh->ctx->cmd_buff_pos = sh->ctx->cmd_buff_len;
}

enum shell_device_status {
SHELL_DEVICE_STATUS_ANY,
SHELL_DEVICE_STATUS_READY,
SHELL_DEVICE_STATUS_NON_READY
};

static const struct device *shell_device_internal(size_t idx,
const char *prefix,
shell_device_filter_t filter)
shell_device_filter_t filter,
enum shell_device_status status)
{
size_t match_idx = 0;
const struct device *dev;
size_t len = z_device_get_all_static(&dev);
const struct device *dev_end = dev + len;

while (dev < dev_end) {
if (device_is_ready(dev)
if ((status == SHELL_DEVICE_STATUS_ANY
|| (status == SHELL_DEVICE_STATUS_READY &&
device_is_ready(dev))
|| (status == SHELL_DEVICE_STATUS_NON_READY &&
!device_is_ready(dev)))
&& (dev->name != NULL)
&& (strlen(dev->name) != 0)
&& ((prefix == NULL)
Expand All @@ -527,13 +538,29 @@ static const struct device *shell_device_internal(size_t idx,
const struct device *shell_device_filter(size_t idx,
shell_device_filter_t filter)
{
return shell_device_internal(idx, NULL, filter);
return shell_device_internal(idx, NULL, filter,
SHELL_DEVICE_STATUS_READY);
}

const struct device *shell_device_lookup(size_t idx,
const char *prefix)
{
return shell_device_internal(idx, prefix, NULL);
return shell_device_internal(idx, prefix, NULL,
SHELL_DEVICE_STATUS_READY);
}

const struct device *shell_device_lookup_all(size_t idx,
const char *prefix)
{
return shell_device_internal(idx, prefix, NULL,
SHELL_DEVICE_STATUS_ANY);
}

const struct device *shell_device_lookup_non_ready(size_t idx,
const char *prefix)
{
return shell_device_internal(idx, prefix, NULL,
SHELL_DEVICE_STATUS_NON_READY);
}

const struct device *shell_device_get_binding(const char *name)
Expand All @@ -547,6 +574,22 @@ const struct device *shell_device_get_binding(const char *name)
return dev;
}

const struct device *shell_device_get_binding_all(const char *name)
{
const struct device *dev;
size_t len = z_device_get_all_static(&dev);
const struct device *dev_end = dev + len;

for (; dev < dev_end; dev++) {
if ((dev->name != NULL) &&

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ((dev->name != NULL) &&
if (dev->name &&

Isn't this the common approach in zephyr as well when checking non-null?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I c/p shell_device_internal().
In linux it is the way yes (like testing any integer if it has a value or 0) but in zephyr... I thought there might be some MISRA rule for this, but looking at https://docs.zephyrproject.org/latest/contribute/coding_guidelines/index.html I am not so sure anymore (could not find anything related to this)

(strncmp(name, dev->name, strlen(name)) == 0)) {
return dev;
}
}

return NULL;
}

long shell_strtol(const char *str, int base, int *err)
{
long val;
Expand Down