1 /*
2  * Copyright (C) 2015-2020 Alibaba Group Holding Limited
3  */
4 
5 #include <aos/errno.h>
6 #include <aos/kernel.h>
7 #include <stdarg.h>
8 #include <stddef.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 
12 #include "amp_task.h"
13 #include "aos/init.h"
14 #include "aos/vfs.h"
15 #include "aos_hal_uart.h"
16 #include "board.h"
17 #include "board_mgr.h"
18 #include "cJSON.h"
19 #include "mpstdinport.h"
20 #include "netmgr.h"
21 #include "py/compile.h"
22 #include "py/gc.h"
23 #include "py/mperrno.h"
24 #include "py/mphal.h"
25 #include "py/repl.h"
26 #include "py/runtime.h"
27 #include "py/stackctrl.h"
28 #include "py_defines.h"
29 #include "shared/runtime/pyexec.h"
30 #include "sys/stat.h"
31 #include "ulog/ulog.h"
32 
33 #if AOS_COMP_CLI
34 #include "aos/cli.h"
35 #endif
36 #include "haas_main.h"
37 #include "miniunz.h"
38 
39 #define LOG_TAG "haas_main"
40 
41 static int8_t *stack_top = NULL;
42 static bool isPythonRunning = false;
43 
44 #if MICROPY_ENABLE_GC
45 static int8_t *heap = NULL;
46 #ifndef MICROPY_GC_HEAP_SIZE
47 #define MICROPY_GC_HEAP_SIZE (1024 * 64)
48 #endif
49 #endif
50 
alloc_mpy_thread_args(int32_t argc,int8_t ** argv)51 static mpy_thread_args *alloc_mpy_thread_args(int32_t argc, int8_t **argv)
52 {
53     mpy_thread_args *temp_args = (mpy_thread_args *)calloc(1, sizeof(mpy_thread_args));
54     if (temp_args == NULL) {
55         LOGE(LOG_TAG, "%s;malloc mpy_thread_args failed\n", __func__);
56         return NULL;
57     }
58 
59     if (argc == 1) {
60         temp_args->is_repl_mode = true;
61     } else {
62         temp_args->is_repl_mode = false;
63     }
64     temp_args->argc = argc;
65 
66     temp_args->argv = (int8_t **)calloc(1, sizeof(int8_t *) * temp_args->argc);
67     if (temp_args->argv == NULL) {
68         LOGE(LOG_TAG, "%s:temp_args->argv alloc memory failed\n", __func__);
69         free(temp_args);
70         return NULL;
71     }
72 
73     for (int32_t i = 0; i < argc; i++) {
74         temp_args->argv[i] = strdup(argv[i]);
75     }
76 
77     return temp_args;
78 }
79 
free_mpy_thread_args(mpy_thread_args * args)80 static int32_t free_mpy_thread_args(mpy_thread_args *args)
81 {
82     if (args == NULL) {
83         LOGE(LOG_TAG, "args is illegal\n");
84         return -1;
85     }
86 
87     for (int32_t i = 0; i < args->argc; i++) {
88         if (args->argv[i] != NULL) {
89             free(args->argv[i]);
90         }
91     }
92 
93     free(args->argv);
94     free(args);
95     return 0;
96 }
97 
is_file_exist(const int8_t * path)98 static bool is_file_exist(const int8_t *path)
99 {
100     if (path == NULL) {
101         LOGE(LOG_TAG, "File path null\n");
102         return false;
103     }
104 
105     FILE *fd = fopen(path, "r");
106     if (fd != NULL) {
107         fclose(fd);
108         return true;
109     }
110 
111     return false;
112 }
113 
is_mainpy_exist()114 static uint8_t *is_mainpy_exist()
115 {
116     /* check whether main/pyamp/main.py */
117     FILE *fd = fopen(AMP_PY_ENTRY_DEFAULE, "r");
118     if (fd != NULL) {
119         printf(" ==== python execute from %s ====\n", AMP_PY_ENTRY_DEFAULE);
120         fclose(fd);
121         return AMP_PY_ENTRY_DEFAULE;
122     }
123 
124     fd = fopen(AMP_PY_ENTRY_BAK, "r");
125     if (fd != NULL) {
126         printf(" ==== python execute from %s ====\n", AMP_PY_ENTRY_BAK);
127         fclose(fd);
128         return AMP_PY_ENTRY_BAK;
129     }
130 
131     return NULL;
132 }
133 
net_init()134 static void net_init()
135 {
136 #define WIFI_DEV_PATH "/dev/wifi0"
137 
138     int8_t *wificonf = MP_FS_ROOT_DIR "/wifi.conf";
139     if (is_file_exist(wificonf) == true) {
140         event_service_init(NULL);
141         netmgr_service_init(NULL);
142         netmgr_set_auto_reconnect(0, true);
143         netmgr_wifi_set_auto_save_ap(true);
144 
145         netmgr_add_dev(WIFI_DEV_PATH);
146         netmgr_hdl_t hdl = netmgr_get_dev(WIFI_DEV_PATH);
147     } else {
148         // need init event service because of eth net
149         event_service_init(NULL);
150     }
151 }
152 
153 #include "extmod/vfs.h"
154 #include "extmod/vfs_posix.h"
155 // Try to mount the data on "/data" and chdir to it for the boot-up directory.
mount_fs(int8_t * mount_point_str)156 static int32_t mount_fs(int8_t *mount_point_str)
157 {
158     mp_obj_t mount_point = mp_obj_new_str(mount_point_str, strlen(mount_point_str));
159     mp_obj_t bdev = mp_type_vfs_posix.make_new(&mp_type_vfs_posix, 0, 0, NULL);
160     int32_t ret = mp_vfs_mount_and_chdir_protected(bdev, mount_point);
161     if (ret != 0) {
162         printf("mount_fs failed with mount_point: %s\n", mount_point_str);
163         return -MP_ENOENT;
164     }
165     return 0;
166 }
167 
py_engine_mainloop(void * p)168 static void py_engine_mainloop(void *p)
169 {
170     mpy_thread_args *args = (mpy_thread_args *)p;
171     if (args == NULL) {
172         LOGE(LOG_TAG, "%s:args is illegal\n", __func__);
173         return;
174     }
175 
176     int32_t ret = mpy_init(args);
177     if (ret) {
178         LOGE(LOG_TAG, "%s:mpy_init failed, ret=%d\n", __func__, ret);
179     } else {
180         /* Suspend CLI to make sure REPL use UART exclusively */
181         aos_cli_suspend();
182 
183         isPythonRunning = true;
184         mpy_run(args->argc, args->argv);
185 
186         /* Resume CLI after REPL */
187         aos_cli_resume();
188     }
189 
190     free_mpy_thread_args(args);
191     mpy_deinit();
192     isPythonRunning = false;
193 }
194 
py_engine_task(void * p)195 static void py_engine_task(void *p)
196 {
197     py_task_init();
198     while (1) {
199         /* loop for asynchronous operation */
200         if (py_task_yield(200) == 1) {
201             printf("pyengine task yield exit! \r\n");
202             break;
203         }
204     }
205     aos_task_exit(0);
206 }
207 
208 #if PY_BUILD_CHANNEL
network_func(void * argv)209 static void network_func(void *argv)
210 {
211     while (!aos_get_network_status()) {
212         aos_msleep(1000);
213     }
214     py_app_management_center_init();
215     aos_task_exit(0);
216     return;
217 }
218 #endif
219 
python_entry(int32_t argc,int8_t ** argv)220 static void python_entry(int32_t argc, int8_t **argv)
221 {
222     if (isPythonRunning == true) {
223         printf(" **************************************************** \r\n");
224         printf(" ** Python is running, cannot start another engine ** \r\n");
225         printf(" **************************************************** \r\n");
226 
227         return;
228     }
229 
230     printf(" Welcome to MicroPython \n");
231 
232     aos_task_t engine_entry = NULL;
233     mpy_thread_args *args = alloc_mpy_thread_args(argc, argv);
234 
235     int32_t ret =
236         aos_task_new_ext(&engine_entry, "py_engine", py_engine_mainloop, args, 1024 * 20, AOS_DEFAULT_APP_PRI);
237     if (ret != 0) {
238         LOGE(LOG_TAG, "py_engine_mainloop task creat failed!");
239         return;
240     }
241 }
242 
get_logLevel()243 static aos_log_level_t get_logLevel()
244 {
245     int32_t ret = -1;
246     int8_t logLevelStr[16] = { 0 };
247     int8_t *data_root_path = AMP_FS_ROOT_DIR "/board.json";
248     int8_t *board_json_path = MP_FS_ROOT_DIR "/python-apps/driver/board.json";
249 
250     FILE *json_fd = fopen(data_root_path, "r");
251     if (json_fd != NULL) {
252         fclose(json_fd);
253         board_json_path = data_root_path;
254     }
255 
256     int8_t *json_buff = board_get_json_buff(board_json_path);
257     if (NULL == json_buff) {
258         goto debugLevel_kv; /* get debugLevel from kv */
259     }
260 
261     cJSON *root = cJSON_Parse(json_buff);
262     if (NULL == root) {
263         goto debugLevel_kv; /* get debugLevel from kv */
264     }
265 
266     cJSON *debug = cJSON_GetObjectItem(root, APP_CONFIG_DEBUG);
267     if (debug != NULL) {
268         if (!cJSON_IsString(debug)) {
269             goto debugLevel_kv;
270         } else {
271             strcpy(logLevelStr, debug->valuestring);
272             goto debugLevel_parser;
273         }
274     }
275 
276 debugLevel_kv:
277     if (json_buff != NULL) {
278         aos_free(json_buff);
279     }
280 
281     int32_t len = sizeof(logLevelStr);
282     ret = aos_kv_get(APP_CONFIG_DEBUG, logLevelStr, &len);
283     if (ret != 0) {
284         uint8_t *set_value = "ERROR";
285         aos_kv_set(APP_CONFIG_DEBUG, set_value, strlen(set_value), 1);
286         strcpy(logLevelStr, set_value);
287     }
288 
289 debugLevel_parser:
290     if (strcmp(logLevelStr, "DEBUG") == 0) {
291         return AOS_LL_DEBUG;
292     } else if (strcmp(logLevelStr, "INFO") == 0) {
293         return AOS_LL_INFO;
294     } else if (strcmp(logLevelStr, "WARN") == 0) {
295         return AOS_LL_WARN;
296     } else if (strcmp(logLevelStr, "ERROR") == 0) {
297         return AOS_LL_ERROR;
298     } else if (strcmp(logLevelStr, "FATAL") == 0) {
299         return AOS_LL_FATAL;
300     } else if (strcmp(logLevelStr, "NONE") == 0) {
301         return AOS_LL_NONE;
302     }
303     return AOS_LL_ERROR;
304 }
305 
haas_main(int32_t argc,int8_t ** argv)306 void haas_main(int32_t argc, int8_t **argv)
307 {
308     int32_t ret = -1;
309 
310 #if PY_BUILD_BOOT
311     aos_cli_suspend();
312     pyamp_boot_main();
313     aos_cli_resume();
314 #endif
315 
316 #if PY_BUILD_KV
317     /* kv module init */
318     ret = kv_init();
319     if (ret != 0) {
320         LOGE(LOG_TAG, "kv init failed!");
321         return;
322     }
323 #endif
324 
325     /* ulog module init */
326     ulog_init();
327     aos_log_level_t log_level = get_logLevel();
328     aos_set_log_level(log_level);
329 
330     /* net init */
331     net_init();
332 
333     aos_task_t engine_task;
334     ret = aos_task_new_ext(&engine_task, "py_engine_task", py_engine_task, NULL, 1024 * 8, AOS_DEFAULT_APP_PRI);
335     if (ret != 0) {
336         LOGE(LOG_TAG, "pyengine task creat failed!");
337         return;
338     }
339 
340 #if PY_BUILD_CHANNEL
341     aos_task_t network_task;
342     /* network start */
343     ret = aos_task_new_ext(&network_task, "mpy_network", network_func, NULL, 1024 * 4, AOS_DEFAULT_APP_PRI);
344     if (ret != 0) {
345         LOGE(LOG_TAG, "network task creat failed!");
346         return ret;
347     }
348 #endif
349 
350     /* Check whether we have main.py to execute */
351     uint8_t *path = is_mainpy_exist();
352     if (path != NULL) {
353         uint8_t *argv[2] = { "python", path };
354         python_entry(2, &argv);
355     }
356 }
357 
handle_unzip_cmd(int32_t argc,int8_t ** argv)358 static void handle_unzip_cmd(int32_t argc, int8_t **argv)
359 {
360     int8_t *zippath;
361     int8_t *destpath;
362     int32_t ret = 0;
363     if (argc < 2) {
364         LOGE(LOG_TAG, "Error params,Usage: unzip /data/src.zip  /sdcard \r\n");
365         return;
366     }
367 
368     if (argc == 2) {
369         destpath = "/data";
370     } else {
371         destpath = argv[2];
372     }
373     zippath = argv[1];
374     ret = miniUnzip(zippath, destpath);
375     if (ret) {
376         LOGE(LOG_TAG, "unzip failed ,errno is %d \r\n", ret);
377     } else {
378         LOGD(LOG_TAG, "unzip succeed \r\n");
379     }
380 }
381 
382 #if MICROPY_ENABLE_COMPILER
do_str(const int8_t * src,mp_parse_input_kind_t input_kind)383 void do_str(const int8_t *src, mp_parse_input_kind_t input_kind)
384 {
385     nlr_buf_t nlr;
386     if (nlr_push(&nlr) == 0) {
387         mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
388         qstr source_name = lex->source_name;
389         mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
390         mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true);
391         mp_call_function_0(module_fun);
392         nlr_pop();
393     } else {
394         // uncaught exception
395         mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
396     }
397 }
398 #endif
399 
mpy_init(mpy_thread_args * args)400 int32_t mpy_init(mpy_thread_args *args)
401 {
402     int32_t ret = -1;
403     uint32_t stack_size = 1024;
404 
405     mp_stdin_init(MP_REPL_UART_PORT, MP_REPL_UART_BAUDRATE);
406 
407 #if MICROPY_PY_THREAD
408     void *stack_addr = mp_sal_get_stack_addr();
409     stack_size = mp_sal_get_stack_size();
410     mp_thread_init(stack_addr, stack_size);
411 #endif
412 
413     mp_stack_set_top(stack_top);
414 
415     // adjust the stack_size to provide room to recover from hitting the
416     // limit
417 #if MICROPY_STACK_CHECK
418     mp_stack_ctrl_init();
419     mp_stack_set_limit(stack_size * sizeof(cpu_stack_t) - 1024);
420 #endif
421 
422 #if MICROPY_ENABLE_GC
423     heap = (int8_t *)malloc(MICROPY_GC_HEAP_SIZE);
424     if (NULL == heap) {
425         LOGE(LOG_TAG, "mpy_init: heap alloc fail!\r\n");
426         return -1;
427     }
428     gc_init(heap, heap + MICROPY_GC_HEAP_SIZE);
429 #endif
430 
431     mp_init();
432 
433 #if MICROPY_VFS_POSIX
434     {
435         // Mount the host FS at the root of our internal VFS
436         ret = mount_fs("/");
437         if (ret != 0) {
438             printf(" !!!!!!!! %s, %d, faild to mount fs !!!!!!!!\n", __func__, __LINE__);
439         }
440         MP_STATE_VM(vfs_cur) = MP_STATE_VM(vfs_mount_table);
441     }
442 #endif
443 
444     /*set default mp_sys_path*/
445     mp_obj_list_init(mp_sys_path, 0);
446     mp_obj_list_append(mp_sys_path,
447                        MP_OBJ_NEW_QSTR(MP_QSTR_));  // current dir (or base dir of the script)
448 
449     int8_t *path = MP_FS_ROOT_DIR "/pyamp";
450     mp_obj_list_append(mp_sys_path, mp_obj_new_str_via_qstr(path, strlen(path)));
451 
452     path = MP_FS_ROOT_DIR "/lib/micropython";
453     mp_obj_list_append(mp_sys_path, mp_obj_new_str_via_qstr(path, strlen(path)));
454 
455     path = MP_FS_ROOT_DIR "/lib/micropython/uasyncio";
456     mp_obj_list_append(mp_sys_path, mp_obj_new_str_via_qstr(path, strlen(path)));
457 
458     path = MP_FS_EXT_ROOT_DIR "/lib/micropython";
459     mp_obj_list_append(mp_sys_path, mp_obj_new_str_via_qstr(path, strlen(path)));
460 
461     mp_obj_list_init(mp_sys_argv, 0);
462 
463     return 0;
464 }
465 
mpy_deinit(void)466 int32_t mpy_deinit(void)
467 {
468     // need relese python run mem
469 #if MICROPY_ENABLE_GC
470     if (NULL != heap) {
471         free(heap);
472         heap == NULL;
473     }
474 #endif
475 
476     mp_stdin_deinit();
477 }
478 
set_sys_argv(int8_t * argv[],int32_t argc,int32_t start_arg)479 static void set_sys_argv(int8_t *argv[], int32_t argc, int32_t start_arg)
480 {
481     for (int32_t i = start_arg; i < argc; i++) {
482         mp_obj_list_append(mp_sys_argv, MP_OBJ_NEW_QSTR(qstr_from_str(argv[i])));
483     }
484 }
485 
is_download_mode(uint32_t wait_time_ms)486 int32_t is_download_mode(uint32_t wait_time_ms)
487 {
488     int32_t ret = 0;
489     int32_t c = 0;
490     int64_t now_time = 0;
491     int64_t begin_time = aos_now_ms();
492 
493     do {
494         int32_t c = ringbuf_get(&stdin_ringbuf);
495         if (c == '\x03') {
496             ret = 1;
497             break;
498         }
499         now_time = aos_now_ms();
500         uint32_t time = now_time - begin_time;
501         if (time >= wait_time_ms) {
502             ret = 0;
503             break;
504         }
505         pyamp_boot_delay(1);
506     } while (1);
507 
508     return ret;
509 }
510 
mpy_run(int32_t argc,int8_t * argv[])511 int32_t mpy_run(int32_t argc, int8_t *argv[])
512 {
513     int32_t mode = 0;
514 #if 0
515     mode = is_download_mode(300); /* Only available on HaaS506 */
516 #endif
517 
518     if (argc >= 2 && mode == 0) {
519         int8_t *filename = argv[1];
520         int8_t filepath[256] = { 0 };
521 
522         if (filename[0] != '/') {
523             getcwd(filepath, sizeof(filepath));
524             strcat(filepath, "/");
525             strcat(filepath, filename);
526         } else {
527             strcpy(filepath, filename);
528         }
529 
530         int8_t *p = strrchr(filepath, '/');
531         size_t len = 0;
532         mp_obj_t *path_items = NULL;
533         mp_obj_list_get(mp_sys_path, &len, &path_items);
534 
535         if (p >= filepath) {
536             path_items[0] = mp_obj_new_str_via_qstr(filepath, p - filepath);
537         } else {
538             path_items[0] = mp_obj_new_str_via_qstr(filepath, filepath - p);
539         }
540 
541         set_sys_argv(argv, argc, 1);
542 
543         if (filepath != NULL) {
544             pyexec_file(filepath);
545         }
546     }
547     {
548         for (;;) {
549             if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
550                 if (pyexec_raw_repl() != 0) {
551                     break;
552                 }
553             } else {
554                 if (pyexec_friendly_repl() != 0) {
555                     break;
556                 }
557             }
558         }
559     }
560     mp_deinit();
561     return 0;
562 }
563 
mp_lexer_new_from_file(const char * filename)564 MP_WEAK mp_lexer_t *mp_lexer_new_from_file(const char *filename)
565 {
566     mp_raise_OSError(MP_ENOENT);
567 }
568 
nlr_jump_fail(void * val)569 void nlr_jump_fail(void *val)
570 {
571     while (1)
572         ;
573 }
574 
__fatal_error(const int8_t * msg)575 void NORETURN __fatal_error(const int8_t *msg)
576 {
577     while (1)
578         ;
579 }
580 
581 #ifndef NDEBUG
__assert_func(const char * file,int line,const char * func,const char * expr)582 void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr)
583 {
584     printf("assert:%s:%d:%s: %s\n", file, line, func, expr);
585     mp_raise_msg(&mp_type_AssertionError, MP_ERROR_TEXT("C-level assert"));
586 }
587 #endif
588 
589 #ifdef AOS_COMP_CLI
590 #if BOARD_HAAS700
591 #include <console.h>
592 #endif
593 /* reg args: fun, cmd, description*/
594 ALIOS_CLI_CMD_REGISTER(python_entry, python, start micropython)
595 ALIOS_CLI_CMD_REGISTER(handle_unzip_cmd, unzip, start unzip)
596 #endif
597