mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-04 00:51:42 +01: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);
 | 
						|
}
 |