1 /*
2 * Copyright (c) 2024, sakumisu
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "FreeRTOS.h"
7 #include "task.h"
8 #include "event_groups.h"
9 #include "csh.h"
10 #include "usbd_core.h"
11 #include "usbd_adb.h"
12 #include "chry_ringbuffer.h"
13
14 static chry_ringbuffer_t shell_rb;
15 static uint8_t mempool[1024];
16
17 #ifndef task_repl_PRIORITY
18 #define task_repl_PRIORITY (configMAX_PRIORITIES - 4U)
19 #endif
20
21 #ifndef task_exec_PRIORITY
22 #define task_exec_PRIORITY (configMAX_PRIORITIES - 5U)
23 #endif
24
25 static chry_shell_t csh;
26 static volatile bool login = false;
27
28 static StaticTask_t task_buffer_repl;
29 static StaticTask_t task_buffer_exec;
30
31 static StackType_t task_stack_repl[1024];
32 static StackType_t task_stack_exec[1024];
33
34 static TaskHandle_t task_hdl_repl = NULL;
35 static TaskHandle_t task_hdl_exec = NULL;
36
37 static EventGroupHandle_t event_hdl;
38 static StaticEventGroup_t event_grp;
39
usbd_adb_notify_shell_read(uint8_t * data,uint32_t len)40 void usbd_adb_notify_shell_read(uint8_t *data, uint32_t len)
41 {
42 chry_ringbuffer_write(&shell_rb, data, len);
43
44 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
45 xEventGroupSetBitsFromISR(event_hdl, 0x10, &xHigherPriorityTaskWoken);
46 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
47 }
48
usbd_adb_notify_write_done(void)49 void usbd_adb_notify_write_done(void)
50 {
51 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
52 xEventGroupSetBitsFromISR(event_hdl, 0x20, &xHigherPriorityTaskWoken);
53 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
54 }
55
csh_sput_cb(chry_readline_t * rl,const void * data,uint16_t size)56 static uint16_t csh_sput_cb(chry_readline_t *rl, const void *data, uint16_t size)
57 {
58 (void)rl;
59
60 if (!usb_device_is_configured(0)) {
61 return size;
62 }
63
64 if (usbd_adb_can_write() && size) {
65 usbd_abd_write(ADB_SHELL_LOALID, data, size);
66 xEventGroupWaitBits(event_hdl, 0x20, pdTRUE, pdFALSE, portMAX_DELAY);
67 }
68
69 return size;
70 }
71
csh_sget_cb(chry_readline_t * rl,void * data,uint16_t size)72 static uint16_t csh_sget_cb(chry_readline_t *rl, void *data, uint16_t size)
73 {
74 (void)rl;
75
76 return chry_ringbuffer_read(&shell_rb, data, size);
77 }
78
wait_char(void)79 static void wait_char(void)
80 {
81 EventBits_t event;
82 wait:
83 /* In order to lock the log from being disrupted , wait for REPL task execution to complete */
84 event = xEventGroupWaitBits(event_hdl, (0x10 | 0x01 | 0x04), pdTRUE, pdFALSE, portMAX_DELAY);
85 if ((event & 0x10) == 0) {
86 if (event & 0x01) {
87 chry_readline_erase_line(&csh.rl);
88 xEventGroupSetBits(event_hdl, 0x02);
89 }
90 if (event & 0x04) {
91 chry_readline_edit_refresh(&csh.rl);
92 xEventGroupSetBits(event_hdl, 0x08);
93 }
94
95 goto wait;
96 }
97 }
98
task_repl(void * param)99 static void task_repl(void *param)
100 {
101 (void)param;
102 int ret;
103 volatile uint8_t *pexec = (void *)&csh.exec;
104
105 for (;;) {
106 restart:
107 if (login) {
108 goto repl;
109 } else {
110 }
111
112 ret = csh_login(&csh);
113 if (ret == 0) {
114 login = true;
115 } else if (ret == 1) {
116 /*!< no enough char */
117 wait_char();
118 continue;
119 } else {
120 continue;
121 }
122
123 repl:
124 ret = chry_shell_task_repl(&csh);
125
126 if (ret == -1) {
127 /*!< error */
128 goto restart;
129 } else if (ret == 1) {
130 /*!< no enough char */
131 wait_char();
132 } else {
133 /*!< restart */
134 }
135
136 /*!< check flag */
137 if (*pexec == CSH_STATUS_EXEC_DONE) {
138 *pexec = CSH_STATUS_EXEC_IDLE;
139 chry_readline_auto_refresh(&csh.rl, true);
140 chry_readline_ignore(&csh.rl, false);
141 chry_readline_edit_refresh(&csh.rl);
142 }
143
144 if (login == false) {
145 chry_readline_erase_line(&csh.rl);
146 csh.rl.noblock = false;
147 }
148 }
149 }
150
task_exec(void * param)151 static void task_exec(void *param)
152 {
153 (void)param;
154
155 /*!< execute shell command */
156 chry_shell_task_exec(&csh);
157
158 /*!< notify REPL task execute done */
159 xEventGroupSetBits(event_hdl, 0x10);
160
161 /*!< wait for REPL task delete */
162 vTaskSuspend(NULL);
163 }
164
chry_shell_port_create_context(chry_shell_t * csh,int argc,const char ** argv)165 int chry_shell_port_create_context(chry_shell_t *csh, int argc, const char **argv)
166 {
167 volatile TaskHandle_t *p_task_hdl_exec = (void *)&task_hdl_exec;
168 (void)csh;
169 (void)argc;
170 (void)argv;
171
172 if (*p_task_hdl_exec != NULL) {
173 vTaskDelete(*p_task_hdl_exec);
174 }
175
176 *p_task_hdl_exec = xTaskCreateStatic(task_exec, "task_exec", 1024U, NULL, task_exec_PRIORITY, task_stack_exec, &task_buffer_exec);
177 return 0;
178 }
179
chry_shell_port_default_handler(chry_shell_t * csh,int sig)180 void chry_shell_port_default_handler(chry_shell_t *csh, int sig)
181 {
182 volatile uint8_t *pexec = (void *)&csh->exec;
183 volatile TaskHandle_t *p_task_hdl_exec = (void *)&task_hdl_exec;
184
185 switch (sig) {
186 case CSH_SIGINT:
187 case CSH_SIGQUIT:
188 case CSH_SIGKILL:
189 case CSH_SIGTERM:
190 break;
191 default:
192 return;
193 }
194
195 /*!< force delete task */
196 if (*p_task_hdl_exec != NULL) {
197 vTaskDelete(task_hdl_exec);
198 *p_task_hdl_exec = NULL;
199 }
200
201 switch (sig) {
202 case CSH_SIGINT:
203 csh->rl.sput(&csh->rl, "^SIGINT" CONFIG_CSH_NEWLINE, sizeof("^SIGINT" CONFIG_CSH_NEWLINE) - 1);
204 break;
205 case CSH_SIGQUIT:
206 csh->rl.sput(&csh->rl, "^SIGQUIT" CONFIG_CSH_NEWLINE, sizeof("^SIGQUIT" CONFIG_CSH_NEWLINE) - 1);
207 break;
208 case CSH_SIGKILL:
209 csh->rl.sput(&csh->rl, "^SIGKILL" CONFIG_CSH_NEWLINE, sizeof("^SIGKILL" CONFIG_CSH_NEWLINE) - 1);
210 break;
211 case CSH_SIGTERM:
212 csh->rl.sput(&csh->rl, "^SIGTERM" CONFIG_CSH_NEWLINE, sizeof("^SIGTERM" CONFIG_CSH_NEWLINE) - 1);
213 break;
214 default:
215 return;
216 }
217
218 *pexec = CSH_STATUS_EXEC_IDLE;
219 chry_readline_auto_refresh(&csh->rl, true);
220 chry_readline_ignore(&csh->rl, false);
221 chry_readline_edit_refresh(&csh->rl);
222 }
223
shell_init(bool need_login)224 int shell_init(bool need_login)
225 {
226 chry_shell_init_t csh_init;
227
228 if (chry_ringbuffer_init(&shell_rb, mempool, sizeof(mempool))) {
229 return -1;
230 }
231
232 if (need_login) {
233 login = false;
234 } else {
235 login = true;
236 }
237
238 /*!< I/O callback */
239 csh_init.sput = csh_sput_cb;
240 csh_init.sget = csh_sget_cb;
241
242 #if defined(CONFIG_CSH_SYMTAB) && CONFIG_CSH_SYMTAB
243 extern const int __fsymtab_start;
244 extern const int __fsymtab_end;
245 extern const int __vsymtab_start;
246 extern const int __vsymtab_end;
247
248 /*!< get table from ld symbol */
249 csh_init.command_table_beg = &__fsymtab_start;
250 csh_init.command_table_end = &__fsymtab_end;
251 csh_init.variable_table_beg = &__vsymtab_start;
252 csh_init.variable_table_end = &__vsymtab_end;
253 #endif
254
255 #if defined(CONFIG_CSH_PROMPTEDIT) && CONFIG_CSH_PROMPTEDIT
256 static char csh_prompt_buffer[128];
257
258 /*!< set prompt buffer */
259 csh_init.prompt_buffer = csh_prompt_buffer;
260 csh_init.prompt_buffer_size = sizeof(csh_prompt_buffer);
261 #endif
262
263 #if defined(CONFIG_CSH_HISTORY) && CONFIG_CSH_HISTORY
264 static char csh_history_buffer[128];
265
266 /*!< set history buffer */
267 csh_init.history_buffer = csh_history_buffer;
268 csh_init.history_buffer_size = sizeof(csh_history_buffer);
269 #endif
270
271 #if defined(CONFIG_CSH_LNBUFF_STATIC) && CONFIG_CSH_LNBUFF_STATIC
272 static char csh_line_buffer[128];
273
274 /*!< set linebuffer */
275 csh_init.line_buffer = csh_line_buffer;
276 csh_init.line_buffer_size = sizeof(csh_line_buffer);
277 #endif
278
279 csh_init.uid = 0;
280 csh_init.user[0] = "cherry";
281
282 /*!< The port hash function is required,
283 and the strcmp attribute is used weakly by default,
284 int chry_shell_port_hash_strcmp(const char *hash, const char *str); */
285 csh_init.hash[0] = "12345678"; /*!< If there is no password, set to NULL */
286 csh_init.host = "cherryadb";
287 csh_init.user_data = NULL;
288
289 int ret = chry_shell_init(&csh, &csh_init);
290 if (ret) {
291 return -1;
292 }
293
294 task_hdl_exec = NULL;
295 event_hdl = xEventGroupCreateStatic(&event_grp);
296 task_hdl_repl = xTaskCreateStatic(task_repl, "task_repl", 1024U, NULL, task_repl_PRIORITY, task_stack_repl, &task_buffer_repl);
297
298 return 0;
299 }
300
shell_lock(void)301 void shell_lock(void)
302 {
303 xEventGroupSetBits(event_hdl, 0x01);
304 xEventGroupWaitBits(event_hdl, 0x02, pdTRUE, pdTRUE, portMAX_DELAY);
305 }
306
shell_unlock(void)307 void shell_unlock(void)
308 {
309 xEventGroupSetBits(event_hdl, 0x04);
310 xEventGroupWaitBits(event_hdl, 0x08, pdTRUE, pdTRUE, portMAX_DELAY);
311 }
312
csh_exit(int argc,char ** argv)313 static int csh_exit(int argc, char **argv)
314 {
315 (void)argc;
316 (void)argv;
317
318 usbd_adb_close(ADB_SHELL_LOALID);
319
320 return 0;
321 }
322 CSH_SCMD_EXPORT_ALIAS(csh_exit, exit, );
323
324 #define __ENV_PATH "/sbin:/bin"
325 const char ENV_PATH[] = __ENV_PATH;
326 CSH_RVAR_EXPORT(ENV_PATH, PATH, sizeof(__ENV_PATH));
327
328 #define __ENV_ZERO ""
329 const char ENV_ZERO[] = __ENV_ZERO;
330 CSH_RVAR_EXPORT(ENV_ZERO, ZERO, sizeof(__ENV_ZERO));
331