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.
@@ -3,187 +3,187 @@ util = require('util')
3
3
  webusb = require('../').webusb
4
4
 
5
5
  if typeof gc is 'function'
6
- # running with --expose-gc, do a sweep between tests so valgrind blames the right one
7
- afterEach -> gc()
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
- it 'should describe basic constants', ->
11
- assert.notEqual(webusb, undefined, "webusb must be undefined")
10
+ it 'should describe basic constants', ->
11
+ assert.notEqual(webusb, undefined, "webusb must be undefined")
12
12
 
13
13
  describe 'requestDevice', ->
14
- it 'should return a device', ->
15
- device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
16
- assert.notEqual(device, undefined)
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
- 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)
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
- device = null
28
- before ->
29
- device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
27
+ device = null
28
+ before ->
29
+ device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
30
30
 
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)
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
- it 'should have device version properties', ->
37
- assert.equal(device.deviceVersionMajor, 1)
38
- assert.equal(device.deviceVersionMinor, 1)
39
- assert.equal(device.deviceVersionSubminor, 0)
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
- it 'should have class properties', ->
42
- assert.equal(device.deviceClass, 0)
43
- assert.equal(device.deviceSubclass, 0)
41
+ it 'should have class properties', ->
42
+ assert.equal(device.deviceClass, 0)
43
+ assert.equal(device.deviceSubclass, 0)
44
44
 
45
- it 'should have protocol property', ->
46
- assert.equal(device.deviceProtocol, 0)
45
+ it 'should have protocol property', ->
46
+ assert.equal(device.deviceProtocol, 0)
47
47
 
48
- it 'should have vid/pid properties', ->
49
- assert.equal(device.vendorId, 0x59e3)
50
- assert.equal(device.productId, 0x0a23)
48
+ it 'should have vid/pid properties', ->
49
+ assert.equal(device.vendorId, 0x59e3)
50
+ assert.equal(device.productId, 0x0a23)
51
51
 
52
- it 'should have a single configuration', ->
53
- assert.equal(device.configurations.length, 1)
54
- assert.equal(device.configurations[0].configurationValue, 1)
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
- it 'should have a configuration property', ->
57
- assert.notEqual(device.configuration, undefined)
56
+ it 'should have a configuration property', ->
57
+ assert.notEqual(device.configuration, undefined)
58
58
 
59
- it 'should have a single interface', ->
60
- assert.equal(device.configuration.interfaces.length, 1)
61
- assert.equal(device.configuration.interfaces[0].interfaceNumber, 0)
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
- 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)
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
- device = null
69
- before ->
70
- device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
68
+ device = null
69
+ before ->
70
+ device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
71
71
 
72
- it 'gets serialNumber string', ->
73
- assert.equal(device.serialNumber, "TEST_DEVICE")
72
+ it 'gets serialNumber string', ->
73
+ assert.equal(device.serialNumber, "TEST_DEVICE")
74
74
 
75
- it 'gets manufacturerName string', ->
76
- assert.equal(device.manufacturerName, "Nonolith Labs")
75
+ it 'gets manufacturerName string', ->
76
+ assert.equal(device.manufacturerName, "Nonolith Labs")
77
77
 
78
- it 'gets productName string', ->
79
- assert.equal(device.productName, "STM32F103 Test Device")
78
+ it 'gets productName string', ->
79
+ assert.equal(device.productName, "STM32F103 Test Device")
80
80
 
81
81
  describe 'Device access', ->
82
- device = null
83
- before ->
84
- device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
82
+ device = null
83
+ before ->
84
+ device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
85
85
 
86
- it 'is not open', ->
87
- assert.equal(device.opened, false)
86
+ it 'is not open', ->
87
+ assert.equal(device.opened, false)
88
88
 
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)
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
- device = null
98
- before ->
99
- device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
100
- await device.open()
97
+ device = null
98
+ before ->
99
+ device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
100
+ await device.open()
101
101
 
102
- it 'selects existing configuration', ->
103
- assert.doesNotReject(device.selectConfiguration(1))
102
+ it 'selects existing configuration', ->
103
+ assert.doesNotReject(device.selectConfiguration(1))
104
104
 
105
- it 'fails to select missing configuration', ->
106
- assert.rejects(device.selectConfiguration(99))
105
+ it 'fails to select missing configuration', ->
106
+ assert.rejects(device.selectConfiguration(99))
107
107
 
108
- after ->
109
- device.close()
108
+ after ->
109
+ device.close()
110
110
 
111
111
  describe 'Interfaces', ->
112
- device = null
113
- before ->
114
- device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
115
- await device.open()
112
+ device = null
113
+ before ->
114
+ device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
115
+ await device.open()
116
116
 
117
- it 'claims existing interface', ->
118
- assert.doesNotReject(device.claimInterface(0))
117
+ it 'claims existing interface', ->
118
+ assert.doesNotReject(device.claimInterface(0))
119
119
 
120
- it 'fails to claim missing interface', ->
121
- assert.rejects(device.claimInterface(99))
120
+ it 'fails to claim missing interface', ->
121
+ assert.rejects(device.claimInterface(99))
122
122
 
123
- it 'releases existing interface', ->
124
- assert.doesNotReject(device.releaseInterface(0))
123
+ it 'releases existing interface', ->
124
+ assert.doesNotReject(device.releaseInterface(0))
125
125
 
126
- it 'fails to release missing interface', ->
127
- assert.rejects(device.releaseInterface(99))
126
+ it 'fails to release missing interface', ->
127
+ assert.rejects(device.releaseInterface(99))
128
128
 
129
- after ->
130
- device.close()
129
+ after ->
130
+ device.close()
131
131
 
132
132
  describe 'Alternates', ->
133
- device = null
134
- before ->
135
- device = await webusb.requestDevice({ filters: [{ vendorId: 0x59e3 }] });
136
- await device.open()
137
- await device.claimInterface(0)
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
- it 'selects existing alternate', ->
140
- assert.doesNotReject(device.selectAlternateInterface(0, 0))
139
+ it 'selects existing alternate', ->
140
+ assert.doesNotReject(device.selectAlternateInterface(0, 0))
141
141
 
142
- after ->
143
- device.close()
142
+ after ->
143
+ device.close()
144
144
 
145
145
  describe 'Transfers', ->
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()
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()
@@ -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;
@@ -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[] | undefined;
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.pollTransfers) {
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.pollTransfers) {
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
- // Polling mechanism for discovering device changes until this is fixed:
44
- // https://github.com/libusb/libusb/issues/86
45
- const pollTimeout = 500;
46
- const hotplugSupported = usb._getLibusbCapability(usb.LIBUSB_CAP_HAS_HOTPLUG) > 0;
47
- let pollingHotplug = false;
48
- let pollDevices = new Set<usb.Device>();
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
- // Collect current devices
58
- const devices = new Set(usb.getDeviceList());
88
+ setTimeout(() => pollHotplug(), 500);
89
+ };
59
90
 
60
- if (!start) {
61
- // Find attached devices
62
- for (const device of devices) {
63
- if (!pollDevices.has(device))
64
- usb.emit('attach', device);
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
- // Find detached devices
68
- for (const device of pollDevices) {
69
- if (!devices.has(device))
70
- usb.emit('detach', device);
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
- pollDevices = devices;
75
- setTimeout(() => {
76
- pollHotplug();
77
- }, pollTimeout);
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
- if (hotplugSupported) {
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
- if (hotplugSupported) {
101
- usb._disableHotplugEvents();
102
- } else {
103
- pollingHotplug = false;
104
- }
145
+ stopHotplug();
105
146
  }
106
147
  });
107
148