usb 2.4.3 → 2.5.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/.gitmodules +2 -2
- package/CHANGELOG.md +5 -0
- package/README.md +34 -0
- package/binding.gyp +48 -25
- package/dist/usb/bindings.d.ts +1 -0
- package/dist/usb/index.d.ts +2 -0
- package/dist/usb/index.js +25 -13
- package/dist/usb/index.js.map +1 -1
- package/package.json +1 -1
- 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 +309 -309
- package/src/helpers.h +36 -36
- package/src/hotplug/hotplug.h +22 -0
- package/src/hotplug/libusb.cc +91 -0
- package/src/hotplug/windows.cc +160 -0
- package/src/node_usb.cc +246 -282
- package/src/node_usb.h +64 -67
- package/src/thread_name.cc +1 -1
- package/src/transfer.cc +114 -114
- package/test/usb.coffee +178 -178
- package/test/webusb.coffee +140 -140
- package/tsc/usb/bindings.ts +1 -0
- package/tsc/usb/index.ts +30 -12
package/test/webusb.coffee
CHANGED
|
@@ -3,187 +3,187 @@ util = require('util')
|
|
|
3
3
|
webusb = require('../').webusb
|
|
4
4
|
|
|
5
5
|
if typeof gc is 'function'
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
# running with --expose-gc, do a sweep between tests so valgrind blames the right one
|
|
7
|
+
afterEach -> gc()
|
|
8
8
|
|
|
9
9
|
describe 'WebUSB Module', ->
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
it 'should describe basic constants', ->
|
|
11
|
+
assert.notEqual(webusb, undefined, "webusb must be undefined")
|
|
12
12
|
|
|
13
13
|
describe 'requestDevice', ->
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
it 'should return a device', ->
|
|
15
|
+
device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
|
|
16
|
+
assert.notEqual(device, undefined)
|
|
17
17
|
|
|
18
18
|
describe 'getDevices', ->
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
it 'should return one device', ->
|
|
20
|
+
device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
|
|
21
|
+
l = await webusb.getDevices()
|
|
22
|
+
assert.equal(l.length, 1)
|
|
23
|
+
assert.notEqual(l[0], undefined)
|
|
24
|
+
assert.deepEqual(l[0], device)
|
|
25
25
|
|
|
26
26
|
describe 'Device properties', ->
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
device = null
|
|
28
|
+
before ->
|
|
29
|
+
device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
it 'should have usb version properties', ->
|
|
32
|
+
assert.equal(device.usbVersionMajor, 1)
|
|
33
|
+
assert.equal(device.usbVersionMinor, 1)
|
|
34
|
+
assert.equal(device.usbVersionSubminor, 0)
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
it 'should have device version properties', ->
|
|
37
|
+
assert.equal(device.deviceVersionMajor, 1)
|
|
38
|
+
assert.equal(device.deviceVersionMinor, 1)
|
|
39
|
+
assert.equal(device.deviceVersionSubminor, 0)
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
it 'should have class properties', ->
|
|
42
|
+
assert.equal(device.deviceClass, 0)
|
|
43
|
+
assert.equal(device.deviceSubclass, 0)
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
it 'should have protocol property', ->
|
|
46
|
+
assert.equal(device.deviceProtocol, 0)
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
it 'should have vid/pid properties', ->
|
|
49
|
+
assert.equal(device.vendorId, 0x59e3)
|
|
50
|
+
assert.equal(device.productId, 0x0a23)
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
it 'should have a single configuration', ->
|
|
53
|
+
assert.equal(device.configurations.length, 1)
|
|
54
|
+
assert.equal(device.configurations[0].configurationValue, 1)
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
it 'should have a configuration property', ->
|
|
57
|
+
assert.notEqual(device.configuration, undefined)
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
it 'should have a single interface', ->
|
|
60
|
+
assert.equal(device.configuration.interfaces.length, 1)
|
|
61
|
+
assert.equal(device.configuration.interfaces[0].interfaceNumber, 0)
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
it 'should have a single alternate', ->
|
|
64
|
+
assert.equal(device.configuration.interfaces[0].alternates.length, 1)
|
|
65
|
+
assert.equal(device.configuration.interfaces[0].alternates[0].alternateSetting, 0)
|
|
66
66
|
|
|
67
67
|
describe 'String descriptors', ->
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
device = null
|
|
69
|
+
before ->
|
|
70
|
+
device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
it 'gets serialNumber string', ->
|
|
73
|
+
assert.equal(device.serialNumber, "TEST_DEVICE")
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
it 'gets manufacturerName string', ->
|
|
76
|
+
assert.equal(device.manufacturerName, "Nonolith Labs")
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
it 'gets productName string', ->
|
|
79
|
+
assert.equal(device.productName, "STM32F103 Test Device")
|
|
80
80
|
|
|
81
81
|
describe 'Device access', ->
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
device = null
|
|
83
|
+
before ->
|
|
84
|
+
device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
it 'is not open', ->
|
|
87
|
+
assert.equal(device.opened, false)
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
89
|
+
it 'is opens and closes', ->
|
|
90
|
+
assert.equal(device.opened, false)
|
|
91
|
+
await device.open()
|
|
92
|
+
assert.equal(device.opened, true)
|
|
93
|
+
await device.close()
|
|
94
|
+
assert.equal(device.opened, false)
|
|
95
95
|
|
|
96
96
|
describe 'Configurations', ->
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
device = null
|
|
98
|
+
before ->
|
|
99
|
+
device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
|
|
100
|
+
await device.open()
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
it 'selects existing configuration', ->
|
|
103
|
+
assert.doesNotReject(device.selectConfiguration(1))
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
it 'fails to select missing configuration', ->
|
|
106
|
+
assert.rejects(device.selectConfiguration(99))
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
after ->
|
|
109
|
+
device.close()
|
|
110
110
|
|
|
111
111
|
describe 'Interfaces', ->
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
device = null
|
|
113
|
+
before ->
|
|
114
|
+
device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
|
|
115
|
+
await device.open()
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
it 'claims existing interface', ->
|
|
118
|
+
assert.doesNotReject(device.claimInterface(0))
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
it 'fails to claim missing interface', ->
|
|
121
|
+
assert.rejects(device.claimInterface(99))
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
it 'releases existing interface', ->
|
|
124
|
+
assert.doesNotReject(device.releaseInterface(0))
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
it 'fails to release missing interface', ->
|
|
127
|
+
assert.rejects(device.releaseInterface(99))
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
after ->
|
|
130
|
+
device.close()
|
|
131
131
|
|
|
132
132
|
describe 'Alternates', ->
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
device = null
|
|
134
|
+
before ->
|
|
135
|
+
device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
|
|
136
|
+
await device.open()
|
|
137
|
+
await device.claimInterface(0)
|
|
138
138
|
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
it 'selects existing alternate', ->
|
|
140
|
+
assert.doesNotReject(device.selectAlternateInterface(0, 0))
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
after ->
|
|
143
|
+
device.close()
|
|
144
144
|
|
|
145
145
|
describe 'Transfers', ->
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
146
|
+
device = null
|
|
147
|
+
b = Uint8Array.from([0x30...0x40]).buffer
|
|
148
|
+
|
|
149
|
+
before ->
|
|
150
|
+
device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
|
|
151
|
+
await device.open()
|
|
152
|
+
await device.claimInterface(0)
|
|
153
|
+
|
|
154
|
+
it 'should control transfer OUT', ->
|
|
155
|
+
transferResult = await device.controlTransferOut({
|
|
156
|
+
requestType: 'device',
|
|
157
|
+
recipient: 'vendor';
|
|
158
|
+
request: 0x81,
|
|
159
|
+
value: 0,
|
|
160
|
+
index: 0
|
|
161
|
+
}, b)
|
|
162
|
+
|
|
163
|
+
assert.equal(transferResult.status, 'ok')
|
|
164
|
+
assert.equal(transferResult.bytesWritten, b.byteLength)
|
|
165
|
+
|
|
166
|
+
it 'should control transfer IN', ->
|
|
167
|
+
transferResult = await device.controlTransferIn({
|
|
168
|
+
requestType: 'device',
|
|
169
|
+
recipient: 'vendor';
|
|
170
|
+
request: 0x81,
|
|
171
|
+
value: 0,
|
|
172
|
+
index: 0
|
|
173
|
+
}, 128)
|
|
174
|
+
|
|
175
|
+
assert.equal(transferResult.status, 'ok')
|
|
176
|
+
assert.equal(transferResult.data.buffer.toString(), b.toString())
|
|
177
|
+
|
|
178
|
+
it 'should transfer OUT', ->
|
|
179
|
+
transferResult = await device.transferOut(2, b)
|
|
180
|
+
assert.equal(transferResult.status, 'ok')
|
|
181
|
+
assert.equal(transferResult.bytesWritten, b.byteLength)
|
|
182
|
+
|
|
183
|
+
it 'should transfer IN', ->
|
|
184
|
+
transferResult = await device.transferIn(1, 64)
|
|
185
|
+
assert.equal(transferResult.status, 'ok')
|
|
186
|
+
assert.equal(transferResult.data.byteLength, 64)
|
|
187
|
+
|
|
188
|
+
after ->
|
|
189
|
+
device.close()
|
package/tsc/usb/bindings.ts
CHANGED
|
@@ -31,6 +31,7 @@ export declare function setDebugLevel(level: number): void;
|
|
|
31
31
|
*/
|
|
32
32
|
export declare function useUsbDkBackend(): void;
|
|
33
33
|
|
|
34
|
+
export declare function _supportedHotplugEvents(): number;
|
|
34
35
|
export declare function _enableHotplugEvents(): void;
|
|
35
36
|
export declare function _disableHotplugEvents(): void;
|
|
36
37
|
export declare function _getLibusbCapability(capability: number): number;
|
package/tsc/usb/index.ts
CHANGED
|
@@ -26,6 +26,8 @@ declare module './bindings' {
|
|
|
26
26
|
interface DeviceEvents extends EventListeners<DeviceEvents> {
|
|
27
27
|
attach: Device;
|
|
28
28
|
detach: Device;
|
|
29
|
+
attachIds: undefined;
|
|
30
|
+
detachIds: undefined;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
function addListener<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
|
|
@@ -40,20 +42,13 @@ declare module './bindings' {
|
|
|
40
42
|
function listenerCount<K extends keyof DeviceEvents>(event: K): number;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
// Polling mechanism for discovering device changes
|
|
44
|
-
// https://github.com/libusb/libusb/issues/86
|
|
45
|
+
// Polling mechanism for discovering device changes where hotplug detection is not available
|
|
45
46
|
const pollTimeout = 500;
|
|
46
|
-
const hotplugSupported = usb.
|
|
47
|
+
const hotplugSupported = usb._supportedHotplugEvents();
|
|
47
48
|
let pollingHotplug = false;
|
|
48
49
|
let pollDevices = new Set<usb.Device>();
|
|
49
50
|
|
|
50
|
-
const
|
|
51
|
-
if (start) {
|
|
52
|
-
pollingHotplug = true;
|
|
53
|
-
} else if (!pollingHotplug) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
51
|
+
const pollHotplugOnce = (start: boolean) => {
|
|
57
52
|
// Collect current devices
|
|
58
53
|
const devices = new Set(usb.getDeviceList());
|
|
59
54
|
|
|
@@ -72,11 +67,34 @@ const pollHotplug = (start = false) => {
|
|
|
72
67
|
}
|
|
73
68
|
|
|
74
69
|
pollDevices = devices;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const pollHotplugLoop = (start = false) => {
|
|
73
|
+
if (start) {
|
|
74
|
+
pollingHotplug = true;
|
|
75
|
+
} else if (!pollingHotplug) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
pollHotplugOnce(start);
|
|
80
|
+
|
|
75
81
|
setTimeout(() => {
|
|
76
|
-
|
|
82
|
+
pollHotplugLoop();
|
|
77
83
|
}, pollTimeout);
|
|
78
84
|
};
|
|
79
85
|
|
|
86
|
+
const hotplugModeIsIdsOnly = hotplugSupported === 2;
|
|
87
|
+
if (hotplugModeIsIdsOnly) {
|
|
88
|
+
// The hotplug backend doesnt emit 'attach' or 'detach', so we need to do some conversion
|
|
89
|
+
const hotplugEventConversion = () => {
|
|
90
|
+
// Future: This might want a debounce, to avoid doing multiple polls when attaching a usb hub or something
|
|
91
|
+
pollHotplugOnce(false);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
usb.on('attachIds', hotplugEventConversion);
|
|
95
|
+
usb.on('detachIds', hotplugEventConversion);
|
|
96
|
+
}
|
|
97
|
+
|
|
80
98
|
usb.on('newListener', event => {
|
|
81
99
|
if (event !== 'attach' && event !== 'detach') {
|
|
82
100
|
return;
|
|
@@ -86,7 +104,7 @@ usb.on('newListener', event => {
|
|
|
86
104
|
if (hotplugSupported) {
|
|
87
105
|
usb._enableHotplugEvents();
|
|
88
106
|
} else {
|
|
89
|
-
|
|
107
|
+
pollHotplugLoop(true);
|
|
90
108
|
}
|
|
91
109
|
}
|
|
92
110
|
});
|