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