1 /*
2  * Copyright (C) 2015-2019 Alibaba Group Holding Limited
3  */
4 
5 #include "amp_platform.h"
6 #include "amp_task.h"
7 #include "aos_system.h"
8 #include "aos_tcp.h"
9 #include "be_inl.h"
10 #include "board_config.h"
11 #include "py_defines.h"
12 
13 #define MOD_STR              "TCP"
14 #define MAX_TCP_RECV_LEN     256
15 #define MAX_TCP_RECV_TIMEOUT 200
16 
17 typedef struct {
18     int sock_id;
19     char *msg;
20     int msg_len;
21     int js_cb_ref;
22 } tcp_send_param_t;
23 
24 typedef struct {
25     int ret;
26     int js_cb_ref;
27 } tcp_send_notify_param_t;
28 
29 typedef struct {
30     int sock_id;
31     int js_cb_ref;
32 } tcp_recv_param_t;
33 
34 typedef struct {
35     char *host;
36     int port;
37     int js_cb_ref;
38 } tcp_create_param_t;
39 
40 typedef struct {
41     int ret;
42     int js_cb_ref;
43 } tcp_create_notify_param_t;
44 
45 typedef struct {
46     char buf[MAX_TCP_RECV_LEN];
47     int recv_len;
48     int js_cb_ref;
49 } tcp_recv_notify_param_t;
50 
51 static char g_tcp_close_flag = 0;
52 static char g_tcp_recv_flag = 0;
53 static aos_sem_t g_tcp_close_sem = NULL;
54 
tcp_create_notify(void * pdata)55 static void tcp_create_notify(void *pdata)
56 {
57     tcp_create_notify_param_t *p = (tcp_create_notify_param_t *)pdata;
58     duk_context *ctx = be_get_context();
59     be_push_ref(ctx, p->js_cb_ref);
60     duk_push_int(ctx, p->ret);
61     if (duk_pcall(ctx, 1) != DUK_EXEC_SUCCESS) {
62         amp_console("%s", duk_safe_to_stacktrace(ctx, -1));
63     }
64     duk_pop(ctx);
65     be_unref(ctx, p->js_cb_ref);
66     aos_free(p);
67     duk_gc(ctx, 0);
68 }
69 
tcp_create_routine(tcp_create_param_t * create_param)70 static int tcp_create_routine(tcp_create_param_t *create_param)
71 {
72     int ret = -1;
73     tcp_create_notify_param_t *p;
74 
75     duk_context *ctx = be_get_context();
76 
77     p = aos_calloc(1, sizeof(tcp_create_notify_param_t));
78     if (!p) {
79         amp_warn(MOD_STR, "allocate memory failed");
80         be_unref(ctx, create_param->js_cb_ref);
81         goto out;
82     }
83 
84     ret = aos_tcp_establish(create_param->host, create_param->port);
85     if (ret < 0) {
86         amp_warn(MOD_STR, "tcp establish failed");
87     }
88 
89     amp_debug(MOD_STR, "sock_id = %d", ret);
90 
91     p->js_cb_ref = create_param->js_cb_ref;
92     p->ret = ret;
93     py_task_schedule_call(tcp_create_notify, p);
94 
95 out:
96 
97     return ret;
98 }
99 
100 /*************************************************************************************
101  * Function:    native_udp_create_socket
102  * Description: js native addon for UDP.createSocket();
103  * Called by:   js api
104  * Input:       none
105  * Output:      return socket fd when create socket success,
106  *            return error number when create socket fail
107  **************************************************************************************/
native_tcp_create_socket(duk_context * ctx)108 static duk_ret_t native_tcp_create_socket(duk_context *ctx)
109 {
110     int err;
111     int ret = -1;
112     int port = 0;
113     const char *host;
114     tcp_create_param_t *create_param = NULL;
115 
116     if (!duk_is_object(ctx, 0) || !duk_is_function(ctx, 1)) {
117         amp_warn(MOD_STR, "parameter must be (object, function)");
118         goto out;
119     }
120 
121     /* get device certificate */
122     duk_get_prop_string(ctx, 0, "host");
123     duk_get_prop_string(ctx, 0, "port");
124 
125     if (!duk_is_string(ctx, -2) || !duk_is_number(ctx, -1)) {
126         amp_warn(MOD_STR,
127                  "Parameter 1 must be an object like {host: string, "
128                  "port: uint}");
129         err = -2;
130         goto out;
131     }
132 
133     host = duk_get_string(ctx, -2);
134     port = duk_get_number(ctx, -1);
135 
136     amp_debug(MOD_STR, "host: %s, port: %d", host, port);
137 
138     create_param = (tcp_create_param_t *)aos_malloc(sizeof(*create_param));
139     if (!create_param) {
140         amp_error(MOD_STR, "allocate memory failed");
141         goto out;
142     }
143 
144     duk_dup(ctx, 1);
145     create_param->host = host;
146     create_param->port = port;
147     create_param->js_cb_ref = be_ref(ctx);
148 
149     ret = tcp_create_routine(create_param);
150     if (ret < 0) {
151         amp_warn(MOD_STR, "tcp create socket failed");
152         goto out;
153     }
154 
155 out:
156     if (create_param) {
157         aos_free(create_param);
158     }
159     duk_push_int(ctx, ret);
160     return 1;
161 }
162 
tcp_send_notify(void * pdata)163 static void tcp_send_notify(void *pdata)
164 {
165     tcp_send_notify_param_t *p = (tcp_send_notify_param_t *)pdata;
166     duk_context *ctx = be_get_context();
167     be_push_ref(ctx, p->js_cb_ref);
168     duk_push_int(ctx, p->ret);
169     if (duk_pcall(ctx, 1) != DUK_EXEC_SUCCESS) {
170         amp_console("%s", duk_safe_to_stacktrace(ctx, -1));
171     }
172     duk_pop(ctx);
173     be_unref(ctx, p->js_cb_ref);
174     aos_free(p);
175     duk_gc(ctx, 0);
176 }
177 
178 /*************************************************************************************
179  * Function:    udp_send_routin
180  * Description: create a task for blocking sendto call
181  * Called by:
182  **************************************************************************************/
tcp_send_routine(tcp_send_param_t * send_param)183 static int tcp_send_routine(tcp_send_param_t *send_param)
184 {
185     int ret = -1;
186     tcp_send_notify_param_t *p;
187     int sock_id;
188 
189     duk_context *ctx = be_get_context();
190 
191     sock_id = send_param->sock_id;
192 
193     ret = aos_tcp_write(sock_id, send_param->msg, send_param->msg_len, 0);
194     p = aos_calloc(1, sizeof(tcp_send_notify_param_t));
195     if (!p) {
196         amp_warn(MOD_STR, "allocate memory failed");
197         be_unref(ctx, send_param->js_cb_ref);
198         goto out;
199     }
200 
201     p->js_cb_ref = send_param->js_cb_ref;
202     p->ret = ret;
203     py_task_schedule_call(tcp_send_notify, p);
204     ret = 0;
205 
206 out:
207     aos_free(send_param->msg);
208     aos_free(send_param);
209     return ret;
210 }
211 
212 /*************************************************************************************
213  * Function:    native_udp_sendto
214  * Description: js native addon for
215  *UDP.send(sock_id,option,buffer_array,function(ret){}) Called by:   js api
216  * Input:       sock_id: interger
217  *            options: is a object include options.ip and options.port
218  *            buffer_array: is a array which include message to send
219  *            function(ret): is the callback function which has a ret input
220  *param Output:      return send msg length when send success return error
221  *number when send fail
222  **************************************************************************************/
223 
native_tcp_send(duk_context * ctx)224 static duk_ret_t native_tcp_send(duk_context *ctx)
225 {
226     int ret = -1;
227     int sock_id;
228     int i;
229     int msg_len;
230     char *msg;
231 
232     if (!duk_is_number(ctx, 0) || !duk_is_array(ctx, 1) ||
233         !duk_is_function(ctx, 2)) {
234         amp_warn(MOD_STR, "parameter must be (number, string, function)");
235         goto out;
236     }
237 
238     sock_id = duk_get_int(ctx, 0);
239     if (sock_id <= 0) {
240         amp_warn(MOD_STR, "socket id[%d] is invalid", sock_id);
241         goto out;
242     }
243 
244     msg_len = duk_get_length(ctx, 1);
245     msg = (char *)aos_malloc(msg_len + 1);
246     if (!msg) {
247         amp_warn(MOD_STR, "allocate memory failed");
248         goto out;
249     }
250     for (i = 0; i < msg_len; i++) {
251         duk_get_prop_index(ctx, 1, i);
252         msg[i] = duk_get_int(ctx, -1);
253         duk_pop(ctx);
254     }
255     msg[msg_len] = 0;
256 
257     // const char *send_buf = duk_get_string(ctx, 1);
258     // msg_len = strlen(send_buf);
259     // msg     = (char *)aos_malloc(msg_len);
260     // if (msg == NULL) {
261     //     amp_warn(MOD_STR, "allocate memory failed");
262     //     goto out;
263     // }
264 
265     // strncpy(msg, send_buf, msg_len);
266     amp_debug(MOD_STR, "msg is:%s, length is: %d", msg, msg_len);
267     tcp_send_param_t *send_param =
268         (tcp_send_param_t *)aos_malloc(sizeof(*send_param));
269     if (!send_param) {
270         amp_error(MOD_STR, "allocate memory failed");
271         aos_free(msg);
272         goto out;
273     }
274     send_param->sock_id = sock_id;
275     send_param->msg = msg;
276     send_param->msg_len = msg_len;
277     duk_dup(ctx, 2);
278     send_param->js_cb_ref = be_ref(ctx);
279 
280     tcp_send_routine(send_param);
281 
282 out:
283     duk_push_int(ctx, ret);
284     return 1;
285 }
286 
tcp_recv_notify(void * pdata)287 static void tcp_recv_notify(void *pdata)
288 {
289     int i = 0;
290     tcp_recv_notify_param_t *p = (tcp_recv_notify_param_t *)pdata;
291     duk_context *ctx = be_get_context();
292     be_push_ref(ctx, p->js_cb_ref);
293     duk_push_int(ctx, p->recv_len);
294     int arr_idx = duk_push_array(ctx);
295     if (p->recv_len > 0) {
296         for (i = 0; i < p->recv_len; i++) {
297             duk_push_int(ctx, p->buf[i]);
298             duk_put_prop_index(ctx, arr_idx, i);
299         }
300     }
301     if (duk_pcall(ctx, 2) != DUK_EXEC_SUCCESS) {
302         amp_console("%s", duk_safe_to_stacktrace(ctx, -1));
303     }
304     duk_pop(ctx);
305     duk_gc(ctx, 0);
306     if (p->recv_len < 0) {
307         be_unref(ctx, p->js_cb_ref);
308         aos_free(p);
309     }
310 }
311 
312 /*************************************************************************************
313  * Function:    udp_recv_routine
314  * Description: create a task for blocking recvfrom call
315  * Called by:
316  **************************************************************************************/
tcp_recv_routine(void * arg)317 static void tcp_recv_routine(void *arg)
318 {
319     tcp_recv_param_t *recv_param = (tcp_recv_param_t *)arg;
320     int sock_id;
321 
322     g_tcp_recv_flag = 1;
323     sock_id = recv_param->sock_id;
324     tcp_recv_notify_param_t *p = aos_calloc(1, sizeof(*p));
325     if (!p) {
326         amp_warn(MOD_STR, "allocate memory failed");
327         duk_context *ctx = be_get_context();
328         be_unref(ctx, recv_param->js_cb_ref);
329         goto out;
330     }
331 
332     while (1) {
333         p->recv_len =
334             aos_tcp_read(sock_id, p->buf, sizeof(p->buf), MAX_TCP_RECV_TIMEOUT);
335         p->js_cb_ref = recv_param->js_cb_ref;
336         if (p->recv_len != 0) {
337             py_task_schedule_call(tcp_recv_notify, p);
338         }
339 
340         if (p->recv_len < 0) {
341             // connection closed
342             amp_error(MOD_STR, "connection closed:%d", p->recv_len);
343             break;
344         }
345 
346         if (g_tcp_close_flag) {
347             duk_context *ctx = be_get_context();
348             be_unref(ctx, recv_param->js_cb_ref);
349             aos_free(p);
350             break;
351         }
352     }
353     aos_tcp_destroy(sock_id);
354 
355 out:
356     aos_free(recv_param);
357     g_tcp_recv_flag = 0;
358     aos_sem_signal(&g_tcp_close_sem);
359     aos_task_exit(0);
360 
361     return;
362 }
363 
364 /*************************************************************************************
365  * Function:    native_udp_recvfrom
366  * Description: js native addon for
367  *            UDP.recv(sock_id,function(length, buffer_array, src_ip,
368  *src_port){}) Called by:   js api Input:       sock_id: interger
369  *            function(length, buffer_array, src_ip, src_port): the callback
370  *function length: the recv msg length buffer_array: the recv msg buffer src_ip:
371  *the peer ip string src_port: the peer port which is a interger
372  *
373  * Output:      return 0 when UDP.recv call ok
374  *            return error number UDP.recv call fail
375  **************************************************************************************/
native_tcp_receive(duk_context * ctx)376 static duk_ret_t native_tcp_receive(duk_context *ctx)
377 {
378     int ret = -1;
379     int sock_id = 0;
380     aos_task_t tcp_recv_task;
381     tcp_recv_param_t *recv_param;
382 
383     if (!duk_is_number(ctx, 0) || !duk_is_function(ctx, 1)) {
384         amp_warn(MOD_STR, "parameter must be number and function");
385         goto out;
386     }
387 
388     sock_id = duk_get_int(ctx, 0);
389     if (sock_id < 0) {
390         amp_warn(MOD_STR, "socket id[%d] is invalid", sock_id);
391         goto out;
392     }
393 
394     amp_debug(MOD_STR, "sock_id: %d", sock_id);
395     recv_param = (tcp_recv_param_t *)aos_calloc(1, sizeof(*recv_param));
396     if (!recv_param) {
397         amp_warn(MOD_STR, "allocate memory failed");
398         goto out;
399     }
400 
401     recv_param->sock_id = sock_id;
402     duk_dup(ctx, 1);
403     recv_param->js_cb_ref = be_ref(ctx);
404 
405     ret =
406         aos_task_new_ext(&tcp_recv_task, "amp tcp recv task", tcp_recv_routine,
407                          recv_param, 1024 * 4, ADDON_TSK_PRIORRITY);
408     if (ret != 0) {
409         amp_warn(MOD_STR, "tcp recv task error");
410         goto out;
411     }
412 
413 out:
414     duk_push_int(ctx, ret);
415     return 1;
416 }
417 
418 /*************************************************************************************
419  * Function:    native_tcp_close
420  * Description: js native addon for
421  *            UDP.close(sock_id)
422  * Called by:   js api
423  * Input:       sock_id: interger
424  *
425  * Output:      return 0 when UDP.close call ok
426  *            return error number UDP.close call fail
427  **************************************************************************************/
native_tcp_close(duk_context * ctx)428 static duk_ret_t native_tcp_close(duk_context *ctx)
429 {
430     int ret = -1;
431     int sock_id = 0;
432 
433     if (!duk_is_number(ctx, 0)) {
434         amp_warn(MOD_STR, "parameter must be number");
435         goto out;
436     }
437 
438     sock_id = duk_get_int(ctx, 0);
439     if (sock_id <= 0) {
440         amp_warn(MOD_STR, "socket id[%d] is invalid", sock_id);
441         goto out;
442     }
443     g_tcp_close_flag = 1;
444     aos_sem_wait(&g_tcp_close_sem, MAX_TCP_RECV_TIMEOUT + 50);
445     g_tcp_close_flag = 0;
446     ret = 0;
447 
448 out:
449     duk_push_int(ctx, ret);
450     return 1;
451 }
452 
module_tcp_source_clean(void)453 static void module_tcp_source_clean(void)
454 {
455     if (g_tcp_recv_flag) {
456         g_tcp_close_flag = 1;
457         aos_sem_wait(&g_tcp_close_sem, MAX_TCP_RECV_TIMEOUT + 50);
458         g_tcp_close_flag = 0;
459     }
460 }
461 
module_tcp_register(void)462 void module_tcp_register(void)
463 {
464     duk_context *ctx = be_get_context();
465 
466     if (!g_tcp_close_sem) {
467         if (aos_sem_new(&g_tcp_close_sem, 0) != 0) {
468             amp_error(MOD_STR, "create tcp sem fail");
469             return;
470         }
471     }
472 
473     amp_module_free_register(module_tcp_source_clean);
474 
475     duk_push_object(ctx);
476 
477     AMP_ADD_FUNCTION("createSocket", native_tcp_create_socket, 2);
478     AMP_ADD_FUNCTION("send", native_tcp_send, 3);
479     AMP_ADD_FUNCTION("recv", native_tcp_receive, 2);
480     AMP_ADD_FUNCTION("close", native_tcp_close, 1);
481 
482     duk_put_prop_string(ctx, -2, "TCP");
483 }
484