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