Skip to content

Commit b6992a5

Browse files
committed
feat(esp_repl): Implement the component logic
1 parent 90799c1 commit b6992a5

File tree

2 files changed

+347
-0
lines changed

2 files changed

+347
-0
lines changed

esp_repl/esp_repl.c

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,180 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66
#include <stdbool.h>
7+
#include "freertos/FreeRTOS.h"
8+
#include "freertos/semphr.h"
79
#include "esp_repl.h"
810
#include "esp_err.h"
911

12+
typedef enum {
13+
ESP_REPL_STATE_RUNNING,
14+
ESP_REPL_STATE_STOPPED
15+
} esp_repl_state_e;
16+
17+
typedef struct esp_repl_state {
18+
esp_repl_state_e state;
19+
SemaphoreHandle_t mux;
20+
} esp_repl_state_t;
21+
22+
typedef struct esp_repl_instance {
23+
esp_repl_instance_handle_t self;
24+
esp_repl_config_t config;
25+
esp_repl_state_t state;
26+
} esp_repl_instance_t;
27+
28+
#define ESP_REPL_CHECK_INSTANCE(handle) do { \
29+
if((handle == NULL) || ((esp_repl_instance_t*)handle->self != (esp_repl_instance_t*)handle)) { \
30+
return ESP_ERR_INVALID_ARG; \
31+
} \
32+
} while(0)
33+
34+
esp_err_t esp_repl_create(esp_repl_instance_handle_t *handle, const esp_repl_config_t *config)
35+
{
36+
if ((config->executor.func == NULL) ||
37+
(config->reader.func == NULL) ||
38+
(config->max_cmd_line_size == 0)) {
39+
return ESP_ERR_INVALID_ARG;
40+
}
41+
42+
esp_repl_instance_t *instance = malloc(sizeof(esp_repl_instance_t));
43+
if (!instance) {
44+
return ESP_ERR_NO_MEM;
45+
}
46+
47+
instance->config = *config;
48+
instance->state.state = ESP_REPL_STATE_STOPPED;
49+
instance->self = instance;
50+
instance->state.mux = xSemaphoreCreateMutex();
51+
if (!instance->state.mux) {
52+
free(instance);
53+
return ESP_FAIL;
54+
}
55+
56+
/* take the mutex right away to prevent the task to start running until
57+
* the user explicitly calls esp_repl_start */
58+
xSemaphoreTake(instance->state.mux, portMAX_DELAY);
59+
60+
*handle = instance;
61+
return ESP_OK;
62+
}
63+
64+
esp_err_t esp_repl_destroy(esp_repl_instance_handle_t handle)
65+
{
66+
ESP_REPL_CHECK_INSTANCE(handle);
67+
esp_repl_state_t *state = &handle->state;
68+
69+
/* the instance has to be not running for esp_repl to destroy it */
70+
if (state->state != ESP_REPL_STATE_STOPPED) {
71+
return ESP_ERR_INVALID_STATE;
72+
}
73+
74+
vSemaphoreDelete(state->mux);
75+
76+
free(handle);
77+
78+
return ESP_OK;
79+
}
80+
81+
esp_err_t esp_repl_start(esp_repl_instance_handle_t handle)
82+
{
83+
ESP_REPL_CHECK_INSTANCE(handle);
84+
esp_repl_state_t *state = &handle->state;
85+
86+
if (state->state != ESP_REPL_STATE_STOPPED) {
87+
return ESP_ERR_INVALID_STATE;
88+
}
89+
state->state = ESP_REPL_STATE_RUNNING;
90+
xSemaphoreGive(state->mux);
91+
92+
return ESP_OK;
93+
}
94+
95+
esp_err_t esp_repl_stop(esp_repl_instance_handle_t handle)
96+
{
97+
ESP_REPL_CHECK_INSTANCE(handle);
98+
esp_repl_config_t *config = &handle->config;
99+
esp_repl_state_t *state = &handle->state;
100+
101+
if (state->state != ESP_REPL_STATE_RUNNING) {
102+
return ESP_ERR_INVALID_STATE;
103+
}
104+
105+
/* update the state to force the while loop in esp_repl to return */
106+
state->state = ESP_REPL_STATE_STOPPED;
107+
108+
/* Call the on_stop callback to let the user unblock reader.func, if provided */
109+
if (config->on_stop.func != NULL) {
110+
config->on_stop.func(config->on_stop.ctx, handle);
111+
}
112+
113+
/* Wait for esp_repl() to finish and signal completion */
114+
xSemaphoreTake(state->mux, portMAX_DELAY);
115+
116+
/* give it back so destroy can also take/give symmetrically */
117+
xSemaphoreGive(state->mux);
118+
119+
return ESP_OK;
120+
}
121+
122+
void esp_repl(esp_repl_instance_handle_t handle)
123+
{
124+
if (!handle || handle->self != handle) {
125+
return;
126+
}
127+
128+
esp_repl_config_t *config = &handle->config;
129+
esp_repl_state_t *state = &handle->state;
130+
131+
/* allocate memory for the command line buffer */
132+
const size_t cmd_line_size = config->max_cmd_line_size;
133+
char *cmd_line = calloc(1, cmd_line_size);
134+
if (!cmd_line) {
135+
return;
136+
}
137+
138+
/* Waiting for task notify. This happens when `esp_repl_start`
139+
* function is called. */
140+
xSemaphoreTake(state->mux, portMAX_DELAY);
141+
142+
/* REPL loop */
143+
while (state->state == ESP_REPL_STATE_RUNNING) {
144+
145+
/* try to read a command line */
146+
const esp_err_t read_ret = config->reader.func(config->reader.ctx, cmd_line, cmd_line_size);
147+
148+
/* forward the raw command line to the pre executor callback (e.g., save in history).
149+
* this callback is not necessary for the user to register, continue if it isn't */
150+
if (config->pre_executor.func != NULL) {
151+
config->pre_executor.func(config->pre_executor.ctx, cmd_line, read_ret);
152+
}
153+
154+
/* at this point, if the command is NULL, skip the executing part */
155+
if (read_ret != ESP_OK) {
156+
continue;
157+
}
158+
159+
/* try to run the command */
160+
int cmd_func_ret;
161+
const esp_err_t exec_ret = config->executor.func(config->executor.ctx, cmd_line, &cmd_func_ret);
162+
163+
/* forward the raw command line to the post executor callback (e.g., save in history).
164+
* this callback is not necessary for the user to register, continue if it isn't */
165+
if (config->post_executor.func != NULL) {
166+
config->post_executor.func(config->post_executor.ctx, cmd_line, exec_ret, cmd_func_ret);
167+
}
168+
169+
/* reset the cmd_line for next loop */
170+
memset(cmd_line, 0x00, cmd_line_size);
171+
}
172+
173+
/* free the memory allocated for the cmd_line buffer */
174+
free(cmd_line);
175+
176+
/* release the semaphore to indicate esp_repl_stop that the esp_repl returned */
177+
xSemaphoreGive(state->mux);
178+
179+
/* call the on_exit callback before returning from esp_repl */
180+
if (config->on_exit.func != NULL) {
181+
config->on_exit.func(config->on_exit.ctx, handle);
182+
}
183+
}

esp_repl/include/esp_repl.h

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,179 @@ extern "C" {
1313
#include "esp_err.h"
1414

1515

16+
/**
17+
* @brief Function prototype for reading input for the REPL.
18+
*
19+
* @param ctx User-defined context pointer.
20+
* @param buf Buffer to store the read data.
21+
* @param buf_size Size of the buffer in bytes.
22+
*
23+
* @return ESP_OK on success, error code otherwise.
24+
*/
25+
typedef esp_err_t (*esp_repl_reader_fn)(void *ctx, char *buf, size_t buf_size);
26+
27+
/**
28+
* @brief Reader configuration structure for the REPL.
29+
*/
30+
typedef struct esp_repl_reader {
31+
esp_repl_reader_fn func; /**!< Function to read input */
32+
void *ctx; /**!< Context passed to the reader function */
33+
} esp_repl_reader_t;
34+
35+
/**
36+
* @brief Function prototype called before executing a command.
37+
*
38+
* @param ctx User-defined context pointer.
39+
* @param buf Buffer containing the command.
40+
* @param reader_ret_val Return value from the reader function.
41+
*
42+
* @return ESP_OK to continue execution, error code to abort.
43+
*/
44+
typedef esp_err_t (*esp_repl_pre_executor_fn)(void *ctx, char *buf, const esp_err_t reader_ret_val);
45+
46+
/**
47+
* @brief Pre-executor configuration structure for the REPL.
48+
*/
49+
typedef struct esp_repl_pre_executor {
50+
esp_repl_pre_executor_fn func; /**!< Function to run before command execution */
51+
void *ctx; /**!< Context passed to the pre-executor function */
52+
} esp_repl_pre_executor_t;
53+
54+
/**
55+
* @brief Function prototype to execute a REPL command.
56+
*
57+
* @param ctx User-defined context pointer.
58+
* @param buf Null-terminated command string.
59+
* @param ret_val Pointer to store the command return value.
60+
*
61+
* @return ESP_OK on success, error code otherwise.
62+
*/
63+
typedef esp_err_t (*esp_repl_executor_fn)(void *ctx, const char *buf, int *ret_val);
64+
65+
/**
66+
* @brief Executor configuration structure for the REPL.
67+
*/
68+
typedef struct esp_repl_executor {
69+
esp_repl_executor_fn func; /**!< Function to execute commands */
70+
void *ctx; /**!< Context passed to the executor function */
71+
} esp_repl_executor_t;
72+
73+
/**
74+
* @brief Function prototype called after executing a command.
75+
*
76+
* @param ctx User-defined context pointer.
77+
* @param buf Command that was executed.
78+
* @param executor_ret_val Return value from the executor function.
79+
* @param cmd_ret_val Command-specific return value.
80+
*
81+
* @return ESP_OK on success, error code otherwise.
82+
*/
83+
typedef esp_err_t (*esp_repl_post_executor_fn)(void *ctx, const char *buf, const esp_err_t executor_ret_val, const int cmd_ret_val);
84+
85+
/**
86+
* @brief Post-executor configuration structure for the REPL.
87+
*/
88+
typedef struct esp_repl_post_executor {
89+
esp_repl_post_executor_fn func; /**!< Function called after command execution */
90+
void *ctx; /**!< Context passed to the post-executor function */
91+
} esp_repl_post_executor_t;
92+
93+
/**
94+
* @brief Function prototype called when the REPL is stopping.
95+
*
96+
* This callback allows the user to unblock the reader (or perform other
97+
* cleanup) so that the REPL can return from `esp_repl()`.
98+
*
99+
* @param ctx User-defined context pointer.
100+
* @param handle Handle to the REPL instance.
101+
*/
102+
typedef void (*esp_repl_on_stop_fn)(void *ctx, esp_linenoise_handle_t handle);
103+
104+
/**
105+
* @brief Stop callback configuration structure for the REPL.
106+
*/
107+
typedef struct esp_repl_on_stop {
108+
esp_repl_on_stop_fn func; /**!< Function called when REPL stop is requested */
109+
void *ctx; /**!< Context passed to the on_stop function */
110+
} esp_repl_on_stop_t;
111+
112+
/**
113+
* @brief Function prototype called when the REPL exits.
114+
*
115+
* @param ctx User-defined context pointer.
116+
* @param handle Handle to the REPL instance.
117+
*/
118+
typedef void (*esp_repl_on_exit_fn)(void *ctx, esp_linenoise_handle_t handle);
119+
120+
/**
121+
* @brief Exit callback configuration structure for the REPL.
122+
*/
123+
typedef struct esp_repl_on_exit {
124+
esp_repl_on_exit_fn func; /**!< Function called on REPL exit */
125+
void *ctx; /**!< Context passed to the exit function */
126+
} esp_repl_on_exit_t;
127+
128+
/**
129+
* @brief Configuration structure to initialize a REPL instance.
130+
*/
131+
typedef struct esp_repl_config {
132+
size_t max_cmd_line_size; /**!< Maximum allowed command line size */
133+
esp_repl_reader_t reader; /**!< Reader callback and context */
134+
esp_repl_pre_executor_t pre_executor; /**!< Pre-executor callback and context */
135+
esp_repl_executor_t executor; /**!< Executor callback and context */
136+
esp_repl_post_executor_t post_executor; /**!< Post-executor callback and context */
137+
esp_repl_on_stop_t on_stop; /**!< Stop callback and context */
138+
esp_repl_on_exit_t on_exit; /**!< Exit callback and context */
139+
} esp_repl_config_t;
140+
141+
/**
142+
* @brief Handle to a REPL instance.
143+
*/
144+
typedef struct esp_repl_instance esp_repl_instance_handle_t;
145+
146+
/**
147+
* @brief Create a REPL instance.
148+
*
149+
* @param handle Pointer to store the created REPL instance handle.
150+
* @param config Pointer to the configuration structure.
151+
*
152+
* @return ESP_OK on success, error code otherwise.
153+
*/
154+
esp_err_t esp_repl_create(esp_repl_instance_handle_t *handle, const esp_repl_config_t *config);
155+
156+
/**
157+
* @brief Destroy a REPL instance.
158+
*
159+
* @param handle REPL instance handle to destroy.
160+
*
161+
* @return ESP_OK on success, error code otherwise.
162+
*/
163+
esp_err_t esp_repl_destroy(esp_repl_instance_handle_t handle);
164+
165+
/**
166+
* @brief Start a REPL instance.
167+
*
168+
* @param handle REPL instance handle.
169+
*
170+
* @return ESP_OK on success, error code otherwise.
171+
*/
172+
esp_err_t esp_repl_start(esp_repl_instance_handle_t handle);
173+
174+
/**
175+
* @brief Stop a REPL instance.
176+
*
177+
* @param handle REPL instance handle.
178+
*
179+
* @return ESP_OK on success, error code otherwise.
180+
*/
181+
esp_err_t esp_repl_stop(esp_repl_instance_handle_t handle);
182+
183+
/**
184+
* @brief Run the REPL loop.
185+
*
186+
* @param handle REPL instance handle.
187+
*/
188+
void esp_repl(esp_repl_instance_handle_t handle);
16189

17190
#ifdef __cplusplus
18191
}

0 commit comments

Comments
 (0)