shell: rtt: Add detection of host presence

If host is not reading RTT data (because there is no PC connection
or RTT reading application is not running on the host), thread will
stuck continuously trying to write to RTT. All threads with equal or
lower priority are blocked then. Adding detection of that case and
if host is not reading data for configurable period then data is
dropped until host accepts new data.

Similar solution is using in RTT logging backend.

Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
Krzysztof Chruściński 2024-02-13 16:12:14 +01:00 committed by Fabio Baltieri
parent dfe34ca03a
commit 04a74ce107
2 changed files with 87 additions and 10 deletions

View file

@ -185,6 +185,7 @@ config SHELL_BACKEND_RTT
bool "RTT backend"
select CONSOLE
select RTT_CONSOLE
select SEGGER_RTT_CUSTOM_LOCKING
depends on USE_SEGGER_RTT
help
Enable RTT backend.
@ -206,6 +207,23 @@ config SHELL_BACKEND_RTT_BUFFER
Select index of up-buffer used for shell output, by default it uses
terminal up-buffer and its settings.
config SHELL_BACKEND_RTT_RETRY_CNT
int "Number of retries"
default 4
help
Number of TX retries before dropping the data and assuming that
RTT session is inactive.
config SHELL_BACKEND_RTT_RETRY_DELAY_MS
int "Delay between TX retries in milliseconds"
default 5
help
Sleep period between TX retry attempts. During RTT session, host pulls
data periodically. Period starts from 1-2 milliseconds and can be
increased if traffic on RTT increases (also from host to device). In
case of heavy traffic data can be lost and it may be necessary to
increase delay or number of retries.
config SHELL_RTT_RX_POLL_PERIOD
int "RX polling period (in milliseconds)"
default 10

View file

@ -9,6 +9,12 @@
#include <SEGGER_RTT.h>
#include <zephyr/logging/log.h>
#define RTT_LOCK() \
COND_CODE_0(CONFIG_SHELL_BACKEND_RTT_BUFFER, (SEGGER_RTT_LOCK()), ())
#define RTT_UNLOCK() \
COND_CODE_0(CONFIG_SHELL_BACKEND_RTT_BUFFER, (SEGGER_RTT_UNLOCK()), ())
#if IS_ENABLED(CONFIG_LOG_BACKEND_RTT)
BUILD_ASSERT(!(CONFIG_SHELL_BACKEND_RTT_BUFFER == CONFIG_LOG_BACKEND_RTT_BUFFER),
"Conflicting log RTT backend enabled on the same channel");
@ -25,7 +31,8 @@ SHELL_DEFINE(shell_rtt, CONFIG_SHELL_PROMPT_RTT, &shell_transport_rtt,
LOG_MODULE_REGISTER(shell_rtt, CONFIG_SHELL_RTT_LOG_LEVEL);
static bool rtt_blocking;
static bool panic_mode;
static bool host_present;
static void timer_handler(struct k_timer *timer)
{
@ -78,29 +85,81 @@ static int enable(const struct shell_transport *transport, bool blocking)
struct shell_rtt *sh_rtt = (struct shell_rtt *)transport->ctx;
if (blocking) {
rtt_blocking = true;
k_timer_stop(&sh_rtt->timer);
}
return 0;
}
static inline bool is_panic_mode(void)
{
return panic_mode;
}
static inline bool is_sync_mode(void)
{
return (IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) && IS_ENABLED(CONFIG_SHELL_LOG_BACKEND)) ||
is_panic_mode();
}
static void on_failed_write(int retry_cnt)
{
if (retry_cnt == 0) {
host_present = false;
} else if (is_sync_mode()) {
k_busy_wait(USEC_PER_MSEC *
CONFIG_SHELL_BACKEND_RTT_RETRY_DELAY_MS);
} else {
k_msleep(CONFIG_SHELL_BACKEND_RTT_RETRY_DELAY_MS);
}
}
static void on_write(int retry_cnt)
{
host_present = true;
if (is_panic_mode()) {
/* In panic mode block on each write until host reads it. This
* way it is ensured that if system resets all messages are read
* by the host. While pending on data being read by the host we
* must also detect situation where host is disconnected.
*/
while (SEGGER_RTT_HasDataUp(CONFIG_SHELL_BACKEND_RTT_BUFFER) &&
host_present) {
on_failed_write(retry_cnt--);
}
}
}
static int write(const struct shell_transport *transport,
const void *data, size_t length, size_t *cnt)
{
struct shell_rtt *sh_rtt = (struct shell_rtt *)transport->ctx;
const uint8_t *data8 = (const uint8_t *)data;
int ret = 0;
int retry_cnt = CONFIG_SHELL_BACKEND_RTT_RETRY_CNT;
if (rtt_blocking) {
*cnt = SEGGER_RTT_WriteNoLock(CONFIG_SHELL_BACKEND_RTT_BUFFER, data8, length);
while (SEGGER_RTT_HasDataUp(CONFIG_SHELL_BACKEND_RTT_BUFFER)) {
/* empty */
do {
if (!is_sync_mode()) {
RTT_LOCK();
ret = SEGGER_RTT_WriteSkipNoLock(CONFIG_SHELL_BACKEND_RTT_BUFFER,
data, length);
RTT_UNLOCK();
} else {
ret = SEGGER_RTT_WriteSkipNoLock(CONFIG_SHELL_BACKEND_RTT_BUFFER,
data, length);
}
} else {
*cnt = SEGGER_RTT_Write(CONFIG_SHELL_BACKEND_RTT_BUFFER, data8, length);
}
if (ret) {
on_write(retry_cnt);
} else if (host_present) {
retry_cnt--;
on_failed_write(retry_cnt);
} else {
}
} while ((ret == 0) && host_present);
sh_rtt->handler(SHELL_TRANSPORT_EVT_TX_RDY, sh_rtt->context);
*cnt = length;
return 0;
}