Skip to content

Commit 6c2efd7

Browse files
fix: js_gpio freeing, resetting and minor stylistic changes
1 parent 24f4f59 commit 6c2efd7

File tree

1 file changed

+70
-26
lines changed

1 file changed

+70
-26
lines changed

applications/system/js_app/modules/js_gpio.c

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,44 @@
66

77
#define INTERRUPT_QUEUE_LEN 16
88

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+
*/
1312
typedef struct {
1413
mjs_val_t callback;
1514
mjs_val_t manager;
1615
} JsGpioInterruptMessage;
1716

17+
/**
18+
* ISR arguments
19+
*/
1820
typedef struct {
1921
FuriMessageQueue* interrupt_queue;
2022
JsGpioInterruptMessage message;
2123
} JsGpioIsrContext;
2224

25+
/**
26+
* Per-pin control structure
27+
*/
2328
typedef struct {
2429
const GpioPin* pin;
2530
GpioMode previous_mode;
2631
GpioPull previous_pull;
32+
GpioSpeed previous_speed;
33+
bool had_interrupt;
2734
FuriMessageQueue* interrupt_queue;
2835
JsGpioIsrContext* isr_context;
2936
} JsGpioPinInst;
3037

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+
3147
/**
3248
* @brief Initializes a GPIO pin according to the provided mode object
3349
*
@@ -117,12 +133,10 @@ static void js_gpio_init(struct mjs* mjs) {
117133

118134
// get state
119135
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));
122137

123138
// init GPIO
124139
furi_hal_gpio_init(manager_data->pin, mode, pull_mode, GpioSpeedVeryHigh);
125-
126140
mjs_return(mjs, MJS_UNDEFINED);
127141
}
128142

@@ -147,12 +161,10 @@ static void js_gpio_write(struct mjs* mjs) {
147161

148162
// get state
149163
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));
152165

153166
// set level
154167
furi_hal_gpio_write(manager_data->pin, logic_level);
155-
156168
mjs_return(mjs, MJS_UNDEFINED);
157169
}
158170

@@ -172,12 +184,10 @@ static void js_gpio_write(struct mjs* mjs) {
172184
static void js_gpio_read(struct mjs* mjs) {
173185
// get state
174186
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));
177188

178189
// get level
179190
bool value = furi_hal_gpio_read(manager_data->pin);
180-
181191
mjs_return(mjs, mjs_mk_boolean(mjs, value));
182192
}
183193

@@ -212,16 +222,16 @@ static void js_gpio_attach_handler(struct mjs* mjs) {
212222

213223
// get state
214224
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));
217226

218227
// attach interrupt
219-
if(manager_data->isr_context) free(manager_data->isr_context);
228+
free(manager_data->isr_context);
220229
JsGpioIsrContext* context = malloc(sizeof(JsGpioIsrContext));
221230
context->interrupt_queue = manager_data->interrupt_queue;
222231
context->message.callback = callback_arg;
223232
context->message.manager = manager;
224233
manager_data->isr_context = context;
234+
manager_data->had_interrupt = true;
225235
furi_hal_gpio_remove_int_callback(manager_data->pin);
226236
furi_hal_gpio_add_int_callback(manager_data->pin, js_gpio_int_cb, (void*)context);
227237
furi_hal_gpio_enable_int_callback(manager_data->pin);
@@ -248,11 +258,10 @@ static void js_gpio_attach_handler(struct mjs* mjs) {
248258
static void js_gpio_detach_handler(struct mjs* mjs) {
249259
// get state
250260
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));
253262

254263
// detach interrupt
255-
if(manager_data->isr_context) free(manager_data->isr_context);
264+
free(manager_data->isr_context);
256265
furi_hal_gpio_remove_int_callback(manager_data->pin);
257266
furi_message_queue_reset(manager_data->interrupt_queue);
258267

@@ -309,13 +318,24 @@ static void js_gpio_get(struct mjs* mjs) {
309318
JsGpioPinInst* manager_data = malloc(sizeof(JsGpioPinInst));
310319
manager_data->pin = pin_record->pin;
311320
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);
312326
mjs_set(mjs, manager, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, manager_data));
313327
mjs_set(mjs, manager, "init", ~0, MJS_MK_FN(js_gpio_init));
314328
mjs_set(mjs, manager, "write", ~0, MJS_MK_FN(js_gpio_write));
315329
mjs_set(mjs, manager, "read", ~0, MJS_MK_FN(js_gpio_read));
316330
mjs_set(mjs, manager, "attach_handler", ~0, MJS_MK_FN(js_gpio_attach_handler));
317331
mjs_set(mjs, manager, "detach_handler", ~0, MJS_MK_FN(js_gpio_detach_handler));
318332
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;
319339
}
320340

321341
/**
@@ -344,6 +364,7 @@ static void js_gpio_process_interrupts(struct mjs* mjs) {
344364
// get new messages
345365
JsGpioInst* module = mjs_get_ptr(mjs, mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0));
346366
JsGpioInterruptMessage message;
367+
// FIXME: FuriWaitForever hangs the device when JS is exited
347368
while(furi_message_queue_get(
348369
module->interrupt_queue, (void*)&message, block ? FuriWaitForever : 0) ==
349370
FuriStatusOk) {
@@ -353,11 +374,12 @@ static void js_gpio_process_interrupts(struct mjs* mjs) {
353374

354375
static void* js_gpio_create(struct mjs* mjs, mjs_val_t* object) {
355376
JsGpioInst* module = malloc(sizeof(JsGpioInst));
377+
module->managed_pins = NULL;
378+
module->managed_pins_length = 0;
356379
module->interrupt_queue =
357380
furi_message_queue_alloc(INTERRUPT_QUEUE_LEN, sizeof(JsGpioInterruptMessage));
358381

359382
mjs_val_t gpio_obj = mjs_mk_object(mjs);
360-
361383
mjs_set(mjs, gpio_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, module));
362384
mjs_set(mjs, gpio_obj, "get", ~0, MJS_MK_FN(js_gpio_get));
363385
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) {
367389
}
368390

369391
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+
}
375415

376-
// TODO: reset pins
416+
// free buffers
417+
furi_message_queue_free(module->interrupt_queue);
418+
free(module->managed_pins);
419+
free(module);
420+
}
377421

378422
expansion_enable(furi_record_open(RECORD_EXPANSION));
379423
furi_record_close(RECORD_EXPANSION);

0 commit comments

Comments
 (0)