feat(console): Make console deinit optional

This commit is contained in:
Guillaume Souchere
2025-03-11 12:18:22 +01:00
parent 8a89dc0b9c
commit 30f8b59ed0
12 changed files with 304 additions and 187 deletions

View File

@@ -2,6 +2,7 @@ idf_build_get_property(target IDF_TARGET)
set(srcs "commands.c"
"esp_console_common.c"
"esp_console_repl_internal.c"
"split_argv.c"
"linenoise/linenoise.c")

View File

@@ -451,13 +451,23 @@ esp_err_t esp_console_new_repl_usb_serial_jtag(const esp_console_dev_usb_serial_
/**
* @brief Start REPL environment
* @param[in] repl REPL handle returned from esp_console_new_repl_xxx
* @note Once the REPL gets started, it won't be stopped until the user calls repl->del(repl) to destroy the REPL environment.
* @note Once the REPL gets started, it won't be stopped until the user calls esp_console_stop_repl to destroy the REPL environment.
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE, if repl has started already
*/
esp_err_t esp_console_start_repl(esp_console_repl_t *repl);
/**
* @brief Stop REPL environment
*
* @param[in] repl REPL handle returned from esp_console_new_repl_xxx
* @return
* - ESP_OK on success
* - others on failure
*/
esp_err_t esp_console_stop_repl(esp_console_repl_t *repl);
#ifdef __cplusplus
}
#endif

View File

@@ -6,6 +6,8 @@
#include "sdkconfig.h"
#include <sys/cdefs.h> // __containerof
#include <sys/param.h>
#include <sys/fcntl.h>
#include "esp_console.h"
#include "console_private.h"
#include "esp_log.h"
@@ -18,10 +20,6 @@
static const char *TAG = "console.common";
static bool s_init = false;
static int s_interrupt_reading_fd = -1;
static uint64_t s_interrupt_reading_signal = 1;
esp_err_t esp_console_setup_prompt(const char *prompt, esp_console_repl_com_t *repl_com)
{
/* set command line prompt */
@@ -68,6 +66,11 @@ _exit:
return ret;
}
__attribute__((weak)) esp_err_t esp_console_internal_set_event_fd(esp_console_repl_com_t *repl_com)
{
return ESP_OK;
}
esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_com_t *repl_com)
{
esp_err_t ret = ESP_OK;
@@ -103,50 +106,36 @@ esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_co
linenoiseSetCompletionCallback(&esp_console_get_completion);
linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint);
/* Tell linenoise what file descriptor to add to the read file descriptor set,
* that will be used to signal a read termination */
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
ret = esp_vfs_eventfd_register(&config);
if (ret == ESP_OK) {
/* first time calling the eventfd register function */
s_interrupt_reading_fd = eventfd(0, 0);
linenoiseSetInterruptReadingData(s_interrupt_reading_fd, s_interrupt_reading_signal);
} else if (ret == ESP_ERR_INVALID_STATE) {
/* the evenfd has already been registered*/
} else {
/* issue with arg, this should not happen */
#if CONFIG_VFS_SUPPORT_SELECT
ret = esp_console_internal_set_event_fd(repl_com);
if (ret != ESP_OK) {
goto _exit;
}
#endif
repl_com->state_mux = xSemaphoreCreateMutex();
if (repl_com->state_mux == NULL) {
ESP_LOGE(TAG, "state_mux create error");
ret = ESP_ERR_NO_MEM;
goto _exit;
}
xSemaphoreGive(repl_com->state_mux);
s_init = true;
return ESP_OK;
_exit:
s_init = false;
return ret;
}
void esp_console_common_deinit(esp_console_repl_com_t *repl_com)
__attribute__((weak)) esp_err_t esp_console_common_deinit(esp_console_repl_com_t *repl_com)
{
// set the state to deinit to force the while loop in
// esp_console_repl_task to break
repl_com->state = CONSOLE_REPL_STATE_DEINIT;
/* Unregister the heap function to avoid memory leak, since it is created
* every time a console init is called. */
esp_err_t ret = esp_console_deregister_help_command();
assert(ret == ESP_OK);
/* unregister eventfd to avoid memory leaks, since it is created every time a
* console init is called */
(void)esp_vfs_eventfd_unregister();
if (ret != ESP_OK) {
return ret;
}
/* free the history to avoid memory leak, since it is created
* every time a console init is called. */
linenoiseHistoryFree();
return ESP_OK;
}
esp_err_t esp_console_start_repl(esp_console_repl_t *repl)
@@ -176,9 +165,10 @@ void esp_console_repl_task(void *args)
* function is called. */
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
assert(repl_com->state_mux != NULL);
BaseType_t ret_val = xSemaphoreTake(repl_com->state_mux, portMAX_DELAY);
assert(ret_val == pdTRUE);
if (repl_com->state_mux != NULL) {
BaseType_t ret_val = xSemaphoreTake(repl_com->state_mux, portMAX_DELAY);
assert(ret_val == pdTRUE);
}
/* Change standard input and output of the task if the requested UART is
* NOT the default one. This block will replace stdin, stdout and stderr.
@@ -242,17 +232,9 @@ void esp_console_repl_task(void *args)
linenoiseFree(line);
}
xSemaphoreGive(repl_com->state_mux);
if (repl_com->state_mux != NULL) {
xSemaphoreGive(repl_com->state_mux);
}
ESP_LOGD(TAG, "The End");
vTaskDelete(NULL);
}
esp_err_t esp_console_interrupt_reading(void)
{
if (!s_init) {
return ESP_FAIL;
}
ssize_t ret = write(s_interrupt_reading_fd, &s_interrupt_reading_signal, sizeof(s_interrupt_reading_signal));
assert(ret == sizeof(s_interrupt_reading_signal));
return ESP_OK;
}

View File

@@ -24,7 +24,7 @@
#include "console_private.h"
#if !CONFIG_ESP_CONSOLE_NONE
static const char *TAG = "console.repl";
static const char *TAG = "console.repl.chip";
#endif // !CONFIG_ESP_CONSOLE_NONE
#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM
@@ -296,27 +296,11 @@ static esp_err_t esp_console_repl_uart_delete(esp_console_repl_t *repl)
goto _exit;
}
// set the state to deinit to force the while loop in
// esp_console_repl_task to break
repl_com->state = CONSOLE_REPL_STATE_DEINIT;
const esp_err_t read_interrupted = esp_console_interrupt_reading();
if (read_interrupted != ESP_OK) {
return ESP_FAIL;
ret = esp_console_common_deinit(&uart_repl->repl_com);
if (ret != ESP_OK) {
goto _exit;
}
// wait for the task to notify that
// esp_console_repl_task returned
assert(repl_com->state_mux != NULL);
BaseType_t ret_val = xSemaphoreTake(repl_com->state_mux, portMAX_DELAY);
assert(ret_val == pdTRUE);
// delete the semaphore for the repl state
vSemaphoreDelete(repl_com->state_mux);
repl_com->state_mux = NULL;
esp_console_common_deinit(&uart_repl->repl_com);
esp_console_deinit();
uart_vfs_dev_use_nonblocking(uart_repl->uart_channel);
uart_driver_delete(uart_repl->uart_channel);
@@ -339,27 +323,11 @@ static esp_err_t esp_console_repl_usb_cdc_delete(esp_console_repl_t *repl)
goto _exit;
}
// set the state to deinit to force the while loop in
// esp_console_repl_task to break
repl_com->state = CONSOLE_REPL_STATE_DEINIT;
const esp_err_t read_interrupted = esp_console_interrupt_reading();
if (read_interrupted != ESP_OK) {
return ESP_FAIL;
ret = esp_console_common_deinit(&cdc_repl->repl_com);
if (ret != ESP_OK) {
goto _exit;
}
// wait for the task to notify that
// esp_console_repl_task returned
assert(repl_com->state_mux != NULL);
BaseType_t ret_val = xSemaphoreTake(repl_com->state_mux, portMAX_DELAY);
assert(ret_val == pdTRUE);
// delete the semaphore for the repl state
vSemaphoreDelete(repl_com->state_mux);
repl_com->state_mux = NULL;
esp_console_common_deinit(&cdc_repl->repl_com);
esp_console_deinit();
free(cdc_repl);
_exit:
@@ -380,27 +348,11 @@ static esp_err_t esp_console_repl_usb_serial_jtag_delete(esp_console_repl_t *rep
goto _exit;
}
// set the state to deinit to force the while loop in
// esp_console_repl_task to break
repl_com->state = CONSOLE_REPL_STATE_DEINIT;
const esp_err_t read_interrupted = esp_console_interrupt_reading();
if (read_interrupted != ESP_OK) {
return ESP_FAIL;
ret = esp_console_common_deinit(&usb_serial_jtag_repl->repl_com);
if (ret != ESP_OK) {
goto _exit;
}
// wait for the task to notify that
// esp_console_repl_task returned
assert(repl_com->state_mux != NULL);
BaseType_t ret_val = xSemaphoreTake(repl_com->state_mux, portMAX_DELAY);
assert(ret_val == pdTRUE);
// delete the semaphore for the repl state
vSemaphoreDelete(repl_com->state_mux);
repl_com->state_mux = NULL;
esp_console_common_deinit(&usb_serial_jtag_repl->repl_com);
esp_console_deinit();
usb_serial_jtag_vfs_use_nonblocking();
usb_serial_jtag_driver_uninstall();

View File

@@ -0,0 +1,166 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <unistd.h>
#include <sys/cdefs.h> // __containerof
#include <sys/param.h>
#include <sys/fcntl.h>
#include "esp_console.h"
#include "console_private.h"
#include "esp_log.h"
#include "linenoise/linenoise.h"
#include "esp_vfs_eventfd.h"
#if CONFIG_VFS_SUPPORT_SELECT
static const char *TAG = "console.repl.internal";
static int s_interrupt_reading_fd = -1;
static uint64_t s_interrupt_reading_signal = 1;
static ssize_t read_bytes(int fd, void *buf, size_t max_bytes)
{
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
if (s_interrupt_reading_fd != -1) {
FD_SET(s_interrupt_reading_fd, &read_fds);
}
int maxFd = MAX(fd, s_interrupt_reading_fd);
/* call select to wait for either a read ready or an except to happen */
int nread = select(maxFd + 1, &read_fds, NULL, NULL, NULL);
if (nread < 0) {
return -1;
}
if (FD_ISSET(s_interrupt_reading_fd, &read_fds)) {
/* read termination request happened, return */
int buf[sizeof(s_interrupt_reading_signal)];
nread = read(s_interrupt_reading_fd, buf, sizeof(s_interrupt_reading_signal));
if ((nread == sizeof(s_interrupt_reading_signal)) && (buf[0] == s_interrupt_reading_signal)) {
return -1;
}
} else if (FD_ISSET(fd, &read_fds)) {
/* a read ready triggered the select to return. call the
* read function with the number of bytes max_bytes */
nread = read(fd, buf, max_bytes);
}
return nread;
}
/* Strong definition of the weak definition of linenoiseSetReadCharacteristics in
* linenoise.c. This function set the read to be non blocking and set the read
* function used by linenoise to read_bytes. This function is compiled only if
* esp_console_stop_repl is used. */
void linenoiseSetReadCharacteristics(void)
{
/* Make sure we are using non blocking reads with
* the select */
int stdin_fileno = fileno(stdin);
int flags = fcntl(stdin_fileno, F_GETFL);
flags |= O_NONBLOCK;
(void)fcntl(stdin_fileno, F_SETFL, flags);
linenoiseSetReadFunction(read_bytes);
}
/* Strong definition of the weak definition of esp_console_internal_set_event_fd
* in esp_console_common to allow the use of select with non blocking
* read. This function is compiled only if esp_console_stop_repl
* is used. */
esp_err_t esp_console_internal_set_event_fd(esp_console_repl_com_t *repl_com)
{
/* Tell linenoise what file descriptor to add to the read file descriptor set,
* that will be used to signal a read termination */
esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT();
esp_err_t ret = esp_vfs_eventfd_register(&config);
if (ret == ESP_OK) {
/* first time calling the eventfd register function */
s_interrupt_reading_fd = eventfd(0, 0);
} else if (ret == ESP_ERR_INVALID_STATE) {
/* the evenfd has already been registered*/
} else {
/* issue with arg, this should not happen */
return ESP_FAIL;
}
repl_com->state_mux = xSemaphoreCreateMutex();
if (repl_com->state_mux == NULL) {
ESP_LOGE(TAG, "state_mux create error");
return ESP_ERR_NO_MEM;
}
xSemaphoreGive(repl_com->state_mux);
return ESP_OK;
}
/* Strong definition of the weak definition of esp_console_common_deinit
* in esp_console_common to allow the use of select with non blocking
* read. This function is compiled only if esp_console_stop_repl
* is used. */
esp_err_t esp_console_common_deinit(esp_console_repl_com_t *repl_com)
{
// set the state to deinit to force the while loop in
// esp_console_repl_task to break
repl_com->state = CONSOLE_REPL_STATE_DEINIT;
if (s_interrupt_reading_fd == -1) {
return ESP_FAIL;
}
int nwrite = write(s_interrupt_reading_fd, &s_interrupt_reading_signal, sizeof(s_interrupt_reading_signal));
if (nwrite != sizeof(s_interrupt_reading_signal)) {
return ESP_FAIL;
}
// wait for the task to notify that
// esp_console_repl_task returned
assert(repl_com->state_mux != NULL);
BaseType_t ret_val = xSemaphoreTake(repl_com->state_mux, portMAX_DELAY);
assert(ret_val == pdTRUE);
// delete the semaphore for the repl state
vSemaphoreDelete(repl_com->state_mux);
repl_com->state_mux = NULL;
/* Unregister the heap function to avoid memory leak, since it is created
* every time a console init is called. */
esp_err_t ret = esp_console_deregister_help_command();
if (ret != ESP_OK) {
return ret;
}
/* unregister eventfd to avoid memory leaks, since it is created every time a
* console init is called */
ret = esp_vfs_eventfd_unregister();
if (ret != ESP_OK) {
return ret;
}
/* free the history to avoid memory leak, since it is created
* every time a console init is called. */
linenoiseHistoryFree();
return ESP_OK;
}
#endif // CONFIG_VFS_SUPPORT_SELECT
/* DO NOT move this function out of this file. All other definitions in this
* file are strong definition of weak functions.
*
* Those function are used to provide a clean way to exit linenoise
* and properly deinitialize the console by using select with non blocking
* read instead of blocking read as the default way to read character implemented
* in linenoise.
*
* If the user never calls this function, then the default read process is used and
* those functions will be ignored by the linker. */
esp_err_t esp_console_stop_repl(esp_console_repl_t *repl)
{
return repl->del(repl);
}

View File

@@ -116,14 +116,12 @@
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/time.h>
#include <sys/param.h>
#include <unistd.h>
#include <assert.h>
#include "linenoise.h"
@@ -145,9 +143,6 @@ static int history_len = 0;
static char **history = NULL;
static bool allow_empty = true;
static int intr_reading_fd = -1;
static uint64_t intr_reading_signal = -1;
/* The linenoiseState structure represents the state during line editing.
* We pass this state to functions implementing specific editing
* functionalities. */
@@ -243,35 +238,21 @@ static void flushWrite(void) {
fsync(fileno(stdout));
}
static int linenoiseReadBytes(int fd, void *buf, size_t max_bytes)
static linenoise_read_bytes_fn read_func = NULL;
void linenoiseSetReadFunction(linenoise_read_bytes_fn read_fn)
{
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
if (intr_reading_fd !=-1) {
FD_SET(intr_reading_fd, &read_fds);
}
int maxFd = MAX(fd, intr_reading_fd);
/* call select to wait for either a read ready or an except to happen */
int nread = select(maxFd + 1, &read_fds, NULL, NULL, NULL);
if (nread < 0) {
return -1;
}
read_func = read_fn;
}
if(FD_ISSET(intr_reading_fd, &read_fds)) {
/* read termination request happened, return */
int buf[sizeof(intr_reading_signal)];
nread = read(intr_reading_fd, buf, sizeof(intr_reading_signal));
if ((nread == sizeof(intr_reading_signal)) && (buf[0] == intr_reading_signal)) {
return -1;
}
}
else if(FD_ISSET(fd, &read_fds)) {
/* a read ready triggered the select to return. call the
* read function with the number of bytes max_bytes */
nread = read(fd, buf, max_bytes);
}
return nread;
__attribute__((weak)) void linenoiseSetReadCharacteristics(void)
{
/* By default linenoise uses blocking reads */
int stdin_fileno = fileno(stdin);
int flags = fcntl(stdin_fileno, F_GETFL);
flags &= ~O_NONBLOCK;
(void)fcntl(stdin_fileno, F_SETFL, flags);
linenoiseSetReadFunction(read);
}
/* Use the ESC [6n escape sequence to query the horizontal cursor position
@@ -308,7 +289,7 @@ static int getCursorPosition(void) {
/* Keep using unistd's functions. Here, using `read` instead of `fgets`
* or `fgets` guarantees us that we we can read a byte regardless on
* whether the sender sent end of line character(s) (CR, CRLF, LF). */
if (linenoiseReadBytes(in_fd, buf + i, 1) != 1 || buf[i] == 'R') {
if (read_func(in_fd, buf + i, 1) != 1 || buf[i] == 'R') {
/* If we couldn't read a byte from STDIN or if 'R' was received,
* the transmission is finished. */
break;
@@ -448,7 +429,7 @@ static int completeLine(struct linenoiseState *ls) {
refreshLine(ls);
}
nread = linenoiseReadBytes(in_fd, &c, 1);
nread = read_func(in_fd, &c, 1);
if (nread <= 0) {
freeCompletions(&lc);
return -1;
@@ -491,12 +472,6 @@ void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
hintsCallback = fn;
}
void linenoiseSetInterruptReadingData(int fd, uint64_t data)
{
intr_reading_fd = fd;
intr_reading_signal = data;
}
/* Register a function to free the hints returned by the hints callback
* registered with linenoiseSetHintsCallback(). */
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
@@ -942,7 +917,7 @@ static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
* about 40ms (or even more)
*/
t1 = getMillis();
int nread = linenoiseReadBytes(in_fd, &c, 1);
int nread = read_func(in_fd, &c, 1);
if (nread <= 0) {
return l.len;
}
@@ -1047,14 +1022,14 @@ static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
case ESC: { /* escape sequence */
/* ESC [ sequences. */
char seq[3];
int r = linenoiseReadBytes(in_fd, seq, 2);
int r = read_func(in_fd, seq, 2);
if (r != 2) {
return -1;
}
if (seq[0] == '[') {
if (seq[1] >= '0' && seq[1] <= '9') {
/* Extended escape, read additional byte. */
r = linenoiseReadBytes(in_fd, seq + 2, 1);
r = read_func(in_fd, seq + 2, 1);
if (r != 1) {
return -1;
}
@@ -1115,11 +1090,13 @@ void linenoiseAllowEmpty(bool val) {
}
int linenoiseProbe(void) {
/* Switch to non-blocking mode */
linenoiseSetReadCharacteristics();
/* Make sure we are in non blocking mode */
int stdin_fileno = fileno(stdin);
int flags = fcntl(stdin_fileno, F_GETFL);
flags |= O_NONBLOCK;
int res = fcntl(stdin_fileno, F_SETFL, flags);
int old_flags = fcntl(stdin_fileno, F_GETFL);
int new_flags = old_flags | O_NONBLOCK;
int res = fcntl(stdin_fileno, F_SETFL, new_flags);
if (res != 0) {
return -1;
}
@@ -1146,6 +1123,12 @@ int linenoiseProbe(void) {
read_bytes += cb;
}
/* Switch back to whatever mode we had before the function call */
res = fcntl(stdin_fileno, F_SETFL, old_flags);
if (res != 0) {
return -1;
}
if (read_bytes < 4) {
return -2;
}
@@ -1177,7 +1160,7 @@ static int linenoiseDumb(char* buf, size_t buflen, const char* prompt) {
while (count < buflen) {
int nread = linenoiseReadBytes(in_fd, &c, 1);
int nread = read_func(in_fd, &c, 1);
if (nread < 0) {
return nread;
}

View File

@@ -43,6 +43,8 @@
extern "C" {
#endif
#include <sys/types.h>
#include <unistd.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -58,7 +60,6 @@ typedef void(linenoiseFreeHintsCallback)(void *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseSetInterruptReadingData(int, uint64_t);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
int linenoiseProbe(void);
@@ -77,6 +78,10 @@ void linenoisePrintKeyCodes(void);
void linenoiseAllowEmpty(bool);
int linenoiseSetMaxLineLen(size_t len);
typedef ssize_t (*linenoise_read_bytes_fn)(int, void*, size_t);
void linenoiseSetReadFunction(linenoise_read_bytes_fn read_fn);
void linenoiseSetReadCharacteristics(void);
#ifdef __cplusplus
}
#endif

View File

@@ -47,9 +47,9 @@ typedef struct {
void esp_console_repl_task(void *args);
esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_com_t *repl_com);
void esp_console_common_deinit(esp_console_repl_com_t *repl_com);
esp_err_t esp_console_common_deinit(esp_console_repl_com_t *repl_com);
esp_err_t esp_console_internal_set_event_fd(esp_console_repl_com_t *repl_com);
esp_err_t esp_console_setup_prompt(const char *prompt, esp_console_repl_com_t *repl_com);
esp_err_t esp_console_setup_history(const char *history_path,
uint32_t max_history_len,
esp_console_repl_com_t *repl_com);
esp_err_t esp_console_interrupt_reading(void);

View File

@@ -12,6 +12,7 @@
#include "linenoise/linenoise.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
/*
* NOTE: Most of these unit tests DO NOT work standalone. They require pytest to control
@@ -24,6 +25,8 @@
* https://docs.espressif.com/projects/esp-idf/en/latest/esp32/contribute/esp-idf-tests-with-pytest.html.
*/
extern void set_leak_threshold(int threshold);
typedef struct {
const char *in;
const char *out;
@@ -123,12 +126,17 @@ TEST_CASE("esp console init/deinit with context test", "[console]")
TEST_ESP_OK(esp_console_deinit());
}
static bool can_terminate = false;
static SemaphoreHandle_t s_test_console_mutex = NULL;
static StaticSemaphore_t s_test_console_mutex_buf;
/* handle 'quit' command */
static int do_cmd_quit(int argc, char **argv)
{
printf("ByeBye\r\n");
TEST_ESP_OK(s_repl->del(s_repl));
linenoiseHistoryFree(); // Free up memory
TEST_ASSERT_NOT_NULL(s_test_console_mutex);
xSemaphoreTake(s_test_console_mutex, portMAX_DELAY);
can_terminate = true;
xSemaphoreGive(s_test_console_mutex);
return 0;
}
@@ -144,6 +152,11 @@ static esp_console_cmd_t s_quit_cmd = {
ran separately in test_console_repl */
TEST_CASE("esp console repl test", "[console][ignore]")
{
set_leak_threshold(400);
s_test_console_mutex = xSemaphoreCreateMutexStatic(&s_test_console_mutex_buf);
TEST_ASSERT_NOT_NULL(s_test_console_mutex);
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl));
@@ -151,10 +164,29 @@ TEST_CASE("esp console repl test", "[console][ignore]")
TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd));
TEST_ESP_OK(esp_console_start_repl(s_repl));
vTaskDelay(pdMS_TO_TICKS(2000));
bool temp_can_terminate = false;
do {
xSemaphoreTake(s_test_console_mutex, portMAX_DELAY);
temp_can_terminate = can_terminate;
xSemaphoreGive(s_test_console_mutex);
} while (temp_can_terminate != true);
/* Wait to make sure the command has finished its execution */
vTaskDelay(pdMS_TO_TICKS(500));
esp_err_t ret = esp_console_stop_repl(s_repl);
TEST_ESP_OK(ret);
xSemaphoreTake(s_test_console_mutex, portMAX_DELAY);
can_terminate = false;
xSemaphoreGive(s_test_console_mutex);
vTaskDelay(pdMS_TO_TICKS(500));
printf("ByeBye\r\n");
}
extern void set_leak_threshold(int threshold);
TEST_CASE("esp console repl deinit", "[console][ignore]")
{
set_leak_threshold(400);
@@ -168,14 +200,12 @@ TEST_CASE("esp console repl deinit", "[console][ignore]")
/* wait to make sure the task reaches linenoiseEdit function
* and gets stuck in the select */
vTaskDelay(pdMS_TO_TICKS(10));
vTaskDelay(pdMS_TO_TICKS(500));
/* call the delete function, this returns only when the repl task terminated */
const esp_err_t res = s_repl->del(s_repl);
const esp_err_t res = esp_console_stop_repl(s_repl);
/* wait to make sure the task reaches linenoiseEdit function
* and gets stuck in the select */
vTaskDelay(pdMS_TO_TICKS(10));
vTaskDelay(pdMS_TO_TICKS(500));
/* if this point is reached, the repl environment has been deleted successfully */
TEST_ASSERT(res == ESP_OK);

View File

@@ -51,6 +51,14 @@ Linenoise library does not need explicit initialization. However, some configura
Set maximum length of the line for linenoise library. Default length is 4096 bytes. The default value can be updated to optimize RAM memory usage.
- :cpp:func:`linenoiseSetReadFunction`
Set the read function to be used by linenoise.
- :cpp:func:`linenoiseSetReadCharacteristics`
Set the characteristics of the read file descriptor (e.g., blocking or non blocking mode). The function has a weak definition in linenoise.c that can be overridden
by providing a strong definition of the function.
Main Loop
^^^^^^^^^

View File

@@ -274,7 +274,7 @@ static int console_exit(int argc, char **argv)
tinyusb_msc_storage_deinit();
tinyusb_driver_uninstall();
printf("Application Exit\n");
repl->del(repl);
esp_console_stop_repl(repl);
return 0;
}

View File

@@ -201,24 +201,6 @@ static void register_ping(void)
static esp_console_repl_t *s_repl = NULL;
/* handle 'quit' command */
static int do_cmd_quit(int argc, char **argv)
{
printf("ByeBye\r\n");
s_repl->del(s_repl);
return 0;
}
static esp_err_t register_quit(void)
{
esp_console_cmd_t command = {
.command = "quit",
.help = "Quit REPL environment",
.func = &do_cmd_quit
};
return esp_console_cmd_register(&command);
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init());
@@ -256,8 +238,6 @@ void app_main(void)
/* register command `ping` */
register_ping();
/* register command `quit` */
register_quit();
// start console REPL
ESP_ERROR_CHECK(esp_console_start_repl(s_repl));