i2c 0.3.0 → 0.4.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/.claude/settings.local.json +36 -0
- package/README.md +83 -91
- package/binding.gyp +3 -4
- package/lib/i2c.js +80 -79
- package/package.json +6 -4
- package/src/i2c.cc +801 -192
- package/test/i2c.test.js +445 -0
package/src/i2c.cc
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
#include <
|
|
2
|
-
#include <node_buffer.h>
|
|
3
|
-
#include <nan.h>
|
|
1
|
+
#include <node_api.h>
|
|
4
2
|
#include <fcntl.h>
|
|
5
3
|
#include <stdint.h>
|
|
6
4
|
#include <errno.h>
|
|
@@ -8,300 +6,911 @@
|
|
|
8
6
|
#include <stdio.h>
|
|
9
7
|
#include <unistd.h>
|
|
10
8
|
#include <string.h>
|
|
11
|
-
#include <
|
|
9
|
+
#include <stdlib.h>
|
|
12
10
|
#include "i2c-dev.h"
|
|
13
11
|
|
|
12
|
+
#define MAX_READ_LEN 1024
|
|
13
|
+
#define I2C_ADDR_COUNT 128
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
int fd;
|
|
17
|
-
int8_t addr;
|
|
15
|
+
typedef struct {
|
|
16
|
+
int fd;
|
|
17
|
+
int8_t addr;
|
|
18
|
+
bool is_open;
|
|
19
|
+
} I2CDevice;
|
|
18
20
|
|
|
19
|
-
void
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
static void device_destructor(napi_env env, void* data, void* hint) {
|
|
22
|
+
I2CDevice* device = (I2CDevice*)data;
|
|
23
|
+
if (device->is_open) {
|
|
24
|
+
close(device->fd);
|
|
25
|
+
}
|
|
26
|
+
free(device);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// --- Helper: unwrap device from `this` ---
|
|
30
|
+
|
|
31
|
+
static I2CDevice* unwrap_device(napi_env env, napi_callback_info info) {
|
|
32
|
+
napi_value jsthis;
|
|
33
|
+
napi_get_cb_info(env, info, NULL, NULL, &jsthis, NULL);
|
|
34
|
+
I2CDevice* device;
|
|
35
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
36
|
+
return device;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// --- Helper: check device is open, throw if not ---
|
|
40
|
+
|
|
41
|
+
static bool require_open(napi_env env, I2CDevice* device) {
|
|
42
|
+
if (!device->is_open) {
|
|
43
|
+
napi_throw_error(env, NULL, "Device is not open");
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// --- Constructor ---
|
|
50
|
+
|
|
51
|
+
static napi_value New(napi_env env, napi_callback_info info) {
|
|
52
|
+
napi_value jsthis;
|
|
53
|
+
napi_get_cb_info(env, info, NULL, NULL, &jsthis, NULL);
|
|
54
|
+
|
|
55
|
+
I2CDevice* device = (I2CDevice*)malloc(sizeof(I2CDevice));
|
|
56
|
+
if (!device) {
|
|
57
|
+
napi_throw_error(env, NULL, "Failed to allocate I2CDevice");
|
|
58
|
+
return NULL;
|
|
59
|
+
}
|
|
60
|
+
device->fd = -1;
|
|
61
|
+
device->addr = 0;
|
|
62
|
+
device->is_open = false;
|
|
63
|
+
|
|
64
|
+
napi_wrap(env, jsthis, device, device_destructor, NULL, NULL);
|
|
65
|
+
return jsthis;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// --- Synchronous: SetAddress ---
|
|
69
|
+
|
|
70
|
+
static napi_value SetAddress(napi_env env, napi_callback_info info) {
|
|
71
|
+
size_t argc = 1;
|
|
72
|
+
napi_value argv[1], jsthis;
|
|
73
|
+
napi_get_cb_info(env, info, &argc, argv, &jsthis, NULL);
|
|
22
74
|
|
|
23
|
-
|
|
75
|
+
I2CDevice* device;
|
|
76
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
77
|
+
|
|
78
|
+
if (!require_open(env, device)) return NULL;
|
|
79
|
+
|
|
80
|
+
int32_t addr;
|
|
81
|
+
napi_get_value_int32(env, argv[0], &addr);
|
|
82
|
+
|
|
83
|
+
device->addr = (int8_t)addr;
|
|
84
|
+
int result = ioctl(device->fd, I2C_SLAVE_FORCE, device->addr);
|
|
24
85
|
if (result == -1) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
);
|
|
28
|
-
|
|
86
|
+
char msg[256];
|
|
87
|
+
snprintf(msg, sizeof(msg), "Failed to set address 0x%02x: %s", addr, strerror(errno));
|
|
88
|
+
napi_throw_error(env, NULL, msg);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return NULL;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// --- Synchronous: Close ---
|
|
95
|
+
|
|
96
|
+
static napi_value Close(napi_env env, napi_callback_info info) {
|
|
97
|
+
I2CDevice* device = unwrap_device(env, info);
|
|
98
|
+
if (device->is_open) {
|
|
99
|
+
close(device->fd);
|
|
100
|
+
device->fd = -1;
|
|
101
|
+
device->is_open = false;
|
|
29
102
|
}
|
|
103
|
+
return NULL;
|
|
30
104
|
}
|
|
31
105
|
|
|
32
|
-
|
|
33
|
-
|
|
106
|
+
// ============================================================
|
|
107
|
+
// Async worker infrastructure
|
|
108
|
+
//
|
|
109
|
+
// Every async operation holds a napi_ref to the JS `this` object
|
|
110
|
+
// to prevent garbage collection of the I2CDevice while the
|
|
111
|
+
// worker thread is accessing it.
|
|
112
|
+
//
|
|
113
|
+
// Before queuing work, we snapshot the fd from the device so
|
|
114
|
+
// the worker thread uses a stable copy. If the device is closed
|
|
115
|
+
// concurrently, the worst case is an EBADF error (not a use of
|
|
116
|
+
// a recycled fd), because close() sets device->fd = -1 and the
|
|
117
|
+
// kernel won't reuse an fd until close() returns.
|
|
118
|
+
// ============================================================
|
|
119
|
+
|
|
120
|
+
// --- Open ---
|
|
121
|
+
|
|
122
|
+
typedef struct {
|
|
123
|
+
I2CDevice* device;
|
|
124
|
+
napi_async_work work;
|
|
125
|
+
napi_ref callback_ref;
|
|
126
|
+
napi_ref this_ref;
|
|
127
|
+
char device_path[256];
|
|
128
|
+
int result_fd;
|
|
129
|
+
int err_no;
|
|
130
|
+
} OpenWorkData;
|
|
131
|
+
|
|
132
|
+
static void open_execute(napi_env env, void* data) {
|
|
133
|
+
OpenWorkData* d = (OpenWorkData*)data;
|
|
134
|
+
d->result_fd = open(d->device_path, O_RDWR);
|
|
135
|
+
if (d->result_fd == -1) {
|
|
136
|
+
d->err_no = errno;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static void open_complete(napi_env env, napi_status status, void* data) {
|
|
141
|
+
OpenWorkData* d = (OpenWorkData*)data;
|
|
142
|
+
|
|
143
|
+
napi_value callback, global, argv[1];
|
|
144
|
+
napi_get_reference_value(env, d->callback_ref, &callback);
|
|
145
|
+
napi_get_global(env, &global);
|
|
34
146
|
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
|
|
147
|
+
if (d->result_fd == -1) {
|
|
148
|
+
char msg[512];
|
|
149
|
+
snprintf(msg, sizeof(msg), "Failed to open I2C device %s: %s", d->device_path, strerror(d->err_no));
|
|
150
|
+
napi_value err_msg;
|
|
151
|
+
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &err_msg);
|
|
152
|
+
napi_create_error(env, NULL, err_msg, &argv[0]);
|
|
153
|
+
} else {
|
|
154
|
+
d->device->fd = d->result_fd;
|
|
155
|
+
d->device->is_open = true;
|
|
156
|
+
napi_get_null(env, &argv[0]);
|
|
38
157
|
}
|
|
39
|
-
|
|
40
|
-
|
|
158
|
+
|
|
159
|
+
napi_call_function(env, global, callback, 1, argv, NULL);
|
|
160
|
+
napi_delete_reference(env, d->callback_ref);
|
|
161
|
+
napi_delete_reference(env, d->this_ref);
|
|
162
|
+
napi_delete_async_work(env, d->work);
|
|
163
|
+
free(d);
|
|
41
164
|
}
|
|
42
165
|
|
|
43
|
-
|
|
44
|
-
|
|
166
|
+
static napi_value Open(napi_env env, napi_callback_info info) {
|
|
167
|
+
size_t argc = 2;
|
|
168
|
+
napi_value argv[2], jsthis;
|
|
169
|
+
napi_get_cb_info(env, info, &argc, argv, &jsthis, NULL);
|
|
45
170
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
171
|
+
I2CDevice* device;
|
|
172
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
173
|
+
|
|
174
|
+
OpenWorkData* d = (OpenWorkData*)malloc(sizeof(OpenWorkData));
|
|
175
|
+
if (!d) {
|
|
176
|
+
napi_throw_error(env, NULL, "Failed to allocate memory for open operation");
|
|
177
|
+
return NULL;
|
|
178
|
+
}
|
|
179
|
+
d->device = device;
|
|
180
|
+
d->err_no = 0;
|
|
50
181
|
|
|
51
|
-
|
|
52
|
-
|
|
182
|
+
size_t path_len;
|
|
183
|
+
napi_get_value_string_utf8(env, argv[0], d->device_path, sizeof(d->device_path), &path_len);
|
|
184
|
+
|
|
185
|
+
napi_create_reference(env, argv[1], 1, &d->callback_ref);
|
|
186
|
+
napi_create_reference(env, jsthis, 1, &d->this_ref);
|
|
187
|
+
|
|
188
|
+
napi_value resource_name;
|
|
189
|
+
napi_create_string_utf8(env, "i2c:open", NAPI_AUTO_LENGTH, &resource_name);
|
|
190
|
+
napi_create_async_work(env, NULL, resource_name, open_execute, open_complete, d, &d->work);
|
|
191
|
+
napi_queue_async_work(env, d->work);
|
|
192
|
+
|
|
193
|
+
return NULL;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// --- Scan ---
|
|
197
|
+
|
|
198
|
+
typedef struct {
|
|
199
|
+
I2CDevice* device;
|
|
200
|
+
napi_async_work work;
|
|
201
|
+
napi_ref callback_ref;
|
|
202
|
+
napi_ref this_ref;
|
|
203
|
+
int fd; // snapshot of device->fd at queue time
|
|
204
|
+
int8_t original_addr;
|
|
205
|
+
int results[I2C_ADDR_COUNT];
|
|
206
|
+
} ScanWorkData;
|
|
207
|
+
|
|
208
|
+
static void scan_execute(napi_env env, void* data) {
|
|
209
|
+
ScanWorkData* d = (ScanWorkData*)data;
|
|
210
|
+
int fd = d->fd;
|
|
211
|
+
|
|
212
|
+
for (int i = 0; i < I2C_ADDR_COUNT; i++) {
|
|
213
|
+
if (ioctl(fd, I2C_SLAVE_FORCE, i) < 0) {
|
|
214
|
+
d->results[i] = -1;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
int res;
|
|
53
219
|
if ((i >= 0x30 && i <= 0x37) || (i >= 0x50 && i <= 0x5F)) {
|
|
54
220
|
res = i2c_smbus_read_byte(fd);
|
|
55
|
-
} else {
|
|
221
|
+
} else {
|
|
56
222
|
res = i2c_smbus_write_quick(fd, I2C_SMBUS_WRITE);
|
|
57
223
|
}
|
|
58
|
-
|
|
59
|
-
res = i;
|
|
60
|
-
}
|
|
61
|
-
Nan::Set(results, i, Nan::New<Integer>(res));
|
|
224
|
+
d->results[i] = (res >= 0) ? i : -1;
|
|
62
225
|
}
|
|
63
226
|
|
|
64
|
-
|
|
227
|
+
// Restore original address
|
|
228
|
+
ioctl(fd, I2C_SLAVE_FORCE, d->original_addr);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
static void scan_complete(napi_env env, napi_status status, void* data) {
|
|
232
|
+
ScanWorkData* d = (ScanWorkData*)data;
|
|
233
|
+
|
|
234
|
+
napi_value callback, global, argv[2];
|
|
235
|
+
napi_get_reference_value(env, d->callback_ref, &callback);
|
|
236
|
+
napi_get_global(env, &global);
|
|
65
237
|
|
|
66
|
-
|
|
67
|
-
Local<Value> argv[argc] = { err, results };
|
|
238
|
+
napi_get_null(env, &argv[0]);
|
|
68
239
|
|
|
69
|
-
|
|
240
|
+
napi_value results;
|
|
241
|
+
napi_create_array_with_length(env, I2C_ADDR_COUNT, &results);
|
|
242
|
+
for (int i = 0; i < I2C_ADDR_COUNT; i++) {
|
|
243
|
+
napi_value val;
|
|
244
|
+
napi_create_int32(env, d->results[i], &val);
|
|
245
|
+
napi_set_element(env, results, i, val);
|
|
246
|
+
}
|
|
247
|
+
argv[1] = results;
|
|
70
248
|
|
|
71
|
-
|
|
249
|
+
napi_call_function(env, global, callback, 2, argv, NULL);
|
|
250
|
+
napi_delete_reference(env, d->callback_ref);
|
|
251
|
+
napi_delete_reference(env, d->this_ref);
|
|
252
|
+
napi_delete_async_work(env, d->work);
|
|
253
|
+
free(d);
|
|
72
254
|
}
|
|
73
255
|
|
|
74
|
-
|
|
75
|
-
|
|
256
|
+
static napi_value Scan(napi_env env, napi_callback_info info) {
|
|
257
|
+
size_t argc = 1;
|
|
258
|
+
napi_value argv[1], jsthis;
|
|
259
|
+
napi_get_cb_info(env, info, &argc, argv, &jsthis, NULL);
|
|
260
|
+
|
|
261
|
+
I2CDevice* device;
|
|
262
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
76
263
|
|
|
77
|
-
if (
|
|
78
|
-
|
|
264
|
+
if (!require_open(env, device)) return NULL;
|
|
265
|
+
|
|
266
|
+
ScanWorkData* d = (ScanWorkData*)malloc(sizeof(ScanWorkData));
|
|
267
|
+
if (!d) {
|
|
268
|
+
napi_throw_error(env, NULL, "Failed to allocate memory for scan operation");
|
|
269
|
+
return NULL;
|
|
79
270
|
}
|
|
80
|
-
|
|
271
|
+
d->device = device;
|
|
272
|
+
d->fd = device->fd;
|
|
273
|
+
d->original_addr = device->addr;
|
|
81
274
|
|
|
82
|
-
|
|
83
|
-
|
|
275
|
+
napi_create_reference(env, argv[0], 1, &d->callback_ref);
|
|
276
|
+
napi_create_reference(env, jsthis, 1, &d->this_ref);
|
|
84
277
|
|
|
85
|
-
|
|
86
|
-
|
|
278
|
+
napi_value resource_name;
|
|
279
|
+
napi_create_string_utf8(env, "i2c:scan", NAPI_AUTO_LENGTH, &resource_name);
|
|
280
|
+
napi_create_async_work(env, NULL, resource_name, scan_execute, scan_complete, d, &d->work);
|
|
281
|
+
napi_queue_async_work(env, d->work);
|
|
87
282
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
283
|
+
return NULL;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// --- Read ---
|
|
287
|
+
|
|
288
|
+
typedef struct {
|
|
289
|
+
napi_async_work work;
|
|
290
|
+
napi_ref callback_ref;
|
|
291
|
+
napi_ref this_ref;
|
|
292
|
+
int fd;
|
|
293
|
+
int len;
|
|
294
|
+
uint8_t* buf;
|
|
295
|
+
int bytes_read;
|
|
296
|
+
int err_no;
|
|
297
|
+
} ReadWorkData;
|
|
298
|
+
|
|
299
|
+
static void read_execute(napi_env env, void* data) {
|
|
300
|
+
ReadWorkData* d = (ReadWorkData*)data;
|
|
301
|
+
d->bytes_read = read(d->fd, d->buf, d->len);
|
|
302
|
+
if (d->bytes_read < 0) {
|
|
303
|
+
d->err_no = errno;
|
|
91
304
|
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
static void read_complete(napi_env env, napi_status status, void* data) {
|
|
308
|
+
ReadWorkData* d = (ReadWorkData*)data;
|
|
92
309
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
310
|
+
napi_value callback, global, argv[2];
|
|
311
|
+
napi_get_reference_value(env, d->callback_ref, &callback);
|
|
312
|
+
napi_get_global(env, &global);
|
|
313
|
+
|
|
314
|
+
if (d->bytes_read < 0) {
|
|
315
|
+
char msg[256];
|
|
316
|
+
snprintf(msg, sizeof(msg), "Cannot read from device: %s", strerror(d->err_no));
|
|
317
|
+
napi_value err_msg;
|
|
318
|
+
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &err_msg);
|
|
319
|
+
napi_create_error(env, NULL, err_msg, &argv[0]);
|
|
320
|
+
napi_get_undefined(env, &argv[1]);
|
|
321
|
+
} else {
|
|
322
|
+
napi_get_null(env, &argv[0]);
|
|
323
|
+
napi_value arr;
|
|
324
|
+
napi_create_array_with_length(env, d->bytes_read, &arr);
|
|
325
|
+
for (int i = 0; i < d->bytes_read; i++) {
|
|
326
|
+
napi_value val;
|
|
327
|
+
napi_create_int32(env, d->buf[i], &val);
|
|
328
|
+
napi_set_element(env, arr, i, val);
|
|
329
|
+
}
|
|
330
|
+
argv[1] = arr;
|
|
98
331
|
}
|
|
332
|
+
|
|
333
|
+
napi_call_function(env, global, callback, 2, argv, NULL);
|
|
334
|
+
napi_delete_reference(env, d->callback_ref);
|
|
335
|
+
napi_delete_reference(env, d->this_ref);
|
|
336
|
+
napi_delete_async_work(env, d->work);
|
|
337
|
+
free(d->buf);
|
|
338
|
+
free(d);
|
|
99
339
|
}
|
|
100
340
|
|
|
101
|
-
|
|
102
|
-
|
|
341
|
+
static napi_value Read(napi_env env, napi_callback_info info) {
|
|
342
|
+
size_t argc = 2;
|
|
343
|
+
napi_value argv[2], jsthis;
|
|
344
|
+
napi_get_cb_info(env, info, &argc, argv, &jsthis, NULL);
|
|
103
345
|
|
|
104
|
-
|
|
346
|
+
I2CDevice* device;
|
|
347
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
105
348
|
|
|
106
|
-
|
|
349
|
+
if (!require_open(env, device)) return NULL;
|
|
107
350
|
|
|
108
|
-
|
|
109
|
-
|
|
351
|
+
int32_t len;
|
|
352
|
+
napi_get_value_int32(env, argv[0], &len);
|
|
110
353
|
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
for (int i = 0; i < len; ++i) {
|
|
115
|
-
Nan::Set(data, i, Nan::New<Integer>(buf[i]));
|
|
116
|
-
}
|
|
354
|
+
if (len <= 0 || len > MAX_READ_LEN) {
|
|
355
|
+
napi_throw_range_error(env, NULL, "read length must be between 1 and 1024");
|
|
356
|
+
return NULL;
|
|
117
357
|
}
|
|
118
|
-
delete[] buf;
|
|
119
358
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
Nan::Call(callback, Nan::GetCurrentContext()->Global(), argc, argv);
|
|
359
|
+
ReadWorkData* d = (ReadWorkData*)malloc(sizeof(ReadWorkData));
|
|
360
|
+
if (!d) {
|
|
361
|
+
napi_throw_error(env, NULL, "Failed to allocate memory for read operation");
|
|
362
|
+
return NULL;
|
|
125
363
|
}
|
|
364
|
+
d->fd = device->fd;
|
|
365
|
+
d->len = len;
|
|
366
|
+
d->buf = (uint8_t*)malloc(len);
|
|
367
|
+
if (!d->buf) {
|
|
368
|
+
free(d);
|
|
369
|
+
napi_throw_error(env, NULL, "Failed to allocate read buffer");
|
|
370
|
+
return NULL;
|
|
371
|
+
}
|
|
372
|
+
d->bytes_read = 0;
|
|
373
|
+
d->err_no = 0;
|
|
374
|
+
|
|
375
|
+
napi_create_reference(env, argv[1], 1, &d->callback_ref);
|
|
376
|
+
napi_create_reference(env, jsthis, 1, &d->this_ref);
|
|
377
|
+
|
|
378
|
+
napi_value resource_name;
|
|
379
|
+
napi_create_string_utf8(env, "i2c:read", NAPI_AUTO_LENGTH, &resource_name);
|
|
380
|
+
napi_create_async_work(env, NULL, resource_name, read_execute, read_complete, d, &d->work);
|
|
381
|
+
napi_queue_async_work(env, d->work);
|
|
382
|
+
|
|
383
|
+
return NULL;
|
|
126
384
|
}
|
|
127
385
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
386
|
+
// --- ReadByte ---
|
|
387
|
+
|
|
388
|
+
typedef struct {
|
|
389
|
+
napi_async_work work;
|
|
390
|
+
napi_ref callback_ref;
|
|
391
|
+
napi_ref this_ref;
|
|
392
|
+
int fd;
|
|
393
|
+
int32_t result;
|
|
394
|
+
int err_no;
|
|
395
|
+
} ReadByteWorkData;
|
|
396
|
+
|
|
397
|
+
static void read_byte_execute(napi_env env, void* data) {
|
|
398
|
+
ReadByteWorkData* d = (ReadByteWorkData*)data;
|
|
399
|
+
d->result = i2c_smbus_read_byte(d->fd);
|
|
400
|
+
if (d->result == -1) {
|
|
401
|
+
d->err_no = errno;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
133
404
|
|
|
134
|
-
|
|
405
|
+
static void read_byte_complete(napi_env env, napi_status status, void* data) {
|
|
406
|
+
ReadByteWorkData* d = (ReadByteWorkData*)data;
|
|
135
407
|
|
|
136
|
-
|
|
137
|
-
|
|
408
|
+
napi_value callback, global, argv[2];
|
|
409
|
+
napi_get_reference_value(env, d->callback_ref, &callback);
|
|
410
|
+
napi_get_global(env, &global);
|
|
411
|
+
|
|
412
|
+
if (d->result == -1) {
|
|
413
|
+
char msg[256];
|
|
414
|
+
snprintf(msg, sizeof(msg), "Cannot read byte from device: %s", strerror(d->err_no));
|
|
415
|
+
napi_value err_msg;
|
|
416
|
+
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &err_msg);
|
|
417
|
+
napi_create_error(env, NULL, err_msg, &argv[0]);
|
|
418
|
+
napi_get_undefined(env, &argv[1]);
|
|
138
419
|
} else {
|
|
139
|
-
|
|
420
|
+
napi_get_null(env, &argv[0]);
|
|
421
|
+
napi_create_int32(env, d->result, &argv[1]);
|
|
140
422
|
}
|
|
141
423
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
424
|
+
napi_call_function(env, global, callback, 2, argv, NULL);
|
|
425
|
+
napi_delete_reference(env, d->callback_ref);
|
|
426
|
+
napi_delete_reference(env, d->this_ref);
|
|
427
|
+
napi_delete_async_work(env, d->work);
|
|
428
|
+
free(d);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
static napi_value ReadByte(napi_env env, napi_callback_info info) {
|
|
432
|
+
size_t argc = 1;
|
|
433
|
+
napi_value argv[1], jsthis;
|
|
434
|
+
napi_get_cb_info(env, info, &argc, argv, &jsthis, NULL);
|
|
435
|
+
|
|
436
|
+
I2CDevice* device;
|
|
437
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
438
|
+
|
|
439
|
+
if (!require_open(env, device)) return NULL;
|
|
440
|
+
|
|
441
|
+
ReadByteWorkData* d = (ReadByteWorkData*)malloc(sizeof(ReadByteWorkData));
|
|
442
|
+
if (!d) {
|
|
443
|
+
napi_throw_error(env, NULL, "Failed to allocate memory for readByte operation");
|
|
444
|
+
return NULL;
|
|
147
445
|
}
|
|
446
|
+
d->fd = device->fd;
|
|
447
|
+
d->result = 0;
|
|
448
|
+
d->err_no = 0;
|
|
449
|
+
|
|
450
|
+
napi_create_reference(env, argv[0], 1, &d->callback_ref);
|
|
451
|
+
napi_create_reference(env, jsthis, 1, &d->this_ref);
|
|
452
|
+
|
|
453
|
+
napi_value resource_name;
|
|
454
|
+
napi_create_string_utf8(env, "i2c:readByte", NAPI_AUTO_LENGTH, &resource_name);
|
|
455
|
+
napi_create_async_work(env, NULL, resource_name, read_byte_execute, read_byte_complete, d, &d->work);
|
|
456
|
+
napi_queue_async_work(env, d->work);
|
|
148
457
|
|
|
149
|
-
|
|
458
|
+
return NULL;
|
|
150
459
|
}
|
|
151
460
|
|
|
152
|
-
|
|
153
|
-
|
|
461
|
+
// --- ReadBlock (single shot, no loop) ---
|
|
462
|
+
|
|
463
|
+
typedef struct {
|
|
464
|
+
napi_async_work work;
|
|
465
|
+
napi_ref callback_ref;
|
|
466
|
+
napi_ref this_ref;
|
|
467
|
+
int fd;
|
|
468
|
+
uint8_t cmd;
|
|
469
|
+
uint8_t len;
|
|
470
|
+
uint8_t buf[I2C_SMBUS_BLOCK_MAX];
|
|
471
|
+
int32_t bytes_read;
|
|
472
|
+
int err_no;
|
|
473
|
+
} ReadBlockWorkData;
|
|
474
|
+
|
|
475
|
+
static void read_block_execute(napi_env env, void* data) {
|
|
476
|
+
ReadBlockWorkData* d = (ReadBlockWorkData*)data;
|
|
477
|
+
d->bytes_read = i2c_smbus_read_i2c_block_data(d->fd, d->cmd, d->len, d->buf);
|
|
478
|
+
if (d->bytes_read == -1) {
|
|
479
|
+
d->err_no = errno;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
154
482
|
|
|
155
|
-
|
|
156
|
-
|
|
483
|
+
static void read_block_complete(napi_env env, napi_status status, void* data) {
|
|
484
|
+
ReadBlockWorkData* d = (ReadBlockWorkData*)data;
|
|
157
485
|
|
|
158
|
-
|
|
159
|
-
|
|
486
|
+
napi_value callback, global, argv[2];
|
|
487
|
+
napi_get_reference_value(env, d->callback_ref, &callback);
|
|
488
|
+
napi_get_global(env, &global);
|
|
160
489
|
|
|
161
|
-
|
|
490
|
+
if (d->bytes_read == -1) {
|
|
491
|
+
char msg[256];
|
|
492
|
+
snprintf(msg, sizeof(msg), "Cannot read block from device: %s", strerror(d->err_no));
|
|
493
|
+
napi_value err_msg;
|
|
494
|
+
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &err_msg);
|
|
495
|
+
napi_create_error(env, NULL, err_msg, &argv[0]);
|
|
496
|
+
napi_get_undefined(env, &argv[1]);
|
|
497
|
+
} else {
|
|
498
|
+
napi_get_null(env, &argv[0]);
|
|
499
|
+
void* buf_data;
|
|
500
|
+
napi_value buffer;
|
|
501
|
+
napi_create_buffer_copy(env, d->bytes_read, d->buf, &buf_data, &buffer);
|
|
502
|
+
argv[1] = buffer;
|
|
503
|
+
}
|
|
162
504
|
|
|
505
|
+
napi_call_function(env, global, callback, 2, argv, NULL);
|
|
506
|
+
napi_delete_reference(env, d->callback_ref);
|
|
507
|
+
napi_delete_reference(env, d->this_ref);
|
|
508
|
+
napi_delete_async_work(env, d->work);
|
|
509
|
+
free(d);
|
|
510
|
+
}
|
|
163
511
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
512
|
+
static napi_value ReadBlock(napi_env env, napi_callback_info info) {
|
|
513
|
+
size_t argc = 3;
|
|
514
|
+
napi_value argv[3], jsthis;
|
|
515
|
+
napi_get_cb_info(env, info, &argc, argv, &jsthis, NULL);
|
|
168
516
|
|
|
169
|
-
|
|
517
|
+
I2CDevice* device;
|
|
518
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
170
519
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
520
|
+
if (!require_open(env, device)) return NULL;
|
|
521
|
+
|
|
522
|
+
int32_t cmd, len;
|
|
523
|
+
napi_get_value_int32(env, argv[0], &cmd);
|
|
524
|
+
napi_get_value_int32(env, argv[1], &len);
|
|
525
|
+
|
|
526
|
+
if (len <= 0 || len > I2C_SMBUS_BLOCK_MAX) {
|
|
527
|
+
napi_throw_range_error(env, NULL, "readBlock length must be between 1 and 32");
|
|
528
|
+
return NULL;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
ReadBlockWorkData* d = (ReadBlockWorkData*)malloc(sizeof(ReadBlockWorkData));
|
|
532
|
+
if (!d) {
|
|
533
|
+
napi_throw_error(env, NULL, "Failed to allocate memory for readBlock operation");
|
|
534
|
+
return NULL;
|
|
184
535
|
}
|
|
536
|
+
d->fd = device->fd;
|
|
537
|
+
d->cmd = (uint8_t)cmd;
|
|
538
|
+
d->len = (uint8_t)len;
|
|
539
|
+
d->bytes_read = 0;
|
|
540
|
+
d->err_no = 0;
|
|
541
|
+
|
|
542
|
+
napi_create_reference(env, argv[2], 1, &d->callback_ref);
|
|
543
|
+
napi_create_reference(env, jsthis, 1, &d->this_ref);
|
|
544
|
+
|
|
545
|
+
napi_value resource_name;
|
|
546
|
+
napi_create_string_utf8(env, "i2c:readBlock", NAPI_AUTO_LENGTH, &resource_name);
|
|
547
|
+
napi_create_async_work(env, NULL, resource_name, read_block_execute, read_block_complete, d, &d->work);
|
|
548
|
+
napi_queue_async_work(env, d->work);
|
|
549
|
+
|
|
550
|
+
return NULL;
|
|
551
|
+
}
|
|
185
552
|
|
|
186
|
-
|
|
553
|
+
// --- Write ---
|
|
554
|
+
|
|
555
|
+
typedef struct {
|
|
556
|
+
napi_async_work work;
|
|
557
|
+
napi_ref callback_ref;
|
|
558
|
+
napi_ref this_ref;
|
|
559
|
+
napi_ref buffer_ref;
|
|
560
|
+
int fd;
|
|
561
|
+
uint8_t* buf;
|
|
562
|
+
size_t len;
|
|
563
|
+
ssize_t bytes_written;
|
|
564
|
+
int err_no;
|
|
565
|
+
} WriteWorkData;
|
|
566
|
+
|
|
567
|
+
static void write_execute(napi_env env, void* data) {
|
|
568
|
+
WriteWorkData* d = (WriteWorkData*)data;
|
|
569
|
+
d->bytes_written = write(d->fd, d->buf, d->len);
|
|
570
|
+
if (d->bytes_written < 0) {
|
|
571
|
+
d->err_no = errno;
|
|
572
|
+
}
|
|
187
573
|
}
|
|
188
574
|
|
|
189
|
-
void
|
|
190
|
-
|
|
575
|
+
static void write_complete(napi_env env, napi_status status, void* data) {
|
|
576
|
+
WriteWorkData* d = (WriteWorkData*)data;
|
|
577
|
+
|
|
578
|
+
napi_value callback, global, argv[1];
|
|
579
|
+
napi_get_reference_value(env, d->callback_ref, &callback);
|
|
580
|
+
napi_get_global(env, &global);
|
|
581
|
+
|
|
582
|
+
if (d->bytes_written < 0) {
|
|
583
|
+
char msg[256];
|
|
584
|
+
snprintf(msg, sizeof(msg), "Cannot write to device: %s", strerror(d->err_no));
|
|
585
|
+
napi_value err_msg;
|
|
586
|
+
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &err_msg);
|
|
587
|
+
napi_create_error(env, NULL, err_msg, &argv[0]);
|
|
588
|
+
} else if ((size_t)d->bytes_written != d->len) {
|
|
589
|
+
char msg[256];
|
|
590
|
+
snprintf(msg, sizeof(msg), "Partial write to device: wrote %zd of %zu bytes",
|
|
591
|
+
d->bytes_written, d->len);
|
|
592
|
+
napi_value err_msg;
|
|
593
|
+
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &err_msg);
|
|
594
|
+
napi_create_error(env, NULL, err_msg, &argv[0]);
|
|
595
|
+
} else {
|
|
596
|
+
napi_get_null(env, &argv[0]);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
napi_call_function(env, global, callback, 1, argv, NULL);
|
|
600
|
+
napi_delete_reference(env, d->callback_ref);
|
|
601
|
+
napi_delete_reference(env, d->this_ref);
|
|
602
|
+
napi_delete_reference(env, d->buffer_ref);
|
|
603
|
+
napi_delete_async_work(env, d->work);
|
|
604
|
+
free(d);
|
|
605
|
+
}
|
|
191
606
|
|
|
192
|
-
|
|
607
|
+
static napi_value Write(napi_env env, napi_callback_info info) {
|
|
608
|
+
size_t argc = 2;
|
|
609
|
+
napi_value argv[2], jsthis;
|
|
610
|
+
napi_get_cb_info(env, info, &argc, argv, &jsthis, NULL);
|
|
193
611
|
|
|
194
|
-
|
|
195
|
-
|
|
612
|
+
I2CDevice* device;
|
|
613
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
196
614
|
|
|
197
|
-
|
|
615
|
+
if (!require_open(env, device)) return NULL;
|
|
198
616
|
|
|
199
|
-
|
|
200
|
-
|
|
617
|
+
WriteWorkData* d = (WriteWorkData*)malloc(sizeof(WriteWorkData));
|
|
618
|
+
if (!d) {
|
|
619
|
+
napi_throw_error(env, NULL, "Failed to allocate memory for write operation");
|
|
620
|
+
return NULL;
|
|
201
621
|
}
|
|
622
|
+
d->fd = device->fd;
|
|
623
|
+
|
|
624
|
+
napi_get_buffer_info(env, argv[0], (void**)&d->buf, &d->len);
|
|
625
|
+
// Hold a reference to the buffer to prevent GC during async work
|
|
626
|
+
napi_create_reference(env, argv[0], 1, &d->buffer_ref);
|
|
627
|
+
|
|
628
|
+
d->bytes_written = 0;
|
|
629
|
+
d->err_no = 0;
|
|
202
630
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
631
|
+
napi_create_reference(env, argv[1], 1, &d->callback_ref);
|
|
632
|
+
napi_create_reference(env, jsthis, 1, &d->this_ref);
|
|
633
|
+
|
|
634
|
+
napi_value resource_name;
|
|
635
|
+
napi_create_string_utf8(env, "i2c:write", NAPI_AUTO_LENGTH, &resource_name);
|
|
636
|
+
napi_create_async_work(env, NULL, resource_name, write_execute, write_complete, d, &d->work);
|
|
637
|
+
napi_queue_async_work(env, d->work);
|
|
638
|
+
|
|
639
|
+
return NULL;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// --- WriteByte ---
|
|
643
|
+
|
|
644
|
+
typedef struct {
|
|
645
|
+
napi_async_work work;
|
|
646
|
+
napi_ref callback_ref;
|
|
647
|
+
napi_ref this_ref;
|
|
648
|
+
int fd;
|
|
649
|
+
uint8_t byte;
|
|
650
|
+
int32_t result;
|
|
651
|
+
int err_no;
|
|
652
|
+
} WriteByteWorkData;
|
|
653
|
+
|
|
654
|
+
static void write_byte_execute(napi_env env, void* data) {
|
|
655
|
+
WriteByteWorkData* d = (WriteByteWorkData*)data;
|
|
656
|
+
d->result = i2c_smbus_write_byte(d->fd, d->byte);
|
|
657
|
+
if (d->result == -1) {
|
|
658
|
+
d->err_no = errno;
|
|
208
659
|
}
|
|
209
660
|
}
|
|
210
661
|
|
|
211
|
-
void
|
|
212
|
-
|
|
662
|
+
static void write_byte_complete(napi_env env, napi_status status, void* data) {
|
|
663
|
+
WriteByteWorkData* d = (WriteByteWorkData*)data;
|
|
213
664
|
|
|
214
|
-
|
|
215
|
-
|
|
665
|
+
napi_value callback, global, argv[1];
|
|
666
|
+
napi_get_reference_value(env, d->callback_ref, &callback);
|
|
667
|
+
napi_get_global(env, &global);
|
|
216
668
|
|
|
217
|
-
if (
|
|
218
|
-
|
|
669
|
+
if (d->result == -1) {
|
|
670
|
+
char msg[256];
|
|
671
|
+
snprintf(msg, sizeof(msg), "Cannot write byte to device: %s", strerror(d->err_no));
|
|
672
|
+
napi_value err_msg;
|
|
673
|
+
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &err_msg);
|
|
674
|
+
napi_create_error(env, NULL, err_msg, &argv[0]);
|
|
675
|
+
} else {
|
|
676
|
+
napi_get_null(env, &argv[0]);
|
|
219
677
|
}
|
|
220
678
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
679
|
+
napi_call_function(env, global, callback, 1, argv, NULL);
|
|
680
|
+
napi_delete_reference(env, d->callback_ref);
|
|
681
|
+
napi_delete_reference(env, d->this_ref);
|
|
682
|
+
napi_delete_async_work(env, d->work);
|
|
683
|
+
free(d);
|
|
227
684
|
}
|
|
228
685
|
|
|
229
|
-
|
|
230
|
-
|
|
686
|
+
static napi_value WriteByte(napi_env env, napi_callback_info info) {
|
|
687
|
+
size_t argc = 2;
|
|
688
|
+
napi_value argv[2], jsthis;
|
|
689
|
+
napi_get_cb_info(env, info, &argc, argv, &jsthis, NULL);
|
|
690
|
+
|
|
691
|
+
I2CDevice* device;
|
|
692
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
693
|
+
|
|
694
|
+
if (!require_open(env, device)) return NULL;
|
|
231
695
|
|
|
232
|
-
|
|
233
|
-
|
|
696
|
+
int32_t byte;
|
|
697
|
+
napi_get_value_int32(env, argv[0], &byte);
|
|
234
698
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
699
|
+
WriteByteWorkData* d = (WriteByteWorkData*)malloc(sizeof(WriteByteWorkData));
|
|
700
|
+
if (!d) {
|
|
701
|
+
napi_throw_error(env, NULL, "Failed to allocate memory for writeByte operation");
|
|
702
|
+
return NULL;
|
|
703
|
+
}
|
|
704
|
+
d->fd = device->fd;
|
|
705
|
+
d->byte = (uint8_t)byte;
|
|
706
|
+
d->result = 0;
|
|
707
|
+
d->err_no = 0;
|
|
708
|
+
|
|
709
|
+
napi_create_reference(env, argv[1], 1, &d->callback_ref);
|
|
710
|
+
napi_create_reference(env, jsthis, 1, &d->this_ref);
|
|
238
711
|
|
|
239
|
-
|
|
712
|
+
napi_value resource_name;
|
|
713
|
+
napi_create_string_utf8(env, "i2c:writeByte", NAPI_AUTO_LENGTH, &resource_name);
|
|
714
|
+
napi_create_async_work(env, NULL, resource_name, write_byte_execute, write_byte_complete, d, &d->work);
|
|
715
|
+
napi_queue_async_work(env, d->work);
|
|
716
|
+
|
|
717
|
+
return NULL;
|
|
718
|
+
}
|
|
240
719
|
|
|
241
|
-
|
|
242
|
-
|
|
720
|
+
// --- WriteBlock ---
|
|
721
|
+
|
|
722
|
+
typedef struct {
|
|
723
|
+
napi_async_work work;
|
|
724
|
+
napi_ref callback_ref;
|
|
725
|
+
napi_ref this_ref;
|
|
726
|
+
napi_ref buffer_ref;
|
|
727
|
+
int fd;
|
|
728
|
+
uint8_t cmd;
|
|
729
|
+
uint8_t* buf;
|
|
730
|
+
size_t len;
|
|
731
|
+
int32_t result;
|
|
732
|
+
int err_no;
|
|
733
|
+
} WriteBlockWorkData;
|
|
734
|
+
|
|
735
|
+
static void write_block_execute(napi_env env, void* data) {
|
|
736
|
+
WriteBlockWorkData* d = (WriteBlockWorkData*)data;
|
|
737
|
+
d->result = i2c_smbus_write_i2c_block_data(d->fd, d->cmd, d->len, d->buf);
|
|
738
|
+
if (d->result == -1) {
|
|
739
|
+
d->err_no = errno;
|
|
243
740
|
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
static void write_block_complete(napi_env env, napi_status status, void* data) {
|
|
744
|
+
WriteBlockWorkData* d = (WriteBlockWorkData*)data;
|
|
244
745
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
746
|
+
napi_value callback, global, argv[1];
|
|
747
|
+
napi_get_reference_value(env, d->callback_ref, &callback);
|
|
748
|
+
napi_get_global(env, &global);
|
|
749
|
+
|
|
750
|
+
if (d->result == -1) {
|
|
751
|
+
char msg[256];
|
|
752
|
+
snprintf(msg, sizeof(msg), "Cannot write block to device: %s", strerror(d->err_no));
|
|
753
|
+
napi_value err_msg;
|
|
754
|
+
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &err_msg);
|
|
755
|
+
napi_create_error(env, NULL, err_msg, &argv[0]);
|
|
756
|
+
} else {
|
|
757
|
+
napi_get_null(env, &argv[0]);
|
|
250
758
|
}
|
|
759
|
+
|
|
760
|
+
napi_call_function(env, global, callback, 1, argv, NULL);
|
|
761
|
+
napi_delete_reference(env, d->callback_ref);
|
|
762
|
+
napi_delete_reference(env, d->this_ref);
|
|
763
|
+
napi_delete_reference(env, d->buffer_ref);
|
|
764
|
+
napi_delete_async_work(env, d->work);
|
|
765
|
+
free(d);
|
|
251
766
|
}
|
|
252
767
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
768
|
+
static napi_value WriteBlock(napi_env env, napi_callback_info info) {
|
|
769
|
+
size_t argc = 3;
|
|
770
|
+
napi_value argv[3], jsthis;
|
|
771
|
+
napi_get_cb_info(env, info, &argc, argv, &jsthis, NULL);
|
|
772
|
+
|
|
773
|
+
I2CDevice* device;
|
|
774
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
775
|
+
|
|
776
|
+
if (!require_open(env, device)) return NULL;
|
|
777
|
+
|
|
778
|
+
int32_t cmd;
|
|
779
|
+
napi_get_value_int32(env, argv[0], &cmd);
|
|
258
780
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
781
|
+
WriteBlockWorkData* d = (WriteBlockWorkData*)malloc(sizeof(WriteBlockWorkData));
|
|
782
|
+
if (!d) {
|
|
783
|
+
napi_throw_error(env, NULL, "Failed to allocate memory for writeBlock operation");
|
|
784
|
+
return NULL;
|
|
263
785
|
}
|
|
786
|
+
d->fd = device->fd;
|
|
787
|
+
d->cmd = (uint8_t)cmd;
|
|
788
|
+
|
|
789
|
+
napi_get_buffer_info(env, argv[1], (void**)&d->buf, &d->len);
|
|
790
|
+
napi_create_reference(env, argv[1], 1, &d->buffer_ref);
|
|
791
|
+
|
|
792
|
+
d->result = 0;
|
|
793
|
+
d->err_no = 0;
|
|
794
|
+
|
|
795
|
+
napi_create_reference(env, argv[2], 1, &d->callback_ref);
|
|
796
|
+
napi_create_reference(env, jsthis, 1, &d->this_ref);
|
|
797
|
+
|
|
798
|
+
napi_value resource_name;
|
|
799
|
+
napi_create_string_utf8(env, "i2c:writeBlock", NAPI_AUTO_LENGTH, &resource_name);
|
|
800
|
+
napi_create_async_work(env, NULL, resource_name, write_block_execute, write_block_complete, d, &d->work);
|
|
801
|
+
napi_queue_async_work(env, d->work);
|
|
264
802
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
803
|
+
return NULL;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// --- WriteWord ---
|
|
807
|
+
|
|
808
|
+
typedef struct {
|
|
809
|
+
napi_async_work work;
|
|
810
|
+
napi_ref callback_ref;
|
|
811
|
+
napi_ref this_ref;
|
|
812
|
+
int fd;
|
|
813
|
+
uint8_t cmd;
|
|
814
|
+
uint16_t word;
|
|
815
|
+
int32_t result;
|
|
816
|
+
int err_no;
|
|
817
|
+
} WriteWordWorkData;
|
|
818
|
+
|
|
819
|
+
static void write_word_execute(napi_env env, void* data) {
|
|
820
|
+
WriteWordWorkData* d = (WriteWordWorkData*)data;
|
|
821
|
+
d->result = i2c_smbus_write_word_data(d->fd, d->cmd, d->word);
|
|
822
|
+
if (d->result == -1) {
|
|
823
|
+
d->err_no = errno;
|
|
270
824
|
}
|
|
271
825
|
}
|
|
272
826
|
|
|
273
|
-
|
|
827
|
+
static void write_word_complete(napi_env env, napi_status status, void* data) {
|
|
828
|
+
WriteWordWorkData* d = (WriteWordWorkData*)data;
|
|
274
829
|
|
|
275
|
-
|
|
276
|
-
|
|
830
|
+
napi_value callback, global, argv[1];
|
|
831
|
+
napi_get_reference_value(env, d->callback_ref, &callback);
|
|
832
|
+
napi_get_global(env, &global);
|
|
833
|
+
|
|
834
|
+
if (d->result == -1) {
|
|
835
|
+
char msg[256];
|
|
836
|
+
snprintf(msg, sizeof(msg), "Cannot write word to device: %s", strerror(d->err_no));
|
|
837
|
+
napi_value err_msg;
|
|
838
|
+
napi_create_string_utf8(env, msg, NAPI_AUTO_LENGTH, &err_msg);
|
|
839
|
+
napi_create_error(env, NULL, err_msg, &argv[0]);
|
|
840
|
+
} else {
|
|
841
|
+
napi_get_null(env, &argv[0]);
|
|
842
|
+
}
|
|
277
843
|
|
|
278
|
-
|
|
279
|
-
|
|
844
|
+
napi_call_function(env, global, callback, 1, argv, NULL);
|
|
845
|
+
napi_delete_reference(env, d->callback_ref);
|
|
846
|
+
napi_delete_reference(env, d->this_ref);
|
|
847
|
+
napi_delete_async_work(env, d->work);
|
|
848
|
+
free(d);
|
|
849
|
+
}
|
|
280
850
|
|
|
281
|
-
|
|
282
|
-
|
|
851
|
+
static napi_value WriteWord(napi_env env, napi_callback_info info) {
|
|
852
|
+
size_t argc = 3;
|
|
853
|
+
napi_value argv[3], jsthis;
|
|
854
|
+
napi_get_cb_info(env, info, &argc, argv, &jsthis, NULL);
|
|
283
855
|
|
|
284
|
-
|
|
285
|
-
|
|
856
|
+
I2CDevice* device;
|
|
857
|
+
napi_unwrap(env, jsthis, (void**)&device);
|
|
286
858
|
|
|
287
|
-
|
|
288
|
-
Nan::GetFunction(Nan::New<FunctionTemplate>(Write)).ToLocalChecked());
|
|
859
|
+
if (!require_open(env, device)) return NULL;
|
|
289
860
|
|
|
290
|
-
|
|
291
|
-
|
|
861
|
+
int32_t cmd, word;
|
|
862
|
+
napi_get_value_int32(env, argv[0], &cmd);
|
|
863
|
+
napi_get_value_int32(env, argv[1], &word);
|
|
292
864
|
|
|
293
|
-
|
|
294
|
-
|
|
865
|
+
WriteWordWorkData* d = (WriteWordWorkData*)malloc(sizeof(WriteWordWorkData));
|
|
866
|
+
if (!d) {
|
|
867
|
+
napi_throw_error(env, NULL, "Failed to allocate memory for writeWord operation");
|
|
868
|
+
return NULL;
|
|
869
|
+
}
|
|
870
|
+
d->fd = device->fd;
|
|
871
|
+
d->cmd = (uint8_t)cmd;
|
|
872
|
+
d->word = (uint16_t)word;
|
|
873
|
+
d->result = 0;
|
|
874
|
+
d->err_no = 0;
|
|
295
875
|
|
|
296
|
-
|
|
297
|
-
|
|
876
|
+
napi_create_reference(env, argv[2], 1, &d->callback_ref);
|
|
877
|
+
napi_create_reference(env, jsthis, 1, &d->this_ref);
|
|
298
878
|
|
|
299
|
-
|
|
300
|
-
|
|
879
|
+
napi_value resource_name;
|
|
880
|
+
napi_create_string_utf8(env, "i2c:writeWord", NAPI_AUTO_LENGTH, &resource_name);
|
|
881
|
+
napi_create_async_work(env, NULL, resource_name, write_word_execute, write_word_complete, d, &d->work);
|
|
882
|
+
napi_queue_async_work(env, d->work);
|
|
301
883
|
|
|
302
|
-
|
|
303
|
-
|
|
884
|
+
return NULL;
|
|
885
|
+
}
|
|
304
886
|
|
|
887
|
+
// ============================================================
|
|
888
|
+
// Module initialization
|
|
889
|
+
// ============================================================
|
|
890
|
+
|
|
891
|
+
static napi_value Init(napi_env env, napi_value exports) {
|
|
892
|
+
napi_value cons;
|
|
893
|
+
|
|
894
|
+
napi_property_descriptor methods[] = {
|
|
895
|
+
{ "open", NULL, Open, NULL, NULL, NULL, napi_default, NULL },
|
|
896
|
+
{ "close", NULL, Close, NULL, NULL, NULL, napi_default, NULL },
|
|
897
|
+
{ "setAddress", NULL, SetAddress, NULL, NULL, NULL, napi_default, NULL },
|
|
898
|
+
{ "scan", NULL, Scan, NULL, NULL, NULL, napi_default, NULL },
|
|
899
|
+
{ "read", NULL, Read, NULL, NULL, NULL, napi_default, NULL },
|
|
900
|
+
{ "readByte", NULL, ReadByte, NULL, NULL, NULL, napi_default, NULL },
|
|
901
|
+
{ "readBlock", NULL, ReadBlock, NULL, NULL, NULL, napi_default, NULL },
|
|
902
|
+
{ "write", NULL, Write, NULL, NULL, NULL, napi_default, NULL },
|
|
903
|
+
{ "writeByte", NULL, WriteByte, NULL, NULL, NULL, napi_default, NULL },
|
|
904
|
+
{ "writeBlock", NULL, WriteBlock, NULL, NULL, NULL, napi_default, NULL },
|
|
905
|
+
{ "writeWord", NULL, WriteWord, NULL, NULL, NULL, napi_default, NULL },
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
napi_define_class(env, "I2CDevice", NAPI_AUTO_LENGTH, New, NULL,
|
|
909
|
+
sizeof(methods) / sizeof(methods[0]), methods, &cons);
|
|
910
|
+
|
|
911
|
+
napi_set_named_property(env, exports, "I2CDevice", cons);
|
|
912
|
+
|
|
913
|
+
return exports;
|
|
305
914
|
}
|
|
306
915
|
|
|
307
|
-
|
|
916
|
+
NAPI_MODULE(i2c, Init)
|