usb 2.3.1 → 2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.4.0] - 2022-05-02
4
+
5
+ ### Fixed
6
+ - Made addon context aware - [`512`](https://github.com/node-usb/node-usb/pull/512) ([Rob Moran](https://github.com/thegecko) & [Alba Mendez](https://github.com/mildsunrise))
7
+ - Fixed deprecation of `libusb_set_option` - [`510`](https://github.com/node-usb/node-usb/pull/510) ([Rob Moran](https://github.com/thegecko))
8
+
9
+ ### Changed
10
+ - **Breaking:** Updated to N-API version 6 (Node >=10.20.0 <11.x, >=12.17.0 <13.0, >=14.0.0) - [`509`](https://github.com/node-usb/node-usb/pull/509) ([Rob Moran](https://github.com/thegecko))
11
+
12
+ ### Added
13
+ - Added `rebuild` command - [`511`](https://github.com/node-usb/node-usb/pull/511) ([Rob Moran](https://github.com/thegecko))
14
+
15
+ ### Removed
16
+ - Removed `USE_POLL` build definition to simplify code - [`507`](https://github.com/node-usb/node-usb/pull/507) ([Rob Moran](https://github.com/thegecko))
17
+
3
18
  ## [2.3.1] - 2022-04-11
4
19
 
5
20
  ### Changed
package/README.md CHANGED
@@ -10,7 +10,7 @@ This is a refactoring / rewrite of Christopher Klein's [node-usb](https://github
10
10
 
11
11
  # Prerequisites
12
12
 
13
- [Node.js >= v10.16.0](https://nodejs.org), which includes `npm`.
13
+ [Node.js >= v10.20.0](https://nodejs.org), which includes `npm`.
14
14
 
15
15
  ## Windows
16
16
 
package/binding.gyp CHANGED
@@ -26,13 +26,12 @@
26
26
  'defines': [
27
27
  '_FILE_OFFSET_BITS=64',
28
28
  '_LARGEFILE_SOURCE',
29
- 'NAPI_VERSION=<(napi_build_version)',
29
+ 'NAPI_VERSION=6',
30
30
  ],
31
31
  'include_dirs+': [
32
32
  'src/',
33
33
  "<!@(node -p \"require('node-addon-api').include\")"
34
34
  ],
35
-
36
35
  'conditions' : [
37
36
  ['use_system_libusb=="false" and OS!="freebsd"', {
38
37
  'dependencies': [
@@ -93,7 +92,8 @@
93
92
  'AdditionalOptions': [ '/EHsc' ],
94
93
  },
95
94
  },
96
- }]
95
+ }
96
+ ]
97
97
  ]
98
98
  },
99
99
  ]
package/package.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "name": "usb",
3
3
  "description": "Library to access USB devices",
4
4
  "license": "MIT",
5
- "version": "2.3.1",
5
+ "version": "2.4.0",
6
6
  "main": "dist/index.js",
7
7
  "engines": {
8
- "node": ">=10.16.0"
8
+ "node": ">=10.20.0 <11.x, >=12.17.0 <13.0, >=14.0.0"
9
9
  },
10
10
  "repository": {
11
11
  "type": "git",
@@ -38,6 +38,7 @@
38
38
  "scripts": {
39
39
  "prepare": "yarn compile",
40
40
  "install": "node-gyp-build",
41
+ "rebuild": "node-gyp rebuild",
41
42
  "clean": "git clean -dfx",
42
43
  "compile": "tsc && yarn lint && yarn docs",
43
44
  "lint": "eslint . --ext .ts",
@@ -46,8 +47,8 @@
46
47
  "full-test": "mocha --require coffeescript/register test/*.coffee",
47
48
  "valgrind": "coffee -c test/*.coffee; valgrind --leak-check=full --show-possibly-lost=no node --expose-gc --trace-gc node_modules/mocha/bin/_mocha -R spec",
48
49
  "docs": "typedoc",
49
- "prebuild": "prebuildify --napi --target 10.16.0 --strip",
50
- "prebuild-cross": "prebuildify-cross --napi --target 10.16.0 --strip",
50
+ "prebuild": "prebuildify --napi --strip",
51
+ "prebuild-cross": "prebuildify-cross --napi --strip",
51
52
  "prepublishOnly": "prebuildify-ci download",
52
53
  "prebuild-download": "prebuildify-ci download"
53
54
  },
@@ -72,7 +73,7 @@
72
73
  },
73
74
  "binary": {
74
75
  "napi_versions": [
75
- 4
76
+ 6
76
77
  ]
77
78
  }
78
79
  }
Binary file
Binary file
Binary file
package/src/device.cc CHANGED
@@ -9,22 +9,24 @@
9
9
 
10
10
  #define MAX_PORTS 7
11
11
 
12
- Napi::FunctionReference Device::constructor;
13
-
14
- Device::Device(const Napi::CallbackInfo & info) : Napi::ObjectWrap<Device>(info), device_handle(0), refs_(0)
15
- #ifndef USE_POLL
16
- , completionQueue(handleCompletion)
17
- #endif
18
- {
12
+ Device::Device(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Device>(info), env(0), device_handle(0), refs_(0), completionQueue(handleCompletion) {
13
+ env = info.Env();
19
14
  device = info[0].As<Napi::External<libusb_device>>().Data();
20
15
  libusb_ref_device(device);
16
+
17
+ std::map<libusb_device*, Device*> byPtr = env.GetInstanceData<ModuleData>()->byPtr;
21
18
  byPtr[device] = this;
19
+
22
20
  DEBUG_LOG("Created device %p", this);
23
21
  Constructor(info);
24
22
  }
25
23
 
26
- Device::~Device(){
24
+ Device::~Device() {
27
25
  DEBUG_LOG("Freed device %p", this);
26
+
27
+ ModuleData* instanceData = env.GetInstanceData<ModuleData>();
28
+ std::map<libusb_device*, Device*> byPtr = instanceData->byPtr;
29
+
28
30
  auto it = byPtr.find(device);
29
31
  if (it != byPtr.end() && it->second == this)
30
32
  byPtr.erase(it);
@@ -32,12 +34,12 @@ Device::~Device(){
32
34
  libusb_unref_device(device);
33
35
  }
34
36
 
35
- // Map pinning each libusb_device to a particular V8 instance
36
- std::map<libusb_device*, Device*> Device::byPtr;
37
-
38
37
  // Get a V8 instance for a libusb_device: either the existing one from the map,
39
38
  // or create a new one and add it to the map.
40
- Napi::Object Device::get(napi_env env, libusb_device* dev){
39
+ Napi::Object Device::get(Napi::Env env, libusb_device* dev) {
40
+ ModuleData* instanceData = env.GetInstanceData<ModuleData>();
41
+ std::map<libusb_device*, Device*> byPtr = instanceData->byPtr;
42
+
41
43
  auto it = byPtr.find(dev);
42
44
  if (it != byPtr.end()) {
43
45
  auto value = it->second->Value();
@@ -46,7 +48,7 @@ Napi::Object Device::get(napi_env env, libusb_device* dev){
46
48
  return value;
47
49
  }
48
50
 
49
- Napi::Object obj = Device::constructor.New({ Napi::External<libusb_device>::New(env, dev) });
51
+ Napi::Object obj = instanceData->deviceConstructor.New({ Napi::External<libusb_device>::New(env, dev) });
50
52
  return obj;
51
53
  }
52
54
 
@@ -90,7 +92,7 @@ Napi::Value Device::Constructor(const Napi::CallbackInfo& info) {
90
92
  return info.This();
91
93
  }
92
94
 
93
- Napi::Object Device::cdesc2V8(napi_env env, libusb_config_descriptor * cdesc){
95
+ Napi::Object Device::cdesc2V8(Napi::Env env, libusb_config_descriptor * cdesc) {
94
96
  Napi::Object v8cdesc = Napi::Object::New(env);
95
97
 
96
98
  STRUCT_TO_V8(v8cdesc, *cdesc, bLength)
@@ -166,7 +168,7 @@ Napi::Value Device::GetConfigDescriptor(const Napi::CallbackInfo& info) {
166
168
  return v8cdesc;
167
169
  }
168
170
 
169
- Napi::Value Device::GetAllConfigDescriptors(const Napi::CallbackInfo& info){
171
+ Napi::Value Device::GetAllConfigDescriptors(const Napi::CallbackInfo& info) {
170
172
  ENTER_METHOD(Device, 0);
171
173
  libusb_config_descriptor * cdesc;
172
174
  struct libusb_device_descriptor dd;
@@ -180,7 +182,7 @@ Napi::Value Device::GetAllConfigDescriptors(const Napi::CallbackInfo& info){
180
182
  return v8cdescriptors;
181
183
  }
182
184
 
183
- Napi::Value Device::GetParent(const Napi::CallbackInfo& info){
185
+ Napi::Value Device::GetParent(const Napi::CallbackInfo& info) {
184
186
  ENTER_METHOD(Device, 0);
185
187
  libusb_device* dev = libusb_get_parent(self->device);
186
188
  if(dev)
@@ -193,9 +195,7 @@ Napi::Value Device::Open(const Napi::CallbackInfo& info) {
193
195
  ENTER_METHOD(Device, 0);
194
196
  if (!self->device_handle){
195
197
  CHECK_USB(libusb_open(self->device, &self->device_handle));
196
- #ifndef USE_POLL
197
198
  completionQueue.start(info.Env());
198
- #endif
199
199
  }
200
200
  return env.Undefined();
201
201
  }
@@ -205,9 +205,7 @@ Napi::Value Device::Close(const Napi::CallbackInfo& info) {
205
205
  if (self->canClose()){
206
206
  libusb_close(self->device_handle);
207
207
  self->device_handle = NULL;
208
- #ifndef USE_POLL
209
208
  completionQueue.stop();
210
- #endif
211
209
  }else{
212
210
  THROW_ERROR("Can't close device with a pending request");
213
211
  }
@@ -410,8 +408,8 @@ Napi::Object Device::Init(Napi::Env env, Napi::Object exports) {
410
408
  });
411
409
  exports.Set("Device", func);
412
410
 
413
- Device::constructor = Napi::Persistent(func);
414
- Device::constructor.SuppressDestruct();
411
+ ModuleData* instanceData = env.GetInstanceData<ModuleData>();
412
+ instanceData->deviceConstructor = Napi::Persistent(func);
415
413
 
416
414
  return exports;
417
415
  }
package/src/node_usb.cc CHANGED
@@ -1,6 +1,4 @@
1
1
  #include "node_usb.h"
2
- #include "uv_async_queue.h"
3
- #include <thread>
4
2
 
5
3
  Napi::Value SetDebugLevel(const Napi::CallbackInfo& info);
6
4
  Napi::Value UseUsbDkBackend(const Napi::CallbackInfo& info);
@@ -12,87 +10,86 @@ Napi::Value RefHotplugEvents(const Napi::CallbackInfo& info);
12
10
  Napi::Value UnrefHotplugEvents(const Napi::CallbackInfo& info);
13
11
  void initConstants(Napi::Object target);
14
12
 
15
- libusb_context* usb_context;
16
- struct HotPlug {
17
- libusb_device* device;
18
- libusb_hotplug_event event;
19
- };
13
+ void handleHotplug(HotPlug* info){
14
+ Napi::ObjectReference* hotplugThis = info->hotplugThis;
15
+ Napi::Env env = hotplugThis->Env();
16
+ Napi::HandleScope scope(env);
20
17
 
21
- #ifdef USE_POLL
22
- #include <poll.h>
23
- #include <uv.h>
24
- #include <sys/time.h>
18
+ libusb_device* dev = info->device;
19
+ libusb_hotplug_event event = info->event;
25
20
 
26
- std::map<int, uv_poll_t*> pollByFD;
21
+ DEBUG_LOG("HandleHotplug %p %i", dev, event);
27
22
 
28
- struct timeval zero_tv = {0, 0};
23
+ Napi::Value v8dev = Device::get(env, dev);
24
+ libusb_unref_device(dev);
29
25
 
30
- void onPollSuccess(uv_poll_t* handle, int status, int events){
31
- libusb_handle_events_timeout(usb_context, &zero_tv);
32
- }
26
+ Napi::String eventName;
27
+ if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
28
+ DEBUG_LOG("Device arrived");
29
+ eventName = Napi::String::New(env, "attach");
33
30
 
34
- void LIBUSB_CALL onPollFDAdded(int fd, short events, void *user_data){
35
- uv_poll_t *poll_fd;
36
- auto it = pollByFD.find(fd);
37
- if (it != pollByFD.end()){
38
- poll_fd = it->second;
39
- }else{
40
- poll_fd = (uv_poll_t*) malloc(sizeof(uv_poll_t));
41
- uv_poll_init(uv_default_loop(), poll_fd, fd);
42
- pollByFD.insert(std::make_pair(fd, poll_fd));
31
+ } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) {
32
+ DEBUG_LOG("Device left");
33
+ eventName = Napi::String::New(env, "detach");
34
+
35
+ } else {
36
+ DEBUG_LOG("Unhandled hotplug event %d\n", event);
37
+ return;
43
38
  }
44
39
 
45
- DEBUG_LOG("Added pollfd %i, %p", fd, poll_fd);
46
- unsigned flags = ((events&POLLIN) ? UV_READABLE:0)
47
- | ((events&POLLOUT)? UV_WRITABLE:0);
48
- uv_poll_start(poll_fd, flags, onPollSuccess);
40
+ hotplugThis->Get("emit").As<Napi::Function>().MakeCallback(hotplugThis->Value(), { eventName, v8dev });
41
+ delete info;
49
42
  }
50
43
 
51
- void LIBUSB_CALL onPollFDRemoved(int fd, void *user_data){
52
- auto it = pollByFD.find(fd);
53
- if (it != pollByFD.end()){
54
- DEBUG_LOG("Removed pollfd %i, %p", fd, it->second);
55
- uv_poll_stop(it->second);
56
- uv_close((uv_handle_t*) it->second, (uv_close_cb) free);
57
- pollByFD.erase(it);
44
+ void USBThreadFn(ModuleData* instanceData) {
45
+ libusb_context* usb_context = instanceData->usb_context;
46
+
47
+ while(true) {
48
+ if (instanceData->handlingEvents == false) {
49
+ break;
50
+ }
51
+ libusb_handle_events(usb_context);
58
52
  }
59
53
  }
60
54
 
61
- #else
62
- std::thread usb_thread;
55
+ ModuleData::ModuleData(libusb_context* usb_context) : usb_context(usb_context), hotplugQueue(handleHotplug) {
56
+ handlingEvents = true;
57
+ usb_thread = std::thread(USBThreadFn, this);
58
+ }
59
+
60
+ ModuleData::~ModuleData() {
61
+ handlingEvents = false;
62
+ libusb_interrupt_event_handler(usb_context);
63
+ usb_thread.join();
63
64
 
64
- void USBThreadFn(){
65
- while(1) libusb_handle_events(usb_context);
65
+ if (usb_context != nullptr) {
66
+ libusb_exit(usb_context);
67
+ usb_context = nullptr;
68
+ }
69
+ }
70
+
71
+ int LIBUSB_CALL hotplug_callback(libusb_context* ctx, libusb_device* device,
72
+ libusb_hotplug_event event, void* user_data) {
73
+ libusb_ref_device(device);
74
+ ModuleData* instanceData = (ModuleData*)user_data;
75
+ instanceData->hotplugQueue.post(new HotPlug {device, event, &instanceData->hotplugThis});
76
+ return 0;
66
77
  }
67
- #endif
68
78
 
69
79
  Napi::Object Init(Napi::Env env, Napi::Object exports) {
70
80
  Napi::HandleScope scope(env);
71
-
72
81
  initConstants(exports);
73
82
 
74
83
  // Initialize libusb. On error, halt initialization.
84
+ libusb_context* usb_context = nullptr;
75
85
  int res = libusb_init(&usb_context);
86
+
76
87
  exports.Set("INIT_ERROR", Napi::Number::New(env, res));
77
88
  if (res != 0) {
78
89
  return exports;
79
90
  }
80
91
 
81
- #ifdef USE_POLL
82
- assert(libusb_pollfds_handle_timeouts(usb_context));
83
- libusb_set_pollfd_notifiers(usb_context, onPollFDAdded, onPollFDRemoved, NULL);
84
-
85
- const struct libusb_pollfd** pollfds = libusb_get_pollfds(usb_context);
86
- assert(pollfds);
87
- for(const struct libusb_pollfd** i=pollfds; *i; i++){
88
- onPollFDAdded((*i)->fd, (*i)->events, NULL);
89
- }
90
- free(pollfds);
91
-
92
- #else
93
- usb_thread = std::thread(USBThreadFn);
94
- usb_thread.detach();
95
- #endif
92
+ env.SetInstanceData(new ModuleData(usb_context));
96
93
 
97
94
  Device::Init(env, exports);
98
95
  Transfer::Init(env, exports);
@@ -117,7 +114,8 @@ Napi::Value SetDebugLevel(const Napi::CallbackInfo& info) {
117
114
  THROW_BAD_ARGS("Usb::SetDebugLevel argument is invalid. [uint:[0-4]]!")
118
115
  }
119
116
 
120
- libusb_set_debug(usb_context, info[0].As<Napi::Number>().Int32Value());
117
+ libusb_context* usb_context = env.GetInstanceData<ModuleData>()->usb_context;
118
+ libusb_set_option(usb_context, LIBUSB_OPTION_LOG_LEVEL, info[0].As<Napi::Number>().Int32Value());
121
119
  return env.Undefined();
122
120
  }
123
121
 
@@ -125,6 +123,7 @@ Napi::Value UseUsbDkBackend(const Napi::CallbackInfo& info) {
125
123
  Napi::Env env = info.Env();
126
124
  Napi::HandleScope scope(env);
127
125
 
126
+ libusb_context* usb_context = env.GetInstanceData<ModuleData>()->usb_context;
128
127
  libusb_set_option(usb_context, LIBUSB_OPTION_USE_USBDK);
129
128
  return env.Undefined();
130
129
  }
@@ -132,7 +131,9 @@ Napi::Value UseUsbDkBackend(const Napi::CallbackInfo& info) {
132
131
  Napi::Value GetDeviceList(const Napi::CallbackInfo& info) {
133
132
  Napi::Env env = info.Env();
134
133
  Napi::HandleScope scope(env);
135
- libusb_device **devs;
134
+ libusb_device** devs;
135
+
136
+ libusb_context* usb_context = env.GetInstanceData<ModuleData>()->usb_context;
136
137
  int cnt = libusb_get_device_list(usb_context, &devs);
137
138
  CHECK_USB(cnt);
138
139
 
@@ -156,62 +157,28 @@ Napi::Value GetLibusbCapability(const Napi::CallbackInfo& info) {
156
157
  return Napi::Number::New(env, res);
157
158
  }
158
159
 
159
- Napi::ObjectReference hotplugThis;
160
-
161
- void handleHotplug(HotPlug* info){
162
- Napi::Env env = hotplugThis.Env();
163
- Napi::HandleScope scope(env);
164
-
165
- libusb_device* dev = info->device;
166
- libusb_hotplug_event event = info->event;
167
- delete info;
168
-
169
- DEBUG_LOG("HandleHotplug %p %i", dev, event);
170
-
171
- Napi::Value v8dev = Device::get(env, dev);
172
- libusb_unref_device(dev);
173
-
174
- Napi::String eventName;
175
- if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) {
176
- DEBUG_LOG("Device arrived");
177
- eventName = Napi::String::New(env, "attach");
178
-
179
- } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) {
180
- DEBUG_LOG("Device left");
181
- eventName = Napi::String::New(env, "detach");
182
-
183
- } else {
184
- DEBUG_LOG("Unhandled hotplug event %d\n", event);
185
- return;
186
- }
187
-
188
- hotplugThis.Get("emit").As<Napi::Function>().MakeCallback(hotplugThis.Value(), { eventName, v8dev });
189
- }
190
-
191
- bool hotplugEnabled = 0;
192
- libusb_hotplug_callback_handle hotplugHandle;
193
- UVQueue<HotPlug*> hotplugQueue(handleHotplug);
194
-
195
- int LIBUSB_CALL hotplug_callback(libusb_context *ctx, libusb_device *device,
196
- libusb_hotplug_event event, void *user_data) {
197
- libusb_ref_device(device);
198
- hotplugQueue.post(new HotPlug {device, event});
199
- return 0;
200
- }
201
-
202
160
  Napi::Value EnableHotplugEvents(const Napi::CallbackInfo& info) {
203
161
  Napi::Env env = info.Env();
204
162
  Napi::HandleScope scope(env);
163
+ ModuleData* instanceData = env.GetInstanceData<ModuleData>();
205
164
 
206
- if (!hotplugEnabled) {
207
- hotplugThis.Reset(info.This().As<Napi::Object>(), 1);
208
- hotplugThis.SuppressDestruct();
209
- CHECK_USB(libusb_hotplug_register_callback(usb_context,
165
+ if (!instanceData->hotplugEnabled) {
166
+ instanceData->hotplugThis.Reset(info.This().As<Napi::Object>(), 1);
167
+
168
+ libusb_context* usb_context = instanceData->usb_context;
169
+ CHECK_USB(libusb_hotplug_register_callback(
170
+ usb_context,
210
171
  (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
211
- (libusb_hotplug_flag)0, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
212
- hotplug_callback, NULL, &hotplugHandle));
213
- hotplugQueue.start(env);
214
- hotplugEnabled = true;
172
+ (libusb_hotplug_flag)0,
173
+ LIBUSB_HOTPLUG_MATCH_ANY,
174
+ LIBUSB_HOTPLUG_MATCH_ANY,
175
+ LIBUSB_HOTPLUG_MATCH_ANY,
176
+ hotplug_callback,
177
+ instanceData,
178
+ &instanceData->hotplugHandle
179
+ ));
180
+ instanceData->hotplugQueue.start(env);
181
+ instanceData->hotplugEnabled = true;
215
182
  }
216
183
  return env.Undefined();
217
184
  }
@@ -219,10 +186,13 @@ Napi::Value EnableHotplugEvents(const Napi::CallbackInfo& info) {
219
186
  Napi::Value DisableHotplugEvents(const Napi::CallbackInfo& info) {
220
187
  Napi::Env env = info.Env();
221
188
  Napi::HandleScope scope(env);
222
- if (hotplugEnabled) {
223
- libusb_hotplug_deregister_callback(usb_context, hotplugHandle);
224
- hotplugQueue.stop();
225
- hotplugEnabled = false;
189
+ ModuleData* instanceData = env.GetInstanceData<ModuleData>();
190
+
191
+ if (instanceData->hotplugEnabled) {
192
+ libusb_context* usb_context = instanceData->usb_context;
193
+ libusb_hotplug_deregister_callback(usb_context, instanceData->hotplugHandle);
194
+ instanceData->hotplugQueue.stop();
195
+ instanceData->hotplugEnabled = false;
226
196
  }
227
197
  return env.Undefined();
228
198
  }
@@ -230,8 +200,10 @@ Napi::Value DisableHotplugEvents(const Napi::CallbackInfo& info) {
230
200
  Napi::Value RefHotplugEvents(const Napi::CallbackInfo& info) {
231
201
  Napi::Env env = info.Env();
232
202
  Napi::HandleScope scope(env);
233
- if (hotplugEnabled) {
234
- hotplugQueue.ref(env);
203
+ ModuleData* instanceData = env.GetInstanceData<ModuleData>();
204
+
205
+ if (instanceData->hotplugEnabled) {
206
+ instanceData->hotplugQueue.ref(env);
235
207
  }
236
208
  return env.Undefined();
237
209
  }
@@ -239,8 +211,10 @@ Napi::Value RefHotplugEvents(const Napi::CallbackInfo& info) {
239
211
  Napi::Value UnrefHotplugEvents(const Napi::CallbackInfo& info) {
240
212
  Napi::Env env = info.Env();
241
213
  Napi::HandleScope scope(env);
242
- if (hotplugEnabled) {
243
- hotplugQueue.unref(env);
214
+ ModuleData* instanceData = env.GetInstanceData<ModuleData>();
215
+
216
+ if (instanceData->hotplugEnabled) {
217
+ instanceData->hotplugQueue.unref(env);
244
218
  }
245
219
  return env.Undefined();
246
220
  }
@@ -362,7 +336,7 @@ void initConstants(Napi::Object target){
362
336
  DEFINE_CONSTANT(target, LIBUSB_ERROR_OTHER);
363
337
  }
364
338
 
365
- Napi::Error libusbException(napi_env env, int errorno) {
339
+ Napi::Error libusbException(Napi::Env env, int errorno) {
366
340
  const char* err = libusb_error_name(errorno);
367
341
  Napi::Error e = Napi::Error::New(env, err);
368
342
  e.Set("errno", (double)errorno);
package/src/node_usb.h CHANGED
@@ -8,35 +8,36 @@
8
8
  #ifdef _WIN32
9
9
  #include <WinSock2.h>
10
10
  #endif
11
- #include <libusb.h>
12
11
 
13
- #define NAPI_VERSION 4
12
+ #include <thread>
13
+ #include <libusb.h>
14
14
  #include <napi.h>
15
15
  #include <node_buffer.h>
16
16
 
17
17
  #include "helpers.h"
18
-
19
- #ifndef USE_POLL
20
18
  #include "uv_async_queue.h"
21
- #endif
19
+
20
+ struct HotPlug {
21
+ libusb_device* device;
22
+ libusb_hotplug_event event;
23
+ Napi::ObjectReference* hotplugThis;
24
+ };
22
25
 
23
26
  struct Transfer;
24
27
 
25
- Napi::Error libusbException(napi_env env, int errorno);
28
+ Napi::Error libusbException(Napi::Env env, int errorno);
26
29
  void handleCompletion(Transfer* self);
27
30
 
28
31
  struct Device: public Napi::ObjectWrap<Device> {
32
+ Napi::Env env;
29
33
  libusb_device* device;
30
34
  libusb_device_handle* device_handle;
31
35
 
32
36
  int refs_;
33
-
34
- #ifndef USE_POLL
35
37
  UVQueue<Transfer*> completionQueue;
36
- #endif
37
38
 
38
39
  static Napi::Object Init(Napi::Env env, Napi::Object exports);
39
- static Napi::Object get(napi_env env, libusb_device* handle);
40
+ static Napi::Object get(Napi::Env env, libusb_device* handle);
40
41
 
41
42
  inline void ref(){ refs_ = Ref();}
42
43
  inline void unref(){ refs_ = Unref();}
@@ -45,8 +46,7 @@ struct Device: public Napi::ObjectWrap<Device> {
45
46
  Device(const Napi::CallbackInfo& info);
46
47
  ~Device();
47
48
 
48
- static Napi::Object cdesc2V8(napi_env env, libusb_config_descriptor * cdesc);
49
-
49
+ static Napi::Object cdesc2V8(Napi::Env env, libusb_config_descriptor * cdesc);
50
50
 
51
51
  Napi::Value GetConfigDescriptor(const Napi::CallbackInfo& info);
52
52
  Napi::Value GetAllConfigDescriptors(const Napi::CallbackInfo& info);
@@ -67,12 +67,24 @@ struct Device: public Napi::ObjectWrap<Device> {
67
67
 
68
68
  Napi::Value ClearHalt(const Napi::CallbackInfo& info);
69
69
  protected:
70
- static std::map<libusb_device*, Device*> byPtr;
71
- static Napi::FunctionReference constructor;
72
-
73
70
  Napi::Value Constructor(const Napi::CallbackInfo& info);
74
71
  };
75
72
 
73
+ struct ModuleData {
74
+ libusb_context* usb_context;
75
+ std::thread usb_thread;
76
+ std::atomic<bool> handlingEvents;
77
+
78
+ bool hotplugEnabled = 0;
79
+ libusb_hotplug_callback_handle hotplugHandle;
80
+ UVQueue<HotPlug*> hotplugQueue;
81
+ Napi::ObjectReference hotplugThis;
82
+ std::map<libusb_device*, Device*> byPtr;
83
+ Napi::FunctionReference deviceConstructor;
84
+
85
+ ModuleData(libusb_context* usb_context);
86
+ ~ModuleData();
87
+ };
76
88
 
77
89
  struct Transfer: public Napi::ObjectWrap<Transfer> {
78
90
  libusb_transfer* transfer;
package/src/transfer.cc CHANGED
@@ -84,12 +84,7 @@ extern "C" void LIBUSB_CALL usbCompletionCb(libusb_transfer *transfer){
84
84
  Transfer* t = static_cast<Transfer*>(transfer->user_data);
85
85
  DEBUG_LOG("Completion callback %p", t);
86
86
  assert(t != NULL);
87
-
88
- #ifdef USE_POLL
89
- handleCompletion(t);
90
- #else
91
87
  t->device->completionQueue.post(t);
92
- #endif
93
88
  }
94
89
 
95
90
  void handleCompletion(Transfer* self){
package/test/usb.coffee CHANGED
@@ -4,6 +4,7 @@ usb = require('../').usb
4
4
  getDeviceList = require('../').getDeviceList
5
5
  findByIds = require('../').findByIds
6
6
  findBySerialNumber = require('../').findBySerialNumber
7
+ Worker = require('worker_threads').Worker
7
8
 
8
9
  if typeof gc is 'function'
9
10
  # running with --expose-gc, do a sweep between tests so valgrind blames the right one
@@ -20,14 +21,14 @@ describe 'USB Module', ->
20
21
  assert.throws -> usb.Device()
21
22
  assert.throws -> usb.Device.prototype.open.call({})
22
23
 
23
- describe 'setDebugLevel', ->
24
- it 'should throw when passed invalid args', ->
25
- assert.throws((-> usb.setDebugLevel()), TypeError)
26
- assert.throws((-> usb.setDebugLevel(-1)), TypeError)
27
- assert.throws((-> usb.setDebugLevel(5)), TypeError)
24
+ describe 'setDebugLevel', ->
25
+ it 'should throw when passed invalid args', ->
26
+ assert.throws((-> usb.setDebugLevel()), TypeError)
27
+ assert.throws((-> usb.setDebugLevel(-1)), TypeError)
28
+ assert.throws((-> usb.setDebugLevel(5)), TypeError)
28
29
 
29
- it 'should succeed with good args', ->
30
- assert.doesNotThrow(-> usb.setDebugLevel(0))
30
+ it 'should succeed with good args', ->
31
+ assert.doesNotThrow(-> usb.setDebugLevel(0))
31
32
 
32
33
  describe 'getDeviceList', ->
33
34
  it 'should return at least one device', ->
@@ -201,3 +202,13 @@ describe 'Device', ->
201
202
 
202
203
  after ->
203
204
  device.close()
205
+
206
+ if process.platform != 'win32'
207
+ describe 'Context Aware', ->
208
+ it 'should handle opening the same device from different contexts', ->
209
+ for n in [1..5]
210
+ worker = new Worker('./test/worker.cjs')
211
+ worker.on 'message', (serial) ->
212
+ assert.equal(serial, 'TEST_DEVICE')
213
+ worker.on 'exit', (code) ->
214
+ assert.equal(code, 0)
@@ -0,0 +1,9 @@
1
+ const parentPort = require('worker_threads').parentPort
2
+ const findByIds = require('../dist').findByIds;
3
+
4
+ const device = findByIds(0x59e3, 0x0a23);
5
+ device.open();
6
+ device.getStringDescriptor(device.deviceDescriptor.iSerialNumber, (_, serial) => {
7
+ parentPort.postMessage(serial);
8
+ device.close();
9
+ });