rpi-io 1.2.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/addon/gpio.c ADDED
@@ -0,0 +1,875 @@
1
+ // -------------------------------------------------------------------
2
+ // RPI-IO: - gpio.c v38 - 2025-11-19
3
+ // source: claude.ai/chat/f3139163-e976-47a4-8e46-01fee65686f2
4
+ // -------------------------------------------------------------------
5
+
6
+ #include <node_api.h>
7
+ #include <gpiod.h>
8
+ #include <string.h>
9
+ #include <stdio.h>
10
+ #include <stdlib.h>
11
+ #include <pthread.h>
12
+ #include <unistd.h>
13
+ #include <errno.h>
14
+ #include <time.h>
15
+
16
+ // La version de libgpiod est détectée par binding.gyp et passée comme define
17
+ // LIBGPIOD_V2 ou LIBGPIOD_V1
18
+ #if defined(LIBGPIOD_V2)
19
+ // #pragma message "Compiling with libgpiod v2.x API"
20
+ #elif defined(LIBGPIOD_V1)
21
+ // #pragma message "Compiling with libgpiod v1.x API"
22
+ #else
23
+ #error "Cannot detect libgpiod version. Please ensure detect-gpiod-version.sh is executable."
24
+ #endif
25
+
26
+ // Structure pour stocker les lignes GPIO ouvertes
27
+ typedef struct {
28
+ #ifdef LIBGPIOD_V2
29
+ struct gpiod_chip *chip;
30
+ struct gpiod_line_request *request;
31
+ struct gpiod_line_settings *line_settings;
32
+ struct gpiod_line_config *line_cfg;
33
+ struct gpiod_request_config *req_cfg;
34
+ unsigned int offset;
35
+ #else
36
+ struct gpiod_chip *chip;
37
+ struct gpiod_line *line;
38
+ #endif
39
+ int line_num;
40
+ int is_output;
41
+ int is_closed;
42
+
43
+ // Pour le monitoring
44
+ int is_monitoring;
45
+ pthread_t monitor_thread;
46
+ napi_threadsafe_function tsfn;
47
+ napi_ref callback_ref;
48
+ } gpio_context_t;
49
+
50
+ // Libérer les ressources GPIO
51
+ static void finalize_gpio(napi_env env, void* finalize_data, void* finalize_hint) {
52
+ gpio_context_t *ctx = (gpio_context_t*)finalize_data;
53
+ if (ctx) {
54
+ // Arrêter le monitoring si actif
55
+ if (ctx->is_monitoring) {
56
+ ctx->is_monitoring = 0;
57
+ if (ctx->monitor_thread) {
58
+ pthread_join(ctx->monitor_thread, NULL);
59
+ }
60
+ }
61
+
62
+ if (ctx->tsfn) {
63
+ napi_release_threadsafe_function(ctx->tsfn, napi_tsfn_abort);
64
+ ctx->tsfn = NULL;
65
+ }
66
+
67
+ // Ne pas utiliser callback_ref ici car nous n'avons plus d'environnement valide
68
+ ctx->callback_ref = NULL;
69
+
70
+ // Ne libérer que si pas déjà fermé
71
+ if (!ctx->is_closed) {
72
+ #ifdef LIBGPIOD_V2
73
+ if (ctx->request) {
74
+ gpiod_line_request_release(ctx->request);
75
+ ctx->request = NULL;
76
+ }
77
+ if (ctx->line_settings) {
78
+ gpiod_line_settings_free(ctx->line_settings);
79
+ ctx->line_settings = NULL;
80
+ }
81
+ if (ctx->line_cfg) {
82
+ gpiod_line_config_free(ctx->line_cfg);
83
+ ctx->line_cfg = NULL;
84
+ }
85
+ if (ctx->req_cfg) {
86
+ gpiod_request_config_free(ctx->req_cfg);
87
+ ctx->req_cfg = NULL;
88
+ }
89
+ if (ctx->chip) {
90
+ gpiod_chip_close(ctx->chip);
91
+ ctx->chip = NULL;
92
+ }
93
+ #else
94
+ if (ctx->line) {
95
+ gpiod_line_release(ctx->line);
96
+ ctx->line = NULL;
97
+ }
98
+ if (ctx->chip) {
99
+ gpiod_chip_close(ctx->chip);
100
+ ctx->chip = NULL;
101
+ }
102
+ #endif
103
+ ctx->is_closed = 1;
104
+ }
105
+ free(ctx);
106
+ }
107
+ }
108
+
109
+ // Fonction: GetVersion() - Retourne la version de libgpiod utilisée
110
+ static napi_value GetVersion(napi_env env, napi_callback_info info) {
111
+ napi_value result;
112
+ const char* version;
113
+
114
+ #ifdef LIBGPIOD_V2
115
+ version = gpiod_api_version();
116
+ #else
117
+ version = gpiod_version_string();
118
+ #endif
119
+
120
+ if (version == NULL) {
121
+ version = "unknown";
122
+ }
123
+
124
+ napi_status status = napi_create_string_utf8(env, version, NAPI_AUTO_LENGTH, &result);
125
+ if (status != napi_ok) {
126
+ napi_throw_error(env, NULL, "Failed to create version string");
127
+ return NULL;
128
+ }
129
+
130
+ return result;
131
+ }
132
+
133
+ // Fonction: openOutput(chipName, lineNumber, initialValue, bias)
134
+ static napi_value OpenOutput(napi_env env, napi_callback_info info) {
135
+ napi_status status;
136
+ size_t argc = 4;
137
+ napi_value args[4];
138
+ char chip_name[256];
139
+ size_t chip_name_len;
140
+ int line_num, initial_value;
141
+ char bias_str[32] = "disable";
142
+
143
+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
144
+ if (status != napi_ok || argc < 2) {
145
+ napi_throw_error(env, NULL, "Expected chipName and lineNumber arguments");
146
+ return NULL;
147
+ }
148
+
149
+ status = napi_get_value_string_utf8(env, args[0], chip_name, sizeof(chip_name), &chip_name_len);
150
+ if (status != napi_ok) {
151
+ napi_throw_error(env, NULL, "Invalid chip name");
152
+ return NULL;
153
+ }
154
+
155
+ status = napi_get_value_int32(env, args[1], &line_num);
156
+ if (status != napi_ok) {
157
+ napi_throw_error(env, NULL, "Invalid line number");
158
+ return NULL;
159
+ }
160
+
161
+ if (argc >= 3) {
162
+ napi_valuetype valuetype;
163
+ status = napi_typeof(env, args[2], &valuetype);
164
+ if (status == napi_ok && valuetype == napi_number) {
165
+ napi_get_value_int32(env, args[2], &initial_value);
166
+ }
167
+ }
168
+
169
+ if (argc >= 4) {
170
+ napi_valuetype valuetype;
171
+ status = napi_typeof(env, args[3], &valuetype);
172
+ if (status == napi_ok && valuetype == napi_string) {
173
+ napi_get_value_string_utf8(env, args[3], bias_str, sizeof(bias_str), NULL);
174
+ }
175
+ }
176
+
177
+ gpio_context_t *ctx = (gpio_context_t*)malloc(sizeof(gpio_context_t));
178
+ if (!ctx) {
179
+ napi_throw_error(env, NULL, "Memory allocation failed");
180
+ return NULL;
181
+ }
182
+ memset(ctx, 0, sizeof(gpio_context_t));
183
+
184
+ ctx->line_num = line_num;
185
+ ctx->is_output = 1;
186
+ ctx->is_closed = 0;
187
+ ctx->is_monitoring = 0;
188
+ ctx->monitor_thread = 0;
189
+ ctx->tsfn = NULL;
190
+ ctx->callback_ref = NULL;
191
+
192
+ #ifdef LIBGPIOD_V2
193
+ ctx->chip = gpiod_chip_open(chip_name);
194
+ if (!ctx->chip) {
195
+ free(ctx);
196
+ napi_throw_error(env, NULL, "Failed to open GPIO chip (v2)");
197
+ return NULL;
198
+ }
199
+
200
+ ctx->offset = (unsigned int)line_num;
201
+
202
+ // Créer les structures de configuration
203
+ ctx->line_settings = gpiod_line_settings_new();
204
+ ctx->line_cfg = gpiod_line_config_new();
205
+ ctx->req_cfg = gpiod_request_config_new();
206
+
207
+ if (!ctx->line_settings || !ctx->line_cfg || !ctx->req_cfg) {
208
+ if (ctx->line_settings) gpiod_line_settings_free(ctx->line_settings);
209
+ if (ctx->line_cfg) gpiod_line_config_free(ctx->line_cfg);
210
+ if (ctx->req_cfg) gpiod_request_config_free(ctx->req_cfg);
211
+ gpiod_chip_close(ctx->chip);
212
+ free(ctx);
213
+ napi_throw_error(env, NULL, "Failed to create config structures");
214
+ return NULL;
215
+ }
216
+
217
+ // Configurer les line_settings pour une sortie
218
+ gpiod_line_settings_set_direction(ctx->line_settings, GPIOD_LINE_DIRECTION_OUTPUT);
219
+ gpiod_line_settings_set_output_value(ctx->line_settings,
220
+ initial_value ? GPIOD_LINE_VALUE_ACTIVE : GPIOD_LINE_VALUE_INACTIVE);
221
+
222
+ // Ajouter les settings à la config pour cette ligne spécifique
223
+ int ret = gpiod_line_config_add_line_settings(ctx->line_cfg, &ctx->offset, 1, ctx->line_settings);
224
+ if (ret < 0) {
225
+ gpiod_line_settings_free(ctx->line_settings);
226
+ gpiod_line_config_free(ctx->line_cfg);
227
+ gpiod_request_config_free(ctx->req_cfg);
228
+ gpiod_chip_close(ctx->chip);
229
+ free(ctx);
230
+ napi_throw_error(env, NULL, "Failed to add line settings");
231
+ return NULL;
232
+ }
233
+
234
+ // Configurer le consumer
235
+ gpiod_request_config_set_consumer(ctx->req_cfg, "nodejs-gpio");
236
+
237
+ // Demander la ligne
238
+ ctx->request = gpiod_chip_request_lines(ctx->chip, ctx->req_cfg, ctx->line_cfg);
239
+ if (!ctx->request) {
240
+ gpiod_line_settings_free(ctx->line_settings);
241
+ gpiod_line_config_free(ctx->line_cfg);
242
+ gpiod_request_config_free(ctx->req_cfg);
243
+ gpiod_chip_close(ctx->chip);
244
+ free(ctx);
245
+ napi_throw_error(env, NULL, "Failed to request line as output (v2)");
246
+ return NULL;
247
+ }
248
+ #else
249
+ ctx->chip = gpiod_chip_open(chip_name);
250
+ if (!ctx->chip) {
251
+ free(ctx);
252
+ napi_throw_error(env, NULL, "Failed to open GPIO chip (v1)");
253
+ return NULL;
254
+ }
255
+
256
+ ctx->line = gpiod_chip_get_line(ctx->chip, line_num);
257
+ if (!ctx->line) {
258
+ gpiod_chip_close(ctx->chip);
259
+ free(ctx);
260
+ napi_throw_error(env, NULL, "Failed to get GPIO line");
261
+ return NULL;
262
+ }
263
+
264
+ int ret = gpiod_line_request_output(ctx->line, "nodejs-gpio", initial_value);
265
+ if (ret < 0) {
266
+ gpiod_chip_close(ctx->chip);
267
+ free(ctx);
268
+ napi_throw_error(env, NULL, "Failed to request line as output (v1)");
269
+ return NULL;
270
+ }
271
+ #endif
272
+
273
+ napi_value external;
274
+ status = napi_create_external(env, ctx, finalize_gpio, NULL, &external);
275
+ if (status != napi_ok) {
276
+ finalize_gpio(env, ctx, NULL);
277
+ napi_throw_error(env, NULL, "Failed to create external");
278
+ return NULL;
279
+ }
280
+
281
+ return external;
282
+ }
283
+
284
+ // Fonction: openInput(chipName, lineNumber, bias)
285
+ static napi_value OpenInput(napi_env env, napi_callback_info info) {
286
+ napi_status status;
287
+ size_t argc = 3;
288
+ napi_value args[3];
289
+ char chip_name[256];
290
+ size_t chip_name_len;
291
+ int line_num;
292
+ char bias_str[32] = "disable";
293
+
294
+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
295
+ if (status != napi_ok || argc < 2) {
296
+ napi_throw_error(env, NULL, "Expected chipName and lineNumber arguments");
297
+ return NULL;
298
+ }
299
+
300
+ status = napi_get_value_string_utf8(env, args[0], chip_name, sizeof(chip_name), &chip_name_len);
301
+ if (status != napi_ok) {
302
+ napi_throw_error(env, NULL, "Invalid chip name");
303
+ return NULL;
304
+ }
305
+
306
+ status = napi_get_value_int32(env, args[1], &line_num);
307
+ if (status != napi_ok) {
308
+ napi_throw_error(env, NULL, "Invalid line number");
309
+ return NULL;
310
+ }
311
+
312
+ if (argc >= 3) {
313
+ napi_valuetype valuetype;
314
+ status = napi_typeof(env, args[2], &valuetype);
315
+ if (status == napi_ok && valuetype == napi_string) {
316
+ napi_get_value_string_utf8(env, args[2], bias_str, sizeof(bias_str), NULL);
317
+ }
318
+ }
319
+
320
+ gpio_context_t *ctx = (gpio_context_t*)malloc(sizeof(gpio_context_t));
321
+ if (!ctx) {
322
+ napi_throw_error(env, NULL, "Memory allocation failed");
323
+ return NULL;
324
+ }
325
+ memset(ctx, 0, sizeof(gpio_context_t));
326
+
327
+ ctx->line_num = line_num;
328
+ ctx->is_output = 0;
329
+ ctx->is_closed = 0;
330
+ ctx->is_monitoring = 0;
331
+ ctx->monitor_thread = 0;
332
+ ctx->tsfn = NULL;
333
+ ctx->callback_ref = NULL;
334
+
335
+ #ifdef LIBGPIOD_V2
336
+ ctx->chip = gpiod_chip_open(chip_name);
337
+ if (!ctx->chip) {
338
+ free(ctx);
339
+ napi_throw_error(env, NULL, "Failed to open GPIO chip (v2)");
340
+ return NULL;
341
+ }
342
+
343
+ ctx->offset = (unsigned int)line_num;
344
+
345
+ // Créer les structures de configuration
346
+ ctx->line_settings = gpiod_line_settings_new();
347
+ ctx->line_cfg = gpiod_line_config_new();
348
+ ctx->req_cfg = gpiod_request_config_new();
349
+
350
+ if (!ctx->line_settings || !ctx->line_cfg || !ctx->req_cfg) {
351
+ if (ctx->line_settings) gpiod_line_settings_free(ctx->line_settings);
352
+ if (ctx->line_cfg) gpiod_line_config_free(ctx->line_cfg);
353
+ if (ctx->req_cfg) gpiod_request_config_free(ctx->req_cfg);
354
+ gpiod_chip_close(ctx->chip);
355
+ free(ctx);
356
+ napi_throw_error(env, NULL, "Failed to create config structures");
357
+ return NULL;
358
+ }
359
+
360
+ // Configurer les line_settings pour une entrée
361
+ gpiod_line_settings_set_direction(ctx->line_settings, GPIOD_LINE_DIRECTION_INPUT);
362
+
363
+ // Configurer le bias
364
+ if (strcmp(bias_str, "pull-up") == 0) {
365
+ gpiod_line_settings_set_bias(ctx->line_settings, GPIOD_LINE_BIAS_PULL_UP);
366
+ } else if (strcmp(bias_str, "pull-down") == 0) {
367
+ gpiod_line_settings_set_bias(ctx->line_settings, GPIOD_LINE_BIAS_PULL_DOWN);
368
+ } else {
369
+ gpiod_line_settings_set_bias(ctx->line_settings, GPIOD_LINE_BIAS_DISABLED);
370
+ }
371
+
372
+ // Configurer la détection d'événements (both edges)
373
+ gpiod_line_settings_set_edge_detection(ctx->line_settings, GPIOD_LINE_EDGE_BOTH);
374
+
375
+ // Ajouter les settings à la config pour cette ligne spécifique
376
+ int ret = gpiod_line_config_add_line_settings(ctx->line_cfg, &ctx->offset, 1, ctx->line_settings);
377
+ if (ret < 0) {
378
+ gpiod_line_settings_free(ctx->line_settings);
379
+ gpiod_line_config_free(ctx->line_cfg);
380
+ gpiod_request_config_free(ctx->req_cfg);
381
+ gpiod_chip_close(ctx->chip);
382
+ free(ctx);
383
+ napi_throw_error(env, NULL, "Failed to add line settings");
384
+ return NULL;
385
+ }
386
+
387
+ // Configurer le consumer
388
+ gpiod_request_config_set_consumer(ctx->req_cfg, "nodejs-gpio");
389
+
390
+ // Demander la ligne
391
+ ctx->request = gpiod_chip_request_lines(ctx->chip, ctx->req_cfg, ctx->line_cfg);
392
+ if (!ctx->request) {
393
+ gpiod_line_settings_free(ctx->line_settings);
394
+ gpiod_line_config_free(ctx->line_cfg);
395
+ gpiod_request_config_free(ctx->req_cfg);
396
+ gpiod_chip_close(ctx->chip);
397
+ free(ctx);
398
+ napi_throw_error(env, NULL, "Failed to request line as input (v2)");
399
+ return NULL;
400
+ }
401
+ #else
402
+ ctx->chip = gpiod_chip_open(chip_name);
403
+ if (!ctx->chip) {
404
+ free(ctx);
405
+ napi_throw_error(env, NULL, "Failed to open GPIO chip (v1)");
406
+ return NULL;
407
+ }
408
+
409
+ ctx->line = gpiod_chip_get_line(ctx->chip, line_num);
410
+ if (!ctx->line) {
411
+ gpiod_chip_close(ctx->chip);
412
+ free(ctx);
413
+ napi_throw_error(env, NULL, "Failed to get GPIO line");
414
+ return NULL;
415
+ }
416
+
417
+ // Configurer les flags pour libgpiod 1.x
418
+ int flags = 0;
419
+ if (strcmp(bias_str, "pull-up") == 0) {
420
+ flags = GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP;
421
+ } else if (strcmp(bias_str, "pull-down") == 0) {
422
+ flags = GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN;
423
+ } else {
424
+ flags = GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE;
425
+ }
426
+
427
+ // Requête avec événements (both edges)
428
+ int ret = gpiod_line_request_both_edges_events_flags(ctx->line, "nodejs-gpio", flags);
429
+ if (ret < 0) {
430
+ // Si gpiod_line_request_both_edges_events_flags n'existe pas (libgpiod < 1.5),
431
+ // essayer sans flags
432
+ ret = gpiod_line_request_both_edges_events(ctx->line, "nodejs-gpio");
433
+ if (ret < 0) {
434
+ gpiod_chip_close(ctx->chip);
435
+ free(ctx);
436
+ napi_throw_error(env, NULL, "Failed to request line as input with events (v1)");
437
+ return NULL;
438
+ }
439
+ }
440
+ #endif
441
+
442
+ napi_value external;
443
+ status = napi_create_external(env, ctx, finalize_gpio, NULL, &external);
444
+ if (status != napi_ok) {
445
+ finalize_gpio(env, ctx, NULL);
446
+ napi_throw_error(env, NULL, "Failed to create external");
447
+ return NULL;
448
+ }
449
+
450
+ return external;
451
+ }
452
+
453
+ // Thread de monitoring des événements
454
+ static void* monitor_thread_func(void* arg) {
455
+ gpio_context_t *ctx = (gpio_context_t*)arg;
456
+
457
+ #ifdef LIBGPIOD_V2
458
+ struct gpiod_edge_event_buffer *event_buffer = gpiod_edge_event_buffer_new(1);
459
+ if (!event_buffer) return NULL;
460
+
461
+ while (ctx->is_monitoring && !ctx->is_closed) {
462
+ // Timeout de 100ms (100000000 nanosecondes)
463
+ int ret = gpiod_line_request_wait_edge_events(ctx->request, 100000000);
464
+ if (ret > 0) {
465
+ ret = gpiod_line_request_read_edge_events(ctx->request, event_buffer, 1);
466
+ if (ret > 0) {
467
+ struct gpiod_edge_event *event = gpiod_edge_event_buffer_get_event(event_buffer, 0);
468
+ if (event) {
469
+ enum gpiod_edge_event_type edge_type = gpiod_edge_event_get_event_type(event);
470
+
471
+ // Créer les données à passer au callback JavaScript
472
+ int *data = (int*)malloc(sizeof(int));
473
+ if (data) {
474
+ *data = (edge_type == GPIOD_EDGE_EVENT_RISING_EDGE) ? 1 : 0;
475
+ napi_call_threadsafe_function(ctx->tsfn, data, napi_tsfn_blocking);
476
+ }
477
+ }
478
+ }
479
+ } else if (ret < 0 && ret != -ETIMEDOUT) {
480
+ // Erreur autre que timeout
481
+ break;
482
+ }
483
+ }
484
+
485
+ gpiod_edge_event_buffer_free(event_buffer);
486
+ #else
487
+ struct gpiod_line_event event;
488
+ struct timespec timeout;
489
+ timeout.tv_sec = 0;
490
+ timeout.tv_nsec = 100000000; // 100ms
491
+
492
+ while (ctx->is_monitoring && !ctx->is_closed) {
493
+ int ret = gpiod_line_event_wait(ctx->line, &timeout);
494
+ if (ret > 0) {
495
+ ret = gpiod_line_event_read(ctx->line, &event);
496
+ if (ret == 0) {
497
+ int *data = (int*)malloc(sizeof(int));
498
+ if (data) {
499
+ *data = (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE) ? 1 : 0;
500
+ napi_call_threadsafe_function(ctx->tsfn, data, napi_tsfn_blocking);
501
+ }
502
+ }
503
+ }
504
+ }
505
+ #endif
506
+
507
+ return NULL;
508
+ }
509
+
510
+ // Callback appelé depuis le thread JavaScript
511
+ static void call_js_callback(napi_env env, napi_value js_callback, void* context, void* data) {
512
+ if (data == NULL) {
513
+ return;
514
+ }
515
+
516
+ int *edge = (int*)data;
517
+
518
+ if (env != NULL && js_callback != NULL) {
519
+ napi_value argv[1];
520
+ napi_status status = napi_create_int32(env, *edge, &argv[0]);
521
+
522
+ if (status == napi_ok) {
523
+ napi_value global;
524
+ status = napi_get_global(env, &global);
525
+
526
+ if (status == napi_ok) {
527
+ napi_value result;
528
+ napi_call_function(env, global, js_callback, 1, argv, &result);
529
+ }
530
+ }
531
+ }
532
+
533
+ free(data);
534
+ }
535
+
536
+ // Fonction: startMonitoring(handle, callback)
537
+ static napi_value StartMonitoring(napi_env env, napi_callback_info info) {
538
+ napi_status status;
539
+ size_t argc = 2;
540
+ napi_value args[2];
541
+ gpio_context_t *ctx = NULL;
542
+
543
+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
544
+ if (status != napi_ok || argc < 2) {
545
+ napi_throw_error(env, NULL, "Expected handle and callback arguments");
546
+ return NULL;
547
+ }
548
+
549
+ status = napi_get_value_external(env, args[0], (void**)&ctx);
550
+ if (status != napi_ok || ctx == NULL) {
551
+ napi_throw_error(env, NULL, "Invalid GPIO handle");
552
+ return NULL;
553
+ }
554
+
555
+ if (ctx->is_closed) {
556
+ napi_throw_error(env, NULL, "GPIO handle has been closed");
557
+ return NULL;
558
+ }
559
+
560
+ if (ctx->is_output) {
561
+ napi_throw_error(env, NULL, "Cannot monitor output GPIO");
562
+ return NULL;
563
+ }
564
+
565
+ if (ctx->is_monitoring) {
566
+ napi_throw_error(env, NULL, "Monitoring already started");
567
+ return NULL;
568
+ }
569
+
570
+ // Créer une threadsafe function
571
+ napi_value async_resource_name;
572
+ napi_create_string_utf8(env, "GPIOMonitor", NAPI_AUTO_LENGTH, &async_resource_name);
573
+
574
+ status = napi_create_threadsafe_function(
575
+ env,
576
+ args[1],
577
+ NULL,
578
+ async_resource_name,
579
+ 0,
580
+ 1,
581
+ NULL,
582
+ NULL,
583
+ ctx,
584
+ call_js_callback,
585
+ &ctx->tsfn
586
+ );
587
+
588
+ if (status != napi_ok) {
589
+ napi_throw_error(env, NULL, "Failed to create threadsafe function");
590
+ return NULL;
591
+ }
592
+
593
+ // Démarrer le thread de monitoring
594
+ ctx->is_monitoring = 1;
595
+ if (pthread_create(&ctx->monitor_thread, NULL, monitor_thread_func, ctx) != 0) {
596
+ ctx->is_monitoring = 0;
597
+ napi_release_threadsafe_function(ctx->tsfn, napi_tsfn_release);
598
+ ctx->tsfn = NULL;
599
+ napi_throw_error(env, NULL, "Failed to create monitor thread");
600
+ return NULL;
601
+ }
602
+
603
+ napi_value result;
604
+ napi_get_undefined(env, &result);
605
+ return result;
606
+ }
607
+
608
+ // Fonction: stopMonitoring(handle)
609
+ static napi_value StopMonitoring(napi_env env, napi_callback_info info) {
610
+ napi_status status;
611
+ size_t argc = 1;
612
+ napi_value args[1];
613
+ gpio_context_t *ctx = NULL;
614
+
615
+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
616
+ if (status != napi_ok || argc < 1) {
617
+ napi_throw_error(env, NULL, "Expected handle argument");
618
+ return NULL;
619
+ }
620
+
621
+ status = napi_get_value_external(env, args[0], (void**)&ctx);
622
+ if (status != napi_ok || ctx == NULL) {
623
+ napi_value result;
624
+ napi_get_undefined(env, &result);
625
+ return result;
626
+ }
627
+
628
+ if (ctx->is_monitoring) {
629
+ ctx->is_monitoring = 0;
630
+ if (ctx->monitor_thread) {
631
+ pthread_join(ctx->monitor_thread, NULL);
632
+ ctx->monitor_thread = 0;
633
+ }
634
+
635
+ if (ctx->tsfn) {
636
+ napi_release_threadsafe_function(ctx->tsfn, napi_tsfn_abort);
637
+ ctx->tsfn = NULL;
638
+ }
639
+ }
640
+
641
+ napi_value result;
642
+ napi_get_undefined(env, &result);
643
+ return result;
644
+ }
645
+
646
+ // Fonction: write(handle, value)
647
+ static napi_value Write(napi_env env, napi_callback_info info) {
648
+ napi_status status;
649
+ size_t argc = 2;
650
+ napi_value args[2];
651
+ gpio_context_t *ctx;
652
+ int value;
653
+
654
+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
655
+ if (status != napi_ok || argc < 2) {
656
+ napi_throw_error(env, NULL, "Expected handle and value arguments");
657
+ return NULL;
658
+ }
659
+
660
+ status = napi_get_value_external(env, args[0], (void**)&ctx);
661
+ if (status != napi_ok || !ctx) {
662
+ napi_throw_error(env, NULL, "Invalid GPIO handle");
663
+ return NULL;
664
+ }
665
+
666
+ if (ctx->is_closed) {
667
+ napi_throw_error(env, NULL, "GPIO handle has been closed");
668
+ return NULL;
669
+ }
670
+
671
+ if (!ctx->is_output) {
672
+ napi_throw_error(env, NULL, "GPIO line is not configured as output");
673
+ return NULL;
674
+ }
675
+
676
+ status = napi_get_value_int32(env, args[1], &value);
677
+ if (status != napi_ok) {
678
+ napi_throw_error(env, NULL, "Invalid value");
679
+ return NULL;
680
+ }
681
+
682
+ #ifdef LIBGPIOD_V2
683
+ enum gpiod_line_value gpio_value = value ? GPIOD_LINE_VALUE_ACTIVE : GPIOD_LINE_VALUE_INACTIVE;
684
+ int ret = gpiod_line_request_set_value(ctx->request, ctx->offset, gpio_value);
685
+ #else
686
+ int ret = gpiod_line_set_value(ctx->line, value ? 1 : 0);
687
+ #endif
688
+
689
+ if (ret < 0) {
690
+ napi_throw_error(env, NULL, "Failed to set GPIO value");
691
+ return NULL;
692
+ }
693
+
694
+ napi_value result;
695
+ napi_get_undefined(env, &result);
696
+ return result;
697
+ }
698
+
699
+ // Fonction: read(handle)
700
+ static napi_value Read(napi_env env, napi_callback_info info) {
701
+ napi_status status;
702
+ size_t argc = 1;
703
+ napi_value args[1];
704
+ gpio_context_t *ctx;
705
+
706
+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
707
+ if (status != napi_ok || argc < 1) {
708
+ napi_throw_error(env, NULL, "Expected handle argument");
709
+ return NULL;
710
+ }
711
+
712
+ status = napi_get_value_external(env, args[0], (void**)&ctx);
713
+ if (status != napi_ok || !ctx) {
714
+ napi_throw_error(env, NULL, "Invalid GPIO handle");
715
+ return NULL;
716
+ }
717
+
718
+ if (ctx->is_closed) {
719
+ napi_throw_error(env, NULL, "GPIO handle has been closed");
720
+ return NULL;
721
+ }
722
+
723
+ #ifdef LIBGPIOD_V2
724
+ enum gpiod_line_value gpio_value = gpiod_line_request_get_value(ctx->request, ctx->offset);
725
+ if (gpio_value == GPIOD_LINE_VALUE_ERROR) {
726
+ napi_throw_error(env, NULL, "Failed to read GPIO value (v2)");
727
+ return NULL;
728
+ }
729
+ int value = (gpio_value == GPIOD_LINE_VALUE_ACTIVE) ? 1 : 0;
730
+ #else
731
+ int value = gpiod_line_get_value(ctx->line);
732
+ if (value < 0) {
733
+ napi_throw_error(env, NULL, "Failed to read GPIO value (v1)");
734
+ return NULL;
735
+ }
736
+ #endif
737
+
738
+ napi_value result;
739
+ status = napi_create_int32(env, value, &result);
740
+ if (status != napi_ok) {
741
+ napi_throw_error(env, NULL, "Failed to create return value");
742
+ return NULL;
743
+ }
744
+
745
+ return result;
746
+ }
747
+
748
+ // Fonction: close(handle)
749
+ static napi_value Close(napi_env env, napi_callback_info info) {
750
+ napi_status status;
751
+ size_t argc = 1;
752
+ napi_value args[1];
753
+ gpio_context_t *ctx = NULL;
754
+
755
+ status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
756
+ if (status != napi_ok || argc < 1) {
757
+ napi_throw_error(env, NULL, "Expected handle argument");
758
+ return NULL;
759
+ }
760
+
761
+ status = napi_get_value_external(env, args[0], (void**)&ctx);
762
+ if (status != napi_ok || ctx == NULL) {
763
+ napi_value result;
764
+ napi_get_undefined(env, &result);
765
+ return result;
766
+ }
767
+
768
+ if (ctx->is_closed) {
769
+ napi_value result;
770
+ napi_get_undefined(env, &result);
771
+ return result;
772
+ }
773
+
774
+ // Arrêter le monitoring si actif
775
+ if (ctx->is_monitoring) {
776
+ ctx->is_monitoring = 0;
777
+ if (ctx->monitor_thread) {
778
+ pthread_join(ctx->monitor_thread, NULL);
779
+ ctx->monitor_thread = 0;
780
+ }
781
+
782
+ if (ctx->tsfn) {
783
+ napi_release_threadsafe_function(ctx->tsfn, napi_tsfn_abort);
784
+ ctx->tsfn = NULL;
785
+ }
786
+ }
787
+
788
+ #ifdef LIBGPIOD_V2
789
+ if (ctx->request) {
790
+ gpiod_line_request_release(ctx->request);
791
+ ctx->request = NULL;
792
+ }
793
+ if (ctx->line_settings) {
794
+ gpiod_line_settings_free(ctx->line_settings);
795
+ ctx->line_settings = NULL;
796
+ }
797
+ if (ctx->line_cfg) {
798
+ gpiod_line_config_free(ctx->line_cfg);
799
+ ctx->line_cfg = NULL;
800
+ }
801
+ if (ctx->req_cfg) {
802
+ gpiod_request_config_free(ctx->req_cfg);
803
+ ctx->req_cfg = NULL;
804
+ }
805
+ if (ctx->chip) {
806
+ gpiod_chip_close(ctx->chip);
807
+ ctx->chip = NULL;
808
+ }
809
+ #else
810
+ if (ctx->line) {
811
+ gpiod_line_release(ctx->line);
812
+ ctx->line = NULL;
813
+ }
814
+ if (ctx->chip) {
815
+ gpiod_chip_close(ctx->chip);
816
+ ctx->chip = NULL;
817
+ }
818
+ #endif
819
+
820
+ ctx->is_closed = 1;
821
+
822
+ napi_value result;
823
+ napi_get_undefined(env, &result);
824
+ return result;
825
+ }
826
+
827
+ // Initialisation du module
828
+ static napi_value Init(napi_env env, napi_value exports) {
829
+ napi_status status;
830
+ napi_value fn;
831
+
832
+ status = napi_create_function(env, NULL, 0, GetVersion, NULL, &fn);
833
+ if (status == napi_ok) {
834
+ napi_set_named_property(env, exports, "getVersion", fn);
835
+ }
836
+
837
+ status = napi_create_function(env, NULL, 0, OpenOutput, NULL, &fn);
838
+ if (status == napi_ok) {
839
+ napi_set_named_property(env, exports, "openOutput", fn);
840
+ }
841
+
842
+ status = napi_create_function(env, NULL, 0, OpenInput, NULL, &fn);
843
+ if (status == napi_ok) {
844
+ napi_set_named_property(env, exports, "openInput", fn);
845
+ }
846
+
847
+ status = napi_create_function(env, NULL, 0, Write, NULL, &fn);
848
+ if (status == napi_ok) {
849
+ napi_set_named_property(env, exports, "write", fn);
850
+ }
851
+
852
+ status = napi_create_function(env, NULL, 0, Read, NULL, &fn);
853
+ if (status == napi_ok) {
854
+ napi_set_named_property(env, exports, "read", fn);
855
+ }
856
+
857
+ status = napi_create_function(env, NULL, 0, StartMonitoring, NULL, &fn);
858
+ if (status == napi_ok) {
859
+ napi_set_named_property(env, exports, "startMonitoring", fn);
860
+ }
861
+
862
+ status = napi_create_function(env, NULL, 0, StopMonitoring, NULL, &fn);
863
+ if (status == napi_ok) {
864
+ napi_set_named_property(env, exports, "stopMonitoring", fn);
865
+ }
866
+
867
+ status = napi_create_function(env, NULL, 0, Close, NULL, &fn);
868
+ if (status == napi_ok) {
869
+ napi_set_named_property(env, exports, "close", fn);
870
+ }
871
+
872
+ return exports;
873
+ }
874
+
875
+ NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)