usb 2.3.0 → 2.4.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/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
+ });
@@ -1,7 +1,6 @@
1
1
  import * as usb from '../usb';
2
2
  import { promisify } from 'util';
3
3
  import { Endpoint, InEndpoint, OutEndpoint } from '../usb/endpoint';
4
- import { Mutex } from './mutex';
5
4
 
6
5
  const LIBUSB_TRANSFER_TYPE_MASK = 0x03;
7
6
  const ENDPOINT_NUMBER_MASK = 0x7f;
@@ -39,8 +38,6 @@ export class WebUSBDevice implements USBDevice {
39
38
  public serialNumber?: string | undefined;
40
39
  public configurations: USBConfiguration[] = [];
41
40
 
42
- private deviceMutex = new Mutex();
43
-
44
41
  private constructor(private device: usb.Device) {
45
42
  const usbVersion = this.decodeVersion(device.deviceDescriptor.bcdUSB);
46
43
  this.usbVersionMajor = usbVersion.major;
@@ -73,8 +70,6 @@ export class WebUSBDevice implements USBDevice {
73
70
 
74
71
  public async open(): Promise<void> {
75
72
  try {
76
- await this.deviceMutex.lock();
77
-
78
73
  if (this.opened) {
79
74
  return;
80
75
  }
@@ -82,15 +77,11 @@ export class WebUSBDevice implements USBDevice {
82
77
  this.device.open();
83
78
  } catch (error) {
84
79
  throw new Error(`open error: ${error}`);
85
- } finally {
86
- this.deviceMutex.unlock();
87
80
  }
88
81
  }
89
82
 
90
83
  public async close(): Promise<void> {
91
84
  try {
92
- await this.deviceMutex.lock();
93
-
94
85
  if (!this.opened) {
95
86
  return;
96
87
  }
@@ -115,137 +106,110 @@ export class WebUSBDevice implements USBDevice {
115
106
  this.device.close();
116
107
  } catch (error) {
117
108
  throw new Error(`close error: ${error}`);
118
- } finally {
119
- this.deviceMutex.unlock();
120
109
  }
121
110
  }
122
111
 
123
112
  public async selectConfiguration(configurationValue: number): Promise<void> {
124
- try {
125
- await this.deviceMutex.lock();
126
-
127
- if (!this.opened || !this.device.configDescriptor) {
128
- throw new Error('selectConfiguration error: invalid state');
129
- }
113
+ if (!this.opened || !this.device.configDescriptor) {
114
+ throw new Error('selectConfiguration error: invalid state');
115
+ }
130
116
 
131
- if (this.device.configDescriptor.bConfigurationValue === configurationValue) {
132
- return;
133
- }
117
+ if (this.device.configDescriptor.bConfigurationValue === configurationValue) {
118
+ return;
119
+ }
134
120
 
135
- const config = this.configurations.find(configuration => configuration.configurationValue === configurationValue);
136
- if (!config) {
137
- throw new Error('selectConfiguration error: configuration not found');
138
- }
121
+ const config = this.configurations.find(configuration => configuration.configurationValue === configurationValue);
122
+ if (!config) {
123
+ throw new Error('selectConfiguration error: configuration not found');
124
+ }
139
125
 
140
- try {
141
- const setConfiguration = promisify(this.device.setConfiguration).bind(this.device);
142
- await setConfiguration(configurationValue);
143
- } catch (error) {
144
- throw new Error(`selectConfiguration error: ${error}`);
145
- }
146
- } finally {
147
- this.deviceMutex.unlock();
126
+ try {
127
+ const setConfiguration = promisify(this.device.setConfiguration).bind(this.device);
128
+ await setConfiguration(configurationValue);
129
+ } catch (error) {
130
+ throw new Error(`selectConfiguration error: ${error}`);
148
131
  }
149
132
  }
150
133
 
151
134
  public async claimInterface(interfaceNumber: number): Promise<void> {
152
- try {
153
- await this.deviceMutex.lock();
135
+ if (!this.opened) {
136
+ throw new Error('claimInterface error: invalid state');
137
+ }
154
138
 
155
- if (!this.opened) {
156
- throw new Error('claimInterface error: invalid state');
157
- }
139
+ if (!this.configuration) {
140
+ throw new Error('claimInterface error: interface not found');
141
+ }
158
142
 
159
- if (!this.configuration) {
160
- throw new Error('claimInterface error: interface not found');
161
- }
143
+ const iface = this.configuration.interfaces.find(usbInterface => usbInterface.interfaceNumber === interfaceNumber);
144
+ if (!iface) {
145
+ throw new Error('claimInterface error: interface not found');
146
+ }
162
147
 
163
- const iface = this.configuration.interfaces.find(usbInterface => usbInterface.interfaceNumber === interfaceNumber);
164
- if (!iface) {
165
- throw new Error('claimInterface error: interface not found');
166
- }
148
+ if (iface.claimed) {
149
+ return;
150
+ }
167
151
 
168
- if (iface.claimed) {
169
- return;
170
- }
152
+ try {
153
+ this.device.interface(interfaceNumber).claim();
154
+
155
+ // Re-create the USBInterface to set the claimed attribute
156
+ this.configuration.interfaces[this.configuration.interfaces.indexOf(iface)] = {
157
+ interfaceNumber,
158
+ alternate: iface.alternate,
159
+ alternates: iface.alternates,
160
+ claimed: true
161
+ };
162
+ } catch (error) {
163
+ throw new Error(`claimInterface error: ${error}`);
164
+ }
165
+ }
171
166
 
172
- try {
173
- this.device.interface(interfaceNumber).claim();
167
+ public async releaseInterface(interfaceNumber: number): Promise<void> {
168
+ await this._releaseInterface(interfaceNumber);
174
169
 
170
+ if (this.configuration) {
171
+ const iface = this.configuration.interfaces.find(usbInterface => usbInterface.interfaceNumber === interfaceNumber);
172
+ if (iface) {
175
173
  // Re-create the USBInterface to set the claimed attribute
176
174
  this.configuration.interfaces[this.configuration.interfaces.indexOf(iface)] = {
177
175
  interfaceNumber,
178
176
  alternate: iface.alternate,
179
177
  alternates: iface.alternates,
180
- claimed: true
178
+ claimed: false
181
179
  };
182
- } catch (error) {
183
- throw new Error(`claimInterface error: ${error}`);
184
- }
185
- } finally {
186
- this.deviceMutex.unlock();
187
- }
188
- }
189
-
190
- public async releaseInterface(interfaceNumber: number): Promise<void> {
191
- try {
192
- await this.deviceMutex.lock();
193
- await this._releaseInterface(interfaceNumber);
194
-
195
- if (this.configuration) {
196
- const iface = this.configuration.interfaces.find(usbInterface => usbInterface.interfaceNumber === interfaceNumber);
197
- if (iface) {
198
- // Re-create the USBInterface to set the claimed attribute
199
- this.configuration.interfaces[this.configuration.interfaces.indexOf(iface)] = {
200
- interfaceNumber,
201
- alternate: iface.alternate,
202
- alternates: iface.alternates,
203
- claimed: false
204
- };
205
- }
206
180
  }
207
-
208
- } finally {
209
- this.deviceMutex.unlock();
210
181
  }
211
182
  }
212
183
 
213
184
  public async selectAlternateInterface(interfaceNumber: number, alternateSetting: number): Promise<void> {
214
- try {
215
- await this.deviceMutex.lock();
216
-
217
- if (!this.opened) {
218
- throw new Error('selectAlternateInterface error: invalid state');
219
- }
185
+ if (!this.opened) {
186
+ throw new Error('selectAlternateInterface error: invalid state');
187
+ }
220
188
 
221
- if (!this.configuration) {
222
- throw new Error('selectAlternateInterface error: interface not found');
223
- }
189
+ if (!this.configuration) {
190
+ throw new Error('selectAlternateInterface error: interface not found');
191
+ }
224
192
 
225
- const iface = this.configuration.interfaces.find(usbInterface => usbInterface.interfaceNumber === interfaceNumber);
226
- if (!iface) {
227
- throw new Error('selectAlternateInterface error: interface not found');
228
- }
193
+ const iface = this.configuration.interfaces.find(usbInterface => usbInterface.interfaceNumber === interfaceNumber);
194
+ if (!iface) {
195
+ throw new Error('selectAlternateInterface error: interface not found');
196
+ }
229
197
 
230
- if (!iface.claimed) {
231
- throw new Error('selectAlternateInterface error: invalid state');
232
- }
198
+ if (!iface.claimed) {
199
+ throw new Error('selectAlternateInterface error: invalid state');
200
+ }
233
201
 
234
- try {
235
- const iface = this.device.interface(interfaceNumber);
236
- const setAltSetting = promisify(iface.setAltSetting).bind(iface);
237
- await setAltSetting(alternateSetting);
238
- } catch (error) {
239
- throw new Error(`selectAlternateInterface error: ${error}`);
240
- }
241
- } finally {
242
- this.deviceMutex.unlock();
202
+ try {
203
+ const iface = this.device.interface(interfaceNumber);
204
+ const setAltSetting = promisify(iface.setAltSetting).bind(iface);
205
+ await setAltSetting(alternateSetting);
206
+ } catch (error) {
207
+ throw new Error(`selectAlternateInterface error: ${error}`);
243
208
  }
244
209
  }
245
210
 
246
211
  public async controlTransferIn(setup: USBControlTransferParameters, length: number): Promise<USBInTransferResult> {
247
212
  try {
248
- await this.deviceMutex.lock();
249
213
  const type = this.controlTransferParamsToType(setup, usb.LIBUSB_ENDPOINT_IN);
250
214
  const controlTransfer = promisify(this.device.controlTransfer).bind(this.device);
251
215
  const result = await controlTransfer(type, setup.request, setup.value, setup.index, length);
@@ -268,14 +232,11 @@ export class WebUSBDevice implements USBDevice {
268
232
  }
269
233
 
270
234
  throw new Error(`controlTransferIn error: ${error}`);
271
- } finally {
272
- this.deviceMutex.unlock();
273
235
  }
274
236
  }
275
237
 
276
238
  public async controlTransferOut(setup: USBControlTransferParameters, data?: ArrayBuffer): Promise<USBOutTransferResult> {
277
239
  try {
278
- await this.deviceMutex.lock();
279
240
  const type = this.controlTransferParamsToType(setup, usb.LIBUSB_ENDPOINT_OUT);
280
241
  const controlTransfer = promisify(this.device.controlTransfer).bind(this.device);
281
242
  const buffer = data ? Buffer.from(data) : Buffer.alloc(0);
@@ -294,27 +255,21 @@ export class WebUSBDevice implements USBDevice {
294
255
  }
295
256
 
296
257
  throw new Error(`controlTransferOut error: ${error}`);
297
- } finally {
298
- this.deviceMutex.unlock();
299
258
  }
300
259
  }
301
260
 
302
261
  public async clearHalt(direction: USBDirection, endpointNumber: number): Promise<void> {
303
262
  try {
304
- await this.deviceMutex.lock();
305
263
  const wIndex = endpointNumber | (direction === 'in' ? usb.LIBUSB_ENDPOINT_IN : usb.LIBUSB_ENDPOINT_OUT);
306
264
  const controlTransfer = promisify(this.device.controlTransfer).bind(this.device);
307
265
  await controlTransfer(usb.LIBUSB_RECIPIENT_ENDPOINT, CLEAR_FEATURE, ENDPOINT_HALT, wIndex, 0);
308
266
  } catch (error) {
309
267
  throw new Error(`clearHalt error: ${error}`);
310
- } finally {
311
- this.deviceMutex.unlock();
312
268
  }
313
269
  }
314
270
 
315
271
  public async transferIn(endpointNumber: number, length: number): Promise<USBInTransferResult> {
316
272
  try {
317
- await this.deviceMutex.lock();
318
273
  const endpoint = this.getEndpoint(endpointNumber | usb.LIBUSB_ENDPOINT_IN) as InEndpoint;
319
274
  const transfer = promisify(endpoint.transfer).bind(endpoint);
320
275
  const result = await transfer(length);
@@ -337,14 +292,11 @@ export class WebUSBDevice implements USBDevice {
337
292
  }
338
293
 
339
294
  throw new Error(`transferIn error: ${error}`);
340
- } finally {
341
- this.deviceMutex.unlock();
342
295
  }
343
296
  }
344
297
 
345
298
  public async transferOut(endpointNumber: number, data: ArrayBuffer): Promise<USBOutTransferResult> {
346
299
  try {
347
- await this.deviceMutex.lock();
348
300
  const endpoint = this.getEndpoint(endpointNumber | usb.LIBUSB_ENDPOINT_OUT) as OutEndpoint;
349
301
  const transfer = promisify(endpoint.transfer).bind(endpoint);
350
302
  const buffer = Buffer.from(data);
@@ -363,20 +315,15 @@ export class WebUSBDevice implements USBDevice {
363
315
  }
364
316
 
365
317
  throw new Error(`transferOut error: ${error}`);
366
- } finally {
367
- this.deviceMutex.unlock();
368
318
  }
369
319
  }
370
320
 
371
321
  public async reset(): Promise<void> {
372
322
  try {
373
- await this.deviceMutex.lock();
374
323
  const reset = promisify(this.device.reset).bind(this.device);
375
324
  await reset();
376
325
  } catch (error) {
377
326
  throw new Error(`reset error: ${error}`);
378
- } finally {
379
- this.deviceMutex.unlock();
380
327
  }
381
328
  }
382
329
 
@@ -394,8 +341,6 @@ export class WebUSBDevice implements USBDevice {
394
341
 
395
342
  private async initialize(): Promise<void> {
396
343
  try {
397
- await this.deviceMutex.lock();
398
-
399
344
  if (!this.opened) {
400
345
  this.device.open();
401
346
  }
@@ -410,8 +355,6 @@ export class WebUSBDevice implements USBDevice {
410
355
  if (this.opened) {
411
356
  this.device.close();
412
357
  }
413
-
414
- this.deviceMutex.unlock();
415
358
  }
416
359
  }
417
360
 
@@ -1,22 +0,0 @@
1
- /**
2
- * A mutex implementation that can be used to lock access between concurrent async functions
3
- */
4
- export declare class Mutex {
5
- private locked;
6
- /**
7
- * Create a new Mutex
8
- */
9
- constructor();
10
- /**
11
- * Yield the current execution context, effectively moving it to the back of the promise queue
12
- */
13
- private sleep;
14
- /**
15
- * Wait until the Mutex is available and claim it
16
- */
17
- lock(): Promise<void>;
18
- /**
19
- * Unlock the Mutex
20
- */
21
- unlock(): void;
22
- }
@@ -1,89 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (_) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.Mutex = void 0;
40
- /**
41
- * A mutex implementation that can be used to lock access between concurrent async functions
42
- */
43
- var Mutex = /** @class */ (function () {
44
- /**
45
- * Create a new Mutex
46
- */
47
- function Mutex() {
48
- this.locked = false;
49
- }
50
- /**
51
- * Yield the current execution context, effectively moving it to the back of the promise queue
52
- */
53
- Mutex.prototype.sleep = function () {
54
- return __awaiter(this, void 0, void 0, function () {
55
- return __generator(this, function (_a) {
56
- return [2 /*return*/, new Promise(function (resolve) { return setTimeout(resolve, 1); })];
57
- });
58
- });
59
- };
60
- /**
61
- * Wait until the Mutex is available and claim it
62
- */
63
- Mutex.prototype.lock = function () {
64
- return __awaiter(this, void 0, void 0, function () {
65
- return __generator(this, function (_a) {
66
- switch (_a.label) {
67
- case 0:
68
- if (!this.locked) return [3 /*break*/, 2];
69
- return [4 /*yield*/, this.sleep()];
70
- case 1:
71
- _a.sent();
72
- return [3 /*break*/, 0];
73
- case 2:
74
- this.locked = true;
75
- return [2 /*return*/];
76
- }
77
- });
78
- });
79
- };
80
- /**
81
- * Unlock the Mutex
82
- */
83
- Mutex.prototype.unlock = function () {
84
- this.locked = false;
85
- };
86
- return Mutex;
87
- }());
88
- exports.Mutex = Mutex;
89
- //# sourceMappingURL=mutex.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mutex.js","sourceRoot":"","sources":["../../tsc/webusb/mutex.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;;GAEG;AACH;IAGI;;OAEG;IACH;QACI,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACxB,CAAC;IAED;;OAEG;IACW,qBAAK,GAAnB;;;gBACI,sBAAO,IAAI,OAAO,CAAC,UAAA,OAAO,IAAI,OAAA,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,EAAtB,CAAsB,CAAC,EAAC;;;KACzD;IAED;;OAEG;IACU,oBAAI,GAAjB;;;;;6BACW,IAAI,CAAC,MAAM;wBACd,qBAAM,IAAI,CAAC,KAAK,EAAE,EAAA;;wBAAlB,SAAkB,CAAC;;;wBAGvB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;;;;;KACtB;IAED;;OAEG;IACI,sBAAM,GAAb;QACI,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACxB,CAAC;IACL,YAAC;AAAD,CAAC,AAlCD,IAkCC;AAlCY,sBAAK"}
@@ -1,38 +0,0 @@
1
- /**
2
- * A mutex implementation that can be used to lock access between concurrent async functions
3
- */
4
- export class Mutex {
5
- private locked: boolean;
6
-
7
- /**
8
- * Create a new Mutex
9
- */
10
- constructor() {
11
- this.locked = false;
12
- }
13
-
14
- /**
15
- * Yield the current execution context, effectively moving it to the back of the promise queue
16
- */
17
- private async sleep(): Promise<void> {
18
- return new Promise(resolve => setTimeout(resolve, 1));
19
- }
20
-
21
- /**
22
- * Wait until the Mutex is available and claim it
23
- */
24
- public async lock(): Promise<void> {
25
- while (this.locked) {
26
- await this.sleep();
27
- }
28
-
29
- this.locked = true;
30
- }
31
-
32
- /**
33
- * Unlock the Mutex
34
- */
35
- public unlock(): void {
36
- this.locked = false;
37
- }
38
- }