1 /*
2  * Copyright (C) 2015-2019 Alibaba Group Holding Limited
3  */
4 
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 
9 #include "amp_config.h"
10 #include "amp_defines.h"
11 #include "aos_hal_gpio.h"
12 #include "aos_system.h"
13 #include "amp_task.h"
14 #include "board_mgr.h"
15 #include "quickjs.h"
16 #include "quickjs_addon_common.h"
17 
18 #define MOD_STR "GPIO"
19 
20 #define GPIO_IRQ_RISING_EDGE "rising"
21 #define GPIO_IRQ_FALLING_EDGE "falling"
22 #define GPIO_IRQ_BOTH_EDGE "both"
23 
24 static uint16_t gpio_init_flag = 0;
25 static JSClassID js_gpio_class_id;
26 
native_gpio_open(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)27 static JSValue native_gpio_open(JSContext *ctx, JSValueConst this_val,
28                           int argc, JSValueConst *argv)
29 {
30     int8_t ret = -1;
31     item_handle_t gpio_handle;
32     gpio_handle.handle      = NULL;
33     gpio_dev_t *gpio_device = NULL;
34 
35     const char *id = JS_ToCString(ctx, argv[0]);
36     if (id == NULL) {
37         amp_error(MOD_STR, "get gpio id fail!");
38         goto out;
39     }
40 
41     ret = board_attach_item(MODULE_GPIO, id, &gpio_handle);
42     if (0 != ret) {
43         amp_error(MOD_STR, "board_attach_item fail!, id %s", id);
44         goto out;
45     }
46 
47     amp_debug(MOD_STR, "gpio handle:%p\n", gpio_handle.handle);
48     gpio_device = board_get_node_by_handle(MODULE_GPIO, &gpio_handle);
49     if (NULL == gpio_device) {
50         amp_error(MOD_STR, "board_get_node_by_handle fail!");
51         goto out;
52     }
53 
54     if (gpio_init_flag & (1 << gpio_device->port)) {
55         amp_debug(MOD_STR, "gpio port [%d] is already inited", gpio_device->port);
56         goto out;
57     }
58 
59     ret = aos_hal_gpio_init(gpio_device);
60     if (0 != ret) {
61         amp_error(MOD_STR, "aos_hal_gpio_init fail!");
62         goto out;
63     }
64 
65     gpio_init_flag |= (1 << gpio_device->port);
66 
67 out:
68     if (id != NULL) {
69         JS_FreeCString(ctx, id);
70     }
71     if (0 != ret) {
72         JS_SetContextOpaque(ctx, NULL);
73         board_disattach_item(MODULE_GPIO, &gpio_handle);
74     } else {
75         JSValue obj;
76         obj = JS_NewObjectClass(ctx, js_gpio_class_id);
77         JS_SetOpaque(obj, (void *)gpio_handle.handle);
78         return obj;
79     }
80     return JS_NewInt32(ctx, ret);
81 }
82 
native_gpio_close(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)83 static JSValue native_gpio_close(JSContext *ctx, JSValueConst this_val,
84                           int argc, JSValueConst *argv)
85 {
86     int32_t ret = -1;
87     item_handle_t gpio_handle;
88     gpio_dev_t *gpio_device = NULL;
89     gpio_params_t *priv = NULL;
90 
91     gpio_handle.handle = JS_GetOpaque2(ctx, this_val, js_gpio_class_id);
92     if (!gpio_handle.handle) {
93         amp_warn(MOD_STR, "parameter must be handle");
94         goto out;
95     }
96 
97     gpio_device = board_get_node_by_handle(MODULE_GPIO, &gpio_handle);
98     if (NULL == gpio_device) {
99         amp_error(MOD_STR, "board_get_node_by_handle fail!");
100         goto out;
101     }
102     priv = (gpio_params_t *)gpio_device->priv;
103     if(priv->reserved != NULL) {
104         aos_printf("func %p free memory %p \n", __func__, priv->reserved);
105         aos_free(priv->reserved);
106         priv->reserved = NULL;
107     }
108     ret = aos_hal_gpio_finalize(gpio_device);
109     if (0 != ret) {
110         amp_error(MOD_STR, "aos_hal_gpio_finalize fail!");
111         goto out;
112     }
113     board_disattach_item(MODULE_GPIO, &gpio_handle);
114 
115 out:
116     return JS_NewInt32(ctx, ret);
117 }
118 
native_gpio_toggle(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)119 static JSValue native_gpio_toggle(JSContext *ctx, JSValueConst this_val,
120                           int argc, JSValueConst *argv)
121 {
122     int32_t ret = -1;
123     item_handle_t gpio_handle;
124     gpio_dev_t *gpio_device = NULL;
125 
126     gpio_handle.handle = JS_GetOpaque2(ctx, this_val, js_gpio_class_id);
127     if (!gpio_handle.handle) {
128         amp_warn(MOD_STR, "parameter must be handle");
129         goto out;
130     }
131     gpio_device = board_get_node_by_handle(MODULE_GPIO, &gpio_handle);
132     if (NULL == gpio_device) {
133         amp_error(MOD_STR, "board_get_node_by_handle fail!");
134         goto out;
135     }
136 
137     ret = aos_hal_gpio_output_toggle(gpio_device);
138 
139 out:
140     return JS_NewInt32(ctx, ret);
141 }
142 
native_gpio_write(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)143 static JSValue native_gpio_write(JSContext *ctx, JSValueConst this_val,
144                           int argc, JSValueConst *argv)
145 {
146     int8_t ret    = -1;
147     int8_t result = -1;
148     int8_t level  = 0;
149     item_handle_t gpio_handle;
150     gpio_dev_t *gpio_device = NULL;
151 
152     gpio_handle.handle = JS_GetOpaque2(ctx, this_val, js_gpio_class_id);
153     if (!gpio_handle.handle) {
154         amp_warn(MOD_STR, "parameter must be handle");
155         goto out;
156     }
157 
158     gpio_device = board_get_node_by_handle(MODULE_GPIO, &gpio_handle);
159     if (NULL == gpio_device) {
160         amp_error(MOD_STR, "board_get_node_by_handle fail!");
161         goto out;
162     }
163     JS_ToInt32(ctx, &level, argv[0]);
164     if (level) {
165         ret = aos_hal_gpio_output_high(gpio_device);
166     } else {
167         ret = aos_hal_gpio_output_low(gpio_device);
168     }
169     if (-1 == ret) {
170         amp_error(MOD_STR, "gpio output set fail!");
171         goto out;
172     }
173     result = 0;
174 out:
175     return JS_NewInt32(ctx, result);
176 }
177 
native_gpio_read(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)178 static JSValue native_gpio_read(JSContext *ctx, JSValueConst this_val,
179                           int argc, JSValueConst *argv)
180 {
181     item_handle_t gpio_handle;
182     uint32_t level          = 0;
183     gpio_dev_t *gpio_device = NULL;
184 
185     gpio_handle.handle = JS_GetOpaque2(ctx, this_val, js_gpio_class_id);
186     if (!gpio_handle.handle) {
187         amp_warn(MOD_STR, "parameter must be handle");
188         goto out;
189     }
190 
191     gpio_device = board_get_node_by_handle(MODULE_GPIO, &gpio_handle);
192     if (NULL == gpio_device) {
193         amp_error(MOD_STR, "board_get_node_by_handle fail!");
194         goto out;
195     }
196     aos_hal_gpio_input_get(gpio_device, &level);
197 out:
198     return JS_NewInt32(ctx, level);
199 }
200 
201 typedef struct {
202     JSValue js_cb_ref;
203     gpio_dev_t *dev;
204 }gpio_irq_notify_param_t;
205 
gpio_irq_notify(void * arg)206 static void gpio_irq_notify(void *arg)
207 {
208     gpio_irq_notify_param_t *param = (gpio_irq_notify_param_t *)arg;
209     JSContext *ctx = js_get_context();
210     uint32_t value = 0;
211 
212     aos_hal_gpio_input_get(param->dev, &value);
213     JSValue v = JS_NewInt32(ctx, value);
214     JSValue val = JS_Call(ctx, param->js_cb_ref, JS_UNDEFINED, 1, &v);
215 
216     JS_FreeValue(ctx, v);
217     JS_FreeValue(ctx, val);
218 
219     aos_free(param);
220 }
221 
222 /* avoid stdout in irq function */
gpio_irq(void * arg)223 static void gpio_irq(void *arg)
224 {
225     static uint64_t irq_lasttime = 0;
226     uint64_t irq_nowtime = aos_now_ms();
227     gpio_irq_notify_param_t *notify = aos_malloc(sizeof(gpio_irq_notify_param_t));
228 
229     if ((NULL == notify) || (NULL == arg)) {
230         /* amp_error(MOD_STR, "param error!\n"); */
231         return;
232     }
233 
234     memcpy(notify, (gpio_irq_notify_param_t *)arg, sizeof(gpio_irq_notify_param_t));
235     if(irq_nowtime - irq_lasttime < 200) {
236         // demounce in 200ms
237         return;
238     }
239     irq_lasttime = irq_nowtime;
240     if (amp_task_schedule_call(gpio_irq_notify, notify) < 0) {
241         /* amp_warn(MOD_STR, "amp_task_schedule_call failed\n"); */
242     }
243 }
244 
native_gpio_on(JSContext * ctx,JSValueConst this_val,int argc,JSValueConst * argv)245 static JSValue native_gpio_on(JSContext *ctx, JSValueConst this_val,
246                           int argc, JSValueConst *argv)
247 {
248     int8_t ret      = -1;
249     int8_t result   = -1;
250     int8_t irq_edge = 0;
251     item_handle_t gpio_handle;
252     gpio_handle.handle      = NULL;
253     gpio_dev_t *gpio_device = NULL;
254     gpio_params_t *priv = NULL;
255     const char *edge;
256     gpio_irq_notify_param_t *notify = NULL;
257 
258     notify = aos_malloc(sizeof(gpio_irq_notify_param_t));
259     aos_printf("func %p alloc memory %p \n", __func__, notify);
260     if (!notify)
261         goto out;
262 
263     gpio_handle.handle = JS_GetOpaque2(ctx, this_val, js_gpio_class_id);
264     if (!gpio_handle.handle) {
265         amp_warn(MOD_STR, "parameter must be handle");
266         aos_free(notify);
267         goto out;
268     }
269     gpio_device        = board_get_node_by_handle(MODULE_GPIO, &gpio_handle);
270     if (NULL == gpio_device) {
271         amp_error(MOD_STR, "board_get_node_by_handle fail!");
272         aos_free(notify);
273         goto out;
274     }
275 
276     priv = (gpio_params_t *)gpio_device->priv;
277     priv->reserved = (void *)notify;  // 停止中断时,释放该内存
278     irq_edge = priv->irq_mode;
279     JSValue irq_cb = argv[0];
280     if (!JS_IsFunction(ctx, irq_cb)) {
281         return JS_ThrowTypeError(ctx, "not a function");
282     }
283     notify->js_cb_ref = JS_DupValue(ctx, irq_cb);
284     notify->dev       = gpio_device;
285 
286     // amp_debug(MOD_STR, "%p, irq_edge:%04x port:%d", gpio_device, irq_edge, gpio_device->port);
287     ret = aos_hal_gpio_enable_irq(gpio_device, irq_edge, gpio_irq, notify);
288     if (ret < 0) {
289         amp_error(MOD_STR, "aos_hal_gpio_enable_irq fail!");
290         goto out;
291     }
292 
293     result = 0;
294 
295 out:
296     return JS_NewInt32(ctx, result);
297 }
298 
299 static JSClassDef js_gpio_class = {
300     "GPIO",
301 };
302 
303 static const JSCFunctionListEntry js_gpio_funcs[] = {
304     JS_CFUNC_DEF("open", 1, native_gpio_open ),
305     JS_CFUNC_DEF("read", 0, native_gpio_read ),
306     JS_CFUNC_DEF("write", 1, native_gpio_write ),
307     JS_CFUNC_DEF("toggle", 0, native_gpio_toggle),
308     JS_CFUNC_DEF("on", 0, native_gpio_on),
309     JS_CFUNC_DEF("close", 0, native_gpio_close ),
310 };
311 
js_gpio_init(JSContext * ctx,JSModuleDef * m)312 static int js_gpio_init(JSContext *ctx, JSModuleDef *m)
313 {
314     JSValue proto;
315 
316     JS_NewClassID(&js_gpio_class_id);
317 
318     JS_NewClass(JS_GetRuntime(ctx), js_gpio_class_id, &js_gpio_class);
319     proto = JS_NewObject(ctx);
320     JS_SetPropertyFunctionList(ctx, proto, js_gpio_funcs,
321                                countof(js_gpio_funcs));
322     JS_SetClassProto(ctx, js_gpio_class_id, proto);
323 
324     return JS_SetModuleExportList(ctx, m, js_gpio_funcs,
325                                   countof(js_gpio_funcs));
326 }
327 
js_init_module_gpio(JSContext * ctx,const char * module_name)328 JSModuleDef *js_init_module_gpio(JSContext *ctx, const char *module_name)
329 {
330     JSModuleDef *m;
331     m = JS_NewCModule(ctx, module_name, js_gpio_init);
332     if (!m)
333         return NULL;
334     JS_AddModuleExportList(ctx, m, js_gpio_funcs, countof(js_gpio_funcs));
335     return m;
336 }
337 
module_gpio_register(void)338 void module_gpio_register(void)
339 {
340     amp_debug(MOD_STR, "module_gpio_register");
341     JSContext *ctx = js_get_context();
342     aos_printf("module gpio register\n");
343     js_init_module_gpio(ctx, "GPIO");
344 }
345