mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-31 04:59:55 +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);
 | |
| }
 | 
