mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-08 04:02:27 +00:00
167 lines
5.6 KiB
C
167 lines
5.6 KiB
C
/*
|
|
* 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);
|
|
}
|