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 +15 -0
- package/README.md +1 -1
- package/binding.gyp +3 -3
- package/package.json +6 -5
- package/prebuilds/android-arm/node.napi.armv7.node +0 -0
- package/prebuilds/android-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
- package/prebuilds/linux-arm/node.napi.armv6.node +0 -0
- package/prebuilds/linux-arm/node.napi.armv7.node +0 -0
- package/prebuilds/linux-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/linux-ia32/node.napi.node +0 -0
- package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/node.napi.musl.node +0 -0
- package/prebuilds/win32-ia32/node.napi.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/src/device.cc +20 -22
- package/src/node_usb.cc +93 -119
- package/src/node_usb.h +27 -15
- package/src/transfer.cc +0 -5
- package/test/usb.coffee +18 -7
- package/test/worker.cjs +9 -0
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.
|
|
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
|
|
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.
|
|
5
|
+
"version": "2.4.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"engines": {
|
|
8
|
-
"node": ">=10.
|
|
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 --
|
|
50
|
-
"prebuild-cross": "prebuildify-cross --napi --
|
|
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
|
-
|
|
76
|
+
6
|
|
76
77
|
]
|
|
77
78
|
}
|
|
78
79
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
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::
|
|
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(
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
414
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
21
|
+
DEBUG_LOG("HandleHotplug %p %i", dev, event);
|
|
27
22
|
|
|
28
|
-
|
|
23
|
+
Napi::Value v8dev = Device::get(env, dev);
|
|
24
|
+
libusb_unref_device(dev);
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
209
|
-
|
|
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,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
234
|
-
|
|
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
|
-
|
|
243
|
-
|
|
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(
|
|
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
|
-
#
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
30
|
-
|
|
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)
|
package/test/worker.cjs
ADDED
|
@@ -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
|
+
});
|