usb 2.4.2 → 2.5.1
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 +16 -0
- package/README.md +42 -2
- package/binding.gyp +48 -25
- package/dist/usb/bindings.d.ts +1 -0
- package/dist/usb/endpoint.d.ts +1 -1
- package/dist/usb/endpoint.js +4 -2
- package/dist/usb/endpoint.js.map +1 -1
- package/dist/usb/index.d.ts +6 -0
- package/dist/usb/index.js +82 -53
- package/dist/usb/index.js.map +1 -1
- package/package.json +4 -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 +309 -309
- package/src/helpers.h +36 -36
- package/src/hotplug/hotplug.h +22 -0
- package/src/hotplug/libusb.cc +90 -0
- package/src/hotplug/windows.cc +161 -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/endpoint.ts +4 -3
- package/tsc/usb/index.ts +73 -32
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/endpoint.ts
CHANGED
|
@@ -52,7 +52,7 @@ export class InEndpoint extends Endpoint {
|
|
|
52
52
|
/** Endpoint direction. */
|
|
53
53
|
public direction: 'in' | 'out' = 'in';
|
|
54
54
|
|
|
55
|
-
protected pollTransfers: Transfer[]
|
|
55
|
+
protected pollTransfers: Transfer[] = [];
|
|
56
56
|
protected pollTransferSize = 0;
|
|
57
57
|
protected pollPending = 0;
|
|
58
58
|
public pollActive = false;
|
|
@@ -113,6 +113,7 @@ export class InEndpoint extends Endpoint {
|
|
|
113
113
|
|
|
114
114
|
if (this.pollPending === 0) {
|
|
115
115
|
this.pollTransfers = [];
|
|
116
|
+
this.pollActive = false;
|
|
116
117
|
this.emit('end');
|
|
117
118
|
}
|
|
118
119
|
}
|
|
@@ -138,7 +139,7 @@ export class InEndpoint extends Endpoint {
|
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
protected startPollTransfers(nTransfers = 3, transferSize = this.descriptor.wMaxPacketSize, callback: (error: LibUSBException | undefined, buffer: Buffer, actualLength: number) => void): Transfer[] {
|
|
141
|
-
if (this.
|
|
142
|
+
if (this.pollActive) {
|
|
142
143
|
throw new Error('Polling already active');
|
|
143
144
|
}
|
|
144
145
|
|
|
@@ -163,7 +164,7 @@ export class InEndpoint extends Endpoint {
|
|
|
163
164
|
* @param callback
|
|
164
165
|
*/
|
|
165
166
|
public stopPoll(callback?: () => void): void {
|
|
166
|
-
if (!this.
|
|
167
|
+
if (!this.pollActive) {
|
|
167
168
|
throw new Error('Polling is not active.');
|
|
168
169
|
}
|
|
169
170
|
for (let i = 0; i < this.pollTransfers.length; i++) {
|
package/tsc/usb/index.ts
CHANGED
|
@@ -18,6 +18,11 @@ interface EventListeners<T> {
|
|
|
18
18
|
removeListener: keyof T;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
interface DeviceIds {
|
|
22
|
+
idVendor: number;
|
|
23
|
+
idProduct: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
declare module './bindings' {
|
|
22
27
|
|
|
23
28
|
/* eslint-disable @typescript-eslint/no-empty-interface */
|
|
@@ -26,6 +31,8 @@ declare module './bindings' {
|
|
|
26
31
|
interface DeviceEvents extends EventListeners<DeviceEvents> {
|
|
27
32
|
attach: Device;
|
|
28
33
|
detach: Device;
|
|
34
|
+
attachIds: DeviceIds;
|
|
35
|
+
detachIds: DeviceIds;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
function addListener<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void;
|
|
@@ -40,41 +47,83 @@ declare module './bindings' {
|
|
|
40
47
|
function listenerCount<K extends keyof DeviceEvents>(event: K): number;
|
|
41
48
|
}
|
|
42
49
|
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
let
|
|
50
|
+
// Hotplug support
|
|
51
|
+
const hotplugSupported = usb._supportedHotplugEvents();
|
|
52
|
+
|
|
53
|
+
// Devices delta support for non-libusb hotplug events
|
|
54
|
+
// This methd needs to be used for attach/detach IDs (hotplugSupportType === 2) rather than a lookup because vid/pid are not unique
|
|
55
|
+
let hotPlugDevices = new Set<usb.Device>();
|
|
56
|
+
const emitHotplugEvents = () => {
|
|
57
|
+
// Collect current devices
|
|
58
|
+
const devices = new Set(usb.getDeviceList());
|
|
59
|
+
|
|
60
|
+
// Find attached devices
|
|
61
|
+
for (const device of devices) {
|
|
62
|
+
if (!hotPlugDevices.has(device)) {
|
|
63
|
+
usb.emit('attach', device);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Find detached devices
|
|
68
|
+
for (const device of hotPlugDevices) {
|
|
69
|
+
if (!devices.has(device)) {
|
|
70
|
+
usb.emit('detach', device);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
hotPlugDevices = devices;
|
|
75
|
+
};
|
|
49
76
|
|
|
77
|
+
// Polling mechanism for checking device changes where hotplug detection is not available
|
|
78
|
+
let pollingHotplug = false;
|
|
50
79
|
const pollHotplug = (start = false) => {
|
|
51
80
|
if (start) {
|
|
52
81
|
pollingHotplug = true;
|
|
53
82
|
} else if (!pollingHotplug) {
|
|
54
83
|
return;
|
|
84
|
+
} else {
|
|
85
|
+
emitHotplugEvents();
|
|
55
86
|
}
|
|
56
87
|
|
|
57
|
-
|
|
58
|
-
|
|
88
|
+
setTimeout(() => pollHotplug(), 500);
|
|
89
|
+
};
|
|
59
90
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
91
|
+
// Hotplug control
|
|
92
|
+
const startHotplug = () => {
|
|
93
|
+
if (hotplugSupported !== 1) {
|
|
94
|
+
// Collect initial devices when not using libusb
|
|
95
|
+
hotPlugDevices = new Set(usb.getDeviceList());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (hotplugSupported) {
|
|
99
|
+
// Use hotplug event emitters
|
|
100
|
+
usb._enableHotplugEvents();
|
|
66
101
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
102
|
+
if (hotplugSupported === 2) {
|
|
103
|
+
// Use hotplug ID events to trigger a change check
|
|
104
|
+
usb.on('attachIds', emitHotplugEvents);
|
|
105
|
+
usb.on('detachIds', emitHotplugEvents);
|
|
71
106
|
}
|
|
107
|
+
} else {
|
|
108
|
+
// Fallback to using polling to check for changes
|
|
109
|
+
pollHotplug(true);
|
|
72
110
|
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const stopHotplug = () => {
|
|
114
|
+
if (hotplugSupported) {
|
|
115
|
+
// Disable hotplug events
|
|
116
|
+
usb._disableHotplugEvents();
|
|
73
117
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
118
|
+
if (hotplugSupported === 2) {
|
|
119
|
+
// Remove hotplug ID event listeners
|
|
120
|
+
usb.off('attachIds', emitHotplugEvents);
|
|
121
|
+
usb.off('detachIds', emitHotplugEvents);
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
// Stop polling
|
|
125
|
+
pollingHotplug = false;
|
|
126
|
+
}
|
|
78
127
|
};
|
|
79
128
|
|
|
80
129
|
usb.on('newListener', event => {
|
|
@@ -83,11 +132,7 @@ usb.on('newListener', event => {
|
|
|
83
132
|
}
|
|
84
133
|
const listenerCount = usb.listenerCount('attach') + usb.listenerCount('detach');
|
|
85
134
|
if (listenerCount === 0) {
|
|
86
|
-
|
|
87
|
-
usb._enableHotplugEvents();
|
|
88
|
-
} else {
|
|
89
|
-
pollHotplug(true);
|
|
90
|
-
}
|
|
135
|
+
startHotplug();
|
|
91
136
|
}
|
|
92
137
|
});
|
|
93
138
|
|
|
@@ -97,11 +142,7 @@ usb.on('removeListener', event => {
|
|
|
97
142
|
}
|
|
98
143
|
const listenerCount = usb.listenerCount('attach') + usb.listenerCount('detach');
|
|
99
144
|
if (listenerCount === 0) {
|
|
100
|
-
|
|
101
|
-
usb._disableHotplugEvents();
|
|
102
|
-
} else {
|
|
103
|
-
pollingHotplug = false;
|
|
104
|
-
}
|
|
145
|
+
stopHotplug();
|
|
105
146
|
}
|
|
106
147
|
});
|
|
107
148
|
|