6
6
7
7
#define INTERRUPT_QUEUE_LEN 16
8
8
9
- typedef struct {
10
- FuriMessageQueue * interrupt_queue ;
11
- } JsGpioInst ;
12
-
9
+ /**
10
+ * ISRs send messages to the module's `interrupt_queue` to be processed later
11
+ */
13
12
typedef struct {
14
13
mjs_val_t callback ;
15
14
mjs_val_t manager ;
16
15
} JsGpioInterruptMessage ;
17
16
17
+ /**
18
+ * ISR arguments
19
+ */
18
20
typedef struct {
19
21
FuriMessageQueue * interrupt_queue ;
20
22
JsGpioInterruptMessage message ;
21
23
} JsGpioIsrContext ;
22
24
25
+ /**
26
+ * Per-pin control structure
27
+ */
23
28
typedef struct {
24
29
const GpioPin * pin ;
25
30
GpioMode previous_mode ;
26
31
GpioPull previous_pull ;
32
+ GpioSpeed previous_speed ;
33
+ bool had_interrupt ;
27
34
FuriMessageQueue * interrupt_queue ;
28
35
JsGpioIsrContext * isr_context ;
29
36
} JsGpioPinInst ;
30
37
38
+ /**
39
+ * Per-module instance control structure
40
+ */
41
+ typedef struct {
42
+ FuriMessageQueue * interrupt_queue ;
43
+ JsGpioPinInst * * managed_pins ;
44
+ uint32_t managed_pins_length ;
45
+ } JsGpioInst ;
46
+
31
47
/**
32
48
* @brief Initializes a GPIO pin according to the provided mode object
33
49
*
@@ -117,12 +133,10 @@ static void js_gpio_init(struct mjs* mjs) {
117
133
118
134
// get state
119
135
mjs_val_t manager = mjs_get_this (mjs );
120
- JsGpioPinInst * manager_data =
121
- (JsGpioPinInst * )(uint32_t )(mjs_get (mjs , manager , INST_PROP_NAME , ~0 ) & 0xFFFFFFFF );
136
+ JsGpioPinInst * manager_data = mjs_get_ptr (mjs , mjs_get (mjs , manager , INST_PROP_NAME , ~0 ));
122
137
123
138
// init GPIO
124
139
furi_hal_gpio_init (manager_data -> pin , mode , pull_mode , GpioSpeedVeryHigh );
125
-
126
140
mjs_return (mjs , MJS_UNDEFINED );
127
141
}
128
142
@@ -147,12 +161,10 @@ static void js_gpio_write(struct mjs* mjs) {
147
161
148
162
// get state
149
163
mjs_val_t manager = mjs_get_this (mjs );
150
- JsGpioPinInst * manager_data =
151
- (JsGpioPinInst * )(uint32_t )(mjs_get (mjs , manager , INST_PROP_NAME , ~0 ) & 0xFFFFFFFF );
164
+ JsGpioPinInst * manager_data = mjs_get_ptr (mjs , mjs_get (mjs , manager , INST_PROP_NAME , ~0 ));
152
165
153
166
// set level
154
167
furi_hal_gpio_write (manager_data -> pin , logic_level );
155
-
156
168
mjs_return (mjs , MJS_UNDEFINED );
157
169
}
158
170
@@ -172,12 +184,10 @@ static void js_gpio_write(struct mjs* mjs) {
172
184
static void js_gpio_read (struct mjs * mjs ) {
173
185
// get state
174
186
mjs_val_t manager = mjs_get_this (mjs );
175
- JsGpioPinInst * manager_data =
176
- (JsGpioPinInst * )(uint32_t )(mjs_get (mjs , manager , INST_PROP_NAME , ~0 ) & 0xFFFFFFFF );
187
+ JsGpioPinInst * manager_data = mjs_get_ptr (mjs , mjs_get (mjs , manager , INST_PROP_NAME , ~0 ));
177
188
178
189
// get level
179
190
bool value = furi_hal_gpio_read (manager_data -> pin );
180
-
181
191
mjs_return (mjs , mjs_mk_boolean (mjs , value ));
182
192
}
183
193
@@ -212,16 +222,16 @@ static void js_gpio_attach_handler(struct mjs* mjs) {
212
222
213
223
// get state
214
224
mjs_val_t manager = mjs_get_this (mjs );
215
- JsGpioPinInst * manager_data =
216
- (JsGpioPinInst * )(uint32_t )(mjs_get (mjs , manager , INST_PROP_NAME , ~0 ) & 0xFFFFFFFF );
225
+ JsGpioPinInst * manager_data = mjs_get_ptr (mjs , mjs_get (mjs , manager , INST_PROP_NAME , ~0 ));
217
226
218
227
// attach interrupt
219
- if ( manager_data -> isr_context ) free (manager_data -> isr_context );
228
+ free (manager_data -> isr_context );
220
229
JsGpioIsrContext * context = malloc (sizeof (JsGpioIsrContext ));
221
230
context -> interrupt_queue = manager_data -> interrupt_queue ;
222
231
context -> message .callback = callback_arg ;
223
232
context -> message .manager = manager ;
224
233
manager_data -> isr_context = context ;
234
+ manager_data -> had_interrupt = true;
225
235
furi_hal_gpio_remove_int_callback (manager_data -> pin );
226
236
furi_hal_gpio_add_int_callback (manager_data -> pin , js_gpio_int_cb , (void * )context );
227
237
furi_hal_gpio_enable_int_callback (manager_data -> pin );
@@ -248,11 +258,10 @@ static void js_gpio_attach_handler(struct mjs* mjs) {
248
258
static void js_gpio_detach_handler (struct mjs * mjs ) {
249
259
// get state
250
260
mjs_val_t manager = mjs_get_this (mjs );
251
- JsGpioPinInst * manager_data =
252
- (JsGpioPinInst * )(uint32_t )(mjs_get (mjs , manager , INST_PROP_NAME , ~0 ) & 0xFFFFFFFF );
261
+ JsGpioPinInst * manager_data = mjs_get_ptr (mjs , mjs_get (mjs , manager , INST_PROP_NAME , ~0 ));
253
262
254
263
// detach interrupt
255
- if ( manager_data -> isr_context ) free (manager_data -> isr_context );
264
+ free (manager_data -> isr_context );
256
265
furi_hal_gpio_remove_int_callback (manager_data -> pin );
257
266
furi_message_queue_reset (manager_data -> interrupt_queue );
258
267
@@ -309,13 +318,24 @@ static void js_gpio_get(struct mjs* mjs) {
309
318
JsGpioPinInst * manager_data = malloc (sizeof (JsGpioPinInst ));
310
319
manager_data -> pin = pin_record -> pin ;
311
320
manager_data -> interrupt_queue = module -> interrupt_queue ;
321
+ // TODO: somehow get actual previous mode
322
+ manager_data -> previous_mode = GpioModeInput ;
323
+ manager_data -> previous_pull = GpioPullNo ;
324
+ manager_data -> previous_speed = GpioSpeedLow ;
325
+ mjs_own (mjs , & manager );
312
326
mjs_set (mjs , manager , INST_PROP_NAME , ~0 , mjs_mk_foreign (mjs , manager_data ));
313
327
mjs_set (mjs , manager , "init" , ~0 , MJS_MK_FN (js_gpio_init ));
314
328
mjs_set (mjs , manager , "write" , ~0 , MJS_MK_FN (js_gpio_write ));
315
329
mjs_set (mjs , manager , "read" , ~0 , MJS_MK_FN (js_gpio_read ));
316
330
mjs_set (mjs , manager , "attach_handler" , ~0 , MJS_MK_FN (js_gpio_attach_handler ));
317
331
mjs_set (mjs , manager , "detach_handler" , ~0 , MJS_MK_FN (js_gpio_detach_handler ));
318
332
mjs_return (mjs , manager );
333
+
334
+ // remember pin
335
+ module -> managed_pins_length ++ ;
336
+ module -> managed_pins =
337
+ realloc (module -> managed_pins , sizeof (JsGpioPinInst ) * module -> managed_pins_length );
338
+ module -> managed_pins [module -> managed_pins_length - 1 ] = manager_data ;
319
339
}
320
340
321
341
/**
@@ -344,6 +364,7 @@ static void js_gpio_process_interrupts(struct mjs* mjs) {
344
364
// get new messages
345
365
JsGpioInst * module = mjs_get_ptr (mjs , mjs_get (mjs , mjs_get_this (mjs ), INST_PROP_NAME , ~0 ));
346
366
JsGpioInterruptMessage message ;
367
+ // FIXME: FuriWaitForever hangs the device when JS is exited
347
368
while (furi_message_queue_get (
348
369
module -> interrupt_queue , (void * )& message , block ? FuriWaitForever : 0 ) ==
349
370
FuriStatusOk ) {
@@ -353,11 +374,12 @@ static void js_gpio_process_interrupts(struct mjs* mjs) {
353
374
354
375
static void * js_gpio_create (struct mjs * mjs , mjs_val_t * object ) {
355
376
JsGpioInst * module = malloc (sizeof (JsGpioInst ));
377
+ module -> managed_pins = NULL ;
378
+ module -> managed_pins_length = 0 ;
356
379
module -> interrupt_queue =
357
380
furi_message_queue_alloc (INTERRUPT_QUEUE_LEN , sizeof (JsGpioInterruptMessage ));
358
381
359
382
mjs_val_t gpio_obj = mjs_mk_object (mjs );
360
-
361
383
mjs_set (mjs , gpio_obj , INST_PROP_NAME , ~0 , mjs_mk_foreign (mjs , module ));
362
384
mjs_set (mjs , gpio_obj , "get" , ~0 , MJS_MK_FN (js_gpio_get ));
363
385
mjs_set (mjs , gpio_obj , "process_interrupts" , ~0 , MJS_MK_FN (js_gpio_process_interrupts ));
@@ -367,13 +389,35 @@ static void* js_gpio_create(struct mjs* mjs, mjs_val_t* object) {
367
389
}
368
390
369
391
static void js_gpio_destroy (void * inst ) {
370
- if (inst != NULL ) {
371
- JsGpioInst * gpio = (JsGpioInst * )inst ;
372
- // TODO: release resources
373
- free (gpio );
374
- }
392
+ if (inst ) {
393
+ // reset pins
394
+ JsGpioInst * module = (JsGpioInst * )inst ;
395
+ for (uint32_t i = 0 ; i < module -> managed_pins_length ; i ++ ) {
396
+ JsGpioPinInst * manager_data = module -> managed_pins [i ];
397
+ if (manager_data -> had_interrupt ) {
398
+ furi_hal_gpio_disable_int_callback (manager_data -> pin );
399
+ furi_hal_gpio_remove_int_callback (manager_data -> pin );
400
+ }
401
+ furi_hal_gpio_init (
402
+ manager_data -> pin ,
403
+ manager_data -> previous_mode ,
404
+ manager_data -> previous_pull ,
405
+ manager_data -> previous_speed );
406
+ free (manager_data );
407
+ // TODO: research `mjs_own` and `mjs_disown`
408
+ // porta: Ideally, pin managers should be disowned via `mjs_disown`,
409
+ // which requires a reference to the mjs structure that we don't have.
410
+ // The module destructor is only ever called shortly before the mjs
411
+ // destructor is called, which may not free our owned object. It looks
412
+ // like it does do so (since no memory leaks manifest themselves),
413
+ // but idk \(-_-)/
414
+ }
375
415
376
- // TODO: reset pins
416
+ // free buffers
417
+ furi_message_queue_free (module -> interrupt_queue );
418
+ free (module -> managed_pins );
419
+ free (module );
420
+ }
377
421
378
422
expansion_enable (furi_record_open (RECORD_EXPANSION ));
379
423
furi_record_close (RECORD_EXPANSION );
0 commit comments