Skip to content

Fix shell threadless support #93184

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
15 changes: 15 additions & 0 deletions doc/kernel/services/threads/nothread.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,18 @@ peripheral drivers.

*List/table of supported drivers to go here, including which API options
are supported*

Shell
=====

The :ref:`shell_api` along with a subset of backends are expected to work
given the application calls :c:func:`shell_start` to start the shell
backend, and intermittently calls :c:func:`shell_process` to check for and
process new input.

The following backends are supported:

* Serial shell backend :kconfig:option:`CONFIG_SHELL_BACKEND_SERIAL`
configured to use either
:kconfig:option:`CONFIG_SHELL_BACKEND_SERIAL_API_INTERRUPT_DRIVEN` or
:kconfig:option:`CONFIG_SHELL_BACKEND_SERIAL_API_ASYNC`
45 changes: 28 additions & 17 deletions include/zephyr/shell/shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -900,11 +900,10 @@ union shell_backend_ctx {
};

enum shell_signal {
SHELL_SIGNAL_RXRDY,
SHELL_SIGNAL_LOG_MSG,
SHELL_SIGNAL_KILL,
SHELL_SIGNAL_TXDONE, /* TXDONE must be last one before SHELL_SIGNALS */
SHELL_SIGNALS
SHELL_SIGNAL_RXRDY = BIT(0),
SHELL_SIGNAL_LOG_MSG = BIT(1),
SHELL_SIGNAL_KILL = BIT(2),
SHELL_SIGNAL_TXDONE = BIT(3),
};

/**
Expand Down Expand Up @@ -962,15 +961,11 @@ struct shell_ctx {
volatile union shell_backend_cfg cfg;
volatile union shell_backend_ctx ctx;

struct k_poll_signal signals[SHELL_SIGNALS];

/** Events that should be used only internally by shell thread.
* Event for SHELL_SIGNAL_TXDONE is initialized but unused.
*/
struct k_poll_event events[SHELL_SIGNALS];

struct k_mutex wr_mtx;
#if CONFIG_MULTITHREADING
struct k_event signal_event;
struct k_sem lock_sem;
k_tid_t tid;
#endif
int ret_val;
};

Expand Down Expand Up @@ -1006,13 +1001,29 @@ struct shell {
LOG_INSTANCE_PTR_DECLARE(log);

const char *name;

#if CONFIG_MULTITHREADING
struct k_thread *thread;
k_thread_stack_t *stack;
#endif
};

extern void z_shell_print_stream(const void *user_ctx, const char *data,
size_t data_len);

#if CONFIG_MULTITHREADING
#define Z_SHELL_THREAD_DEFINE(_name) \
static K_KERNEL_STACK_DEFINE(_name##_stack, CONFIG_SHELL_STACK_SIZE); \
static struct k_thread _name##_thread

#define Z_SHELL_THREAD_INIT(_name) \
.thread = &_name##_thread, \
.stack = _name##_stack,
#else
#define Z_SHELL_THREAD_DEFINE(_name)
#define Z_SHELL_THREAD_INIT(_name)
#endif

/** @brief Internal macro for defining a shell instance.
*
* As it does not create the default shell logging backend it allows to use
Expand All @@ -1033,8 +1044,7 @@ extern void z_shell_print_stream(const void *user_ctx, const char *data,
IS_ENABLED(CONFIG_SHELL_PRINTF_AUTOFLUSH), z_shell_print_stream); \
LOG_INSTANCE_REGISTER(shell, _name, CONFIG_SHELL_LOG_LEVEL); \
Z_SHELL_STATS_DEFINE(_name); \
static K_KERNEL_STACK_DEFINE(_name##_stack, CONFIG_SHELL_STACK_SIZE); \
static struct k_thread _name##_thread; \
Z_SHELL_THREAD_DEFINE(_name); \
static const STRUCT_SECTION_ITERABLE(shell, _name) = { \
.default_prompt = _prompt, \
.iface = _transport_iface, \
Expand All @@ -1044,8 +1054,9 @@ extern void z_shell_print_stream(const void *user_ctx, const char *data,
.fprintf_ctx = &_name##_fprintf, \
.stats = Z_SHELL_STATS_PTR(_name), \
.log_backend = _log_backend, \
LOG_INSTANCE_PTR_INIT(log, shell, _name).name = \
STRINGIFY(_name), .thread = &_name##_thread, .stack = _name##_stack}
LOG_INSTANCE_PTR_INIT(log, shell, _name).name = STRINGIFY(_name), \
Z_SHELL_THREAD_INIT(_name) \
}

/**
* @brief Macro for defining a shell instance.
Expand Down
9 changes: 9 additions & 0 deletions samples/subsys/shell/threadless/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright 2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(shell_threadless)

target_sources(app PRIVATE src/main.c)
43 changes: 43 additions & 0 deletions samples/subsys/shell/threadless/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.. zephyr:code-sample:: threadless_shell
:name: Threadless shell

Run shell in single threaded build

Overview
********

Demonstrate how to configure and use a shell in a single threaded build
(:kconfig:option:`CONFIG_MULTITHREADING` disabled). The sample uses the
UART shell backend using either the async
(:kconfig:option:`CONFIG_UART_ASYNC_API`) or interrupt driven
(:kconfig:option:`CONFIG_UART_INTERRUPT_DRIVEN`) APIs. The sample
implements a single command, which simply echoes back the single
argument passed to it, along with the built-in "help" command.

.. code-block:: console

uart:~$ sample foobar
foobar
uart:~$

Building and Running
********************

This application can be built and executed as follows:

.. zephyr-app-commands::
:zephyr-app: samples/subsys/shell/threadless
:host-os: unix
:board: nrf54l15dk/nrf54l15/cpuapp
:goals: run
:compact:

To build for another board, change "nrf54l15dk/nrf54l15/cpuapp" above
to that board's name.

Sample Output
*************

.. code-block:: console

uart:~$
3 changes: 3 additions & 0 deletions samples/subsys/shell/threadless/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_MULTITHREADING=n
CONFIG_SHELL=y
CONFIG_SHELL_MINIMAL=y
18 changes: 18 additions & 0 deletions samples/subsys/shell/threadless/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
sample:
description: Threadless shell using uart backend
name: Threadless shell
tests:
sample.subsys.shell.threadless:
tags: threadless
min_ram: 2
min_flash: 32
filter: CONFIG_ARCH_HAS_SINGLE_THREAD_SUPPORT and
dt_chosen_enabled("zephyr,shell-uart")
harness: shell
harness_config:
shell_commands:
- command: "sample foobar"
expected: "foobar"
platform_exclude:
- qemu_cortex_a53
- qemu_cortex_a53/qemu_cortex_a53/smp
32 changes: 32 additions & 0 deletions samples/subsys/shell/threadless/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/shell/shell.h>
#include <zephyr/shell/shell_uart.h>

static int sample_sh_echo(const struct shell *sh, size_t argc, char **argv)
{
shell_print(sh, "%s", argv[1]);
return 0;
}

SHELL_CMD_ARG_REGISTER(sample, NULL, "Echo", sample_sh_echo, 2, 2);

int main(void)
{
/*
* Start the shell. This prints the shell prompt and enables
* input reception.
*/
shell_start(shell_backend_uart_get_ptr());

while (1) {
/* Check for new input and process it if present */
shell_process(shell_backend_uart_get_ptr());
}

return 0;
}
2 changes: 1 addition & 1 deletion subsys/shell/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
menuconfig SHELL
bool "Shell"
imply LOG_RUNTIME_FILTERING
select POLL
select EVENTS if MULTITHREADING

if SHELL

Expand Down
Loading