1 /*
2  * Copyright (C) 2015-2019 Alibaba Group Holding Limited
3  */
4 
5 #include <stdint.h>
6 
7 #include "amp_config.h"
8 #include "aos_system.h"
9 #include "amp_defines.h"
10 #include "aos_hal_can.h"
11 #include "board_mgr.h"
12 #include "amp_task.h"
13 #include "be_inl.h"
14 
15 #define MOD_STR "CAN"
16 #define CAN_TIMEOUT (0xFFFFFF)
17 #define MAX_CAN_RECV_LEN    8
18 #define MAX_CAN_PORT    2
19 
20 typedef struct {
21     can_dev_t *can_device;
22     int js_cb_ref;
23 } can_recv_param_t;
24 
25 typedef struct {
26     uint8_t buf[MAX_CAN_RECV_LEN];
27     can_frameheader_t rx_header;
28     int js_cb_ref;
29 } can_recv_notify_param_t;
30 
31 static char g_can_close_flag = 0;
32 static char g_can_recv_flag = 0;
33 static aos_sem_t g_can_close_sem = NULL;
34 
35 can_dev_t g_can_handle[MAX_CAN_PORT];
36 
native_can_open(duk_context * ctx)37 static duk_ret_t native_can_open(duk_context *ctx)
38 {
39     int8_t ret = -1;
40     item_handle_t can_handle;
41     can_handle.handle     = NULL;
42     can_dev_t *can_device = NULL;
43 
44     if (!duk_is_string(ctx, 0)) {
45         amp_warn(MOD_STR, "parameter must be string");
46         goto out;
47     }
48     const char *id = duk_get_string(ctx, 0);
49     ret            = board_attach_item(MODULE_CAN, id, &can_handle);
50     if (0 != ret) {
51         amp_error(MOD_STR, "board_attach_item fail!");
52         goto out;
53     }
54     amp_debug(MOD_STR, "can handle:%u", can_handle.handle);
55     can_device = board_get_node_by_handle(MODULE_CAN, &can_handle);
56     if (NULL == can_device) {
57         amp_error(MOD_STR, "board_get_node_by_handle fail!");
58         goto out;
59     }
60 
61     ret = aos_hal_can_init(can_device);
62     if (0 != ret) {
63         amp_error(MOD_STR, "aos_hal_can_init fail!");
64         goto out;
65     }
66 
67     g_can_handle[can_device->port] = *can_device;
68 
69 out:
70     if (0 != ret) {
71         duk_push_pointer(ctx, NULL);
72         board_disattach_item(MODULE_CAN, &can_handle);
73     } else {
74         duk_push_pointer(ctx, (void *)can_handle.handle);
75     }
76     return 1;
77 }
78 
native_can_close(duk_context * ctx)79 static duk_ret_t native_can_close(duk_context *ctx)
80 {
81     int8_t ret = -1;
82     item_handle_t can_handle;
83     can_dev_t *can_device = NULL;
84 
85     if (!duk_is_pointer(ctx, 0)) {
86         amp_warn(MOD_STR, "parameter must be handle");
87         goto out;
88     }
89     can_handle.handle = duk_get_pointer(ctx, 0);
90     can_device        = board_get_node_by_handle(MODULE_CAN, &can_handle);
91     if (NULL == can_device) {
92         amp_error(MOD_STR, "board_get_node_by_handle fail!");
93         goto out;
94     }
95     ret = aos_hal_can_finalize(can_device);
96     if (0 != ret) {
97         amp_error(MOD_STR, "aos_hal_spi_finalize fail!");
98         goto out;
99     }
100     board_disattach_item(MODULE_CAN, &can_handle);
101     g_can_close_flag = 1;
102     aos_sem_wait(&g_can_close_sem, CAN_TIMEOUT + 50);
103     g_can_close_flag = 0;
104 
105 out:
106     duk_push_int(ctx, ret);
107     return 1;
108 }
109 
native_can_send(duk_context * ctx)110 static duk_ret_t native_can_send(duk_context *ctx)
111 {
112     int8_t ret    = -1;
113     uint8_t *data = NULL;
114     uint32_t len  = 0;
115     uint32_t i    = 0;
116     item_handle_t can_handle;
117     can_dev_t *can_device = NULL;
118     can_frameheader_t tx_header;
119     int arr_idx;
120     uint32_t id;
121     uint8_t rtr, dlc;
122     int err = -1;
123 
124     if (!duk_is_pointer(ctx, 0) || !duk_is_object(ctx, 1) ||
125         !duk_is_array(ctx, 2)) {
126         amp_warn(MOD_STR, "parameter must be handle object array");
127         goto out;
128     }
129 
130     can_handle.handle = duk_get_pointer(ctx, 0);
131     can_device        = board_get_node_by_handle(MODULE_CAN, &can_handle);
132     if (NULL == can_device) {
133         amp_error(MOD_STR, "board_get_node_by_handle fail!");
134         goto out;
135     }
136 
137     /* get can_frame header */
138     duk_get_prop_string(ctx, 1, "id");
139     duk_get_prop_string(ctx, 1, "rtr");
140     duk_get_prop_string(ctx, 1, "dlc");
141 
142     if (!duk_is_number(ctx, -3) || !duk_is_number(ctx, -2) ||
143         !duk_is_number(ctx, -1))
144     {
145         amp_warn(MOD_STR,
146             "Parameter 1 must be an object like {id: number, rtr: number, dlc: number");
147         ret = -2;
148         goto out;
149     }
150 
151     id    = duk_get_uint(ctx, -3);
152     rtr   = duk_get_uint(ctx, -2);
153     if (rtr > 1) {
154         amp_warn(MOD_STR, "rtr is invalid");
155         goto out;
156     }
157 
158     dlc  = duk_get_uint(ctx, -1);
159     if (dlc > 8) {
160         amp_warn(MOD_STR, "dlc is invalid");
161         goto out;
162     }
163 
164     duk_pop_3(ctx);
165 
166     tx_header.id = id;
167     tx_header.rtr = rtr;
168     tx_header.dlc = dlc;
169 
170     arr_idx = duk_normalize_index(ctx, 2);
171     len     = duk_get_length(ctx, arr_idx);
172     data    = (uint8_t *)aos_malloc(sizeof(uint8_t) * len);
173     if (NULL == data) {
174         amp_warn(MOD_STR, "allocate memory failed");
175         goto out;
176     }
177     for (i = 0; i < len; i++) {
178         duk_get_prop_index(ctx, arr_idx, i);
179         if (!duk_is_number(ctx, -1)) {
180             amp_warn(MOD_STR, "data is not number, index: %d", i);
181             duk_pop(ctx);
182             goto out;
183         }
184         data[i] = (uint8_t)duk_get_int(ctx, -1);
185         duk_pop(ctx);
186     }
187 
188     ret = aos_hal_can_send(can_device, &tx_header, data, CAN_TIMEOUT);
189     if (-1 == ret) {
190         amp_error(MOD_STR, "aos_hal_can_send fail!");
191         goto out;
192     }
193     err = 0;
194 out:
195     aos_free(data);
196     duk_push_int(ctx, err);
197     return 1;
198 }
199 
can_recv_notify(void * pdata)200 static void can_recv_notify(void *pdata)
201 {
202     int i                      = 0;
203     can_recv_notify_param_t *p = (can_recv_notify_param_t *)pdata;
204     duk_context *ctx           = be_get_context();
205 
206     be_push_ref(ctx, p->js_cb_ref);
207     duk_push_uint(ctx, p->rx_header.rtr);
208     duk_push_uint(ctx, p->rx_header.id);
209     int arr_idx = duk_push_array(ctx);
210     if(p->rx_header.rtr == 0) {
211         for (i = 0; i < p->rx_header.dlc; i++) {
212             duk_push_int(ctx, p->buf[i]);
213             duk_put_prop_index(ctx, arr_idx, i);
214         }
215     }
216     if (duk_pcall(ctx, 3) != DUK_EXEC_SUCCESS) {
217         amp_console("%s", duk_safe_to_stacktrace(ctx, -1));
218     }
219 
220     duk_pop(ctx);
221     duk_gc(ctx, 0);
222     // aos_free(p);
223 }
224 
225 /*************************************************************************************
226  * Function:    udp_recv_routine
227  * Description: create a task for blocking recvfrom call
228  * Called by:
229  **************************************************************************************/
can_recv_routine(void * arg)230 static void can_recv_routine(void *arg)
231 {
232     int ret = -1;
233     can_recv_param_t *recv_param = (can_recv_param_t *)arg;
234     can_dev_t *can_device = (can_dev_t *)recv_param->can_device;
235 
236     can_recv_notify_param_t *p = aos_calloc(1, sizeof(*p));
237     if (!p) {
238         amp_warn(MOD_STR, "allocate memory failed");
239         duk_context *ctx = be_get_context();
240         be_unref(ctx, recv_param->js_cb_ref);
241         goto out;
242     }
243 
244     g_can_recv_flag = 1;
245     while(1) {
246         ret = aos_hal_can_recv(can_device, &p->rx_header, p->buf, CAN_TIMEOUT);
247         if (ret == 0) {
248             p->js_cb_ref = recv_param->js_cb_ref;
249             amp_task_schedule_call(can_recv_notify, p);
250         }
251 
252         if (g_can_close_flag) {
253             duk_context *ctx = be_get_context();
254             be_unref(ctx, recv_param->js_cb_ref);
255             aos_free(p);
256             break;
257         }
258 
259         aos_msleep(10);
260     }
261     ret = aos_hal_can_finalize(can_device);
262     if (ret != 0) {
263         amp_error(MOD_STR, "hal can finalize failed");
264     }
265 
266 out:
267     aos_free(recv_param);
268     g_can_recv_flag = 0;
269     aos_sem_signal(&g_can_close_sem);
270     aos_task_exit(0);
271 
272     return;
273 }
274 
native_can_receive(duk_context * ctx)275 static duk_ret_t native_can_receive(duk_context *ctx)
276 {
277     int8_t ret    = -1;
278     uint8_t *data = NULL;
279     item_handle_t can_handle;
280     can_dev_t *can_device = NULL;
281     can_recv_param_t *recv_param;
282     aos_task_t can_recv_task;
283 
284     if (!duk_is_pointer(ctx, 0) || !duk_is_function(ctx, 1)) {
285         amp_warn(MOD_STR, "parameter must be handle and function");
286         goto out;
287     }
288 
289     can_handle.handle = duk_get_pointer(ctx, 0);
290     can_device        = board_get_node_by_handle(MODULE_CAN, &can_handle);
291     if (NULL == can_device) {
292         amp_error(MOD_STR, "board_get_node_by_handle fail!");
293         goto out;
294     }
295 amp_debug(MOD_STR, "can handle %p", can_device);
296     recv_param = (can_recv_param_t *)aos_calloc(1, sizeof(*recv_param));
297     if (!recv_param) {
298         amp_warn(MOD_STR, "allocate memory failed");
299         goto out;
300     }
301 
302     duk_dup(ctx, 1);
303     recv_param->js_cb_ref = be_ref(ctx);
304     recv_param->can_device = can_device;
305 
306     ret = aos_task_new_ext(&can_recv_task, "amp can recv task", can_recv_routine, recv_param, 1024 * 4, ADDON_TSK_PRIORRITY);
307     if (ret != 0) {
308         amp_warn(MOD_STR, "tcp recv task error");
309         goto out;
310     }
311 
312 out:
313     duk_push_int(ctx, ret);
314     return 1;
315 }
316 
module_can_clean(void)317 static void module_can_clean(void)
318 {
319     if (g_can_recv_flag) {
320         g_can_close_flag = 1;
321         aos_sem_wait(&g_can_close_sem, CAN_TIMEOUT + 50);
322         g_can_close_flag = 0;
323     }
324 }
325 
module_can_register(void)326 void module_can_register(void)
327 {
328     amp_debug(MOD_STR, "module_can_register");
329     duk_context *ctx = be_get_context();
330 
331     if (!g_can_close_sem) {
332         if (aos_sem_new(&g_can_close_sem, 0) != 0) {
333             amp_error(MOD_STR, "create can sem fail");
334             return;
335         }
336     }
337 
338     amp_module_free_register(module_can_clean);
339 
340     duk_push_object(ctx);
341 
342     AMP_ADD_FUNCTION("open",    native_can_open, 1);
343     AMP_ADD_FUNCTION("send",    native_can_send, 3);
344     AMP_ADD_FUNCTION("receive", native_can_receive, 2);
345     AMP_ADD_FUNCTION("close",   native_can_close, 1);
346 
347     duk_put_prop_string(ctx, -2, "CAN");
348 }
349