@onekeyfe/hd-transport-usb 1.2.0-alpha.3 → 1.2.0-alpha.30
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/__tests__/protocol-v2-link.test.ts +398 -0
- package/dist/index.d.ts +87 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +202 -177
- package/dist/transportLog.d.ts +7 -0
- package/dist/transportLog.d.ts.map +1 -0
- package/jest.config.js +5 -0
- package/package.json +5 -4
- package/src/index.ts +254 -223
- package/src/transportLog.ts +11 -0
package/dist/index.js
CHANGED
|
@@ -59,6 +59,14 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
59
59
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
+
const HIGH_VOLUME_CALLS = new Set(['FileWrite', 'FilesystemFileWrite', 'EmmcFileWrite']);
|
|
63
|
+
function shouldSuppressHighVolumeCallLog(name) {
|
|
64
|
+
return HIGH_VOLUME_CALLS.has(name);
|
|
65
|
+
}
|
|
66
|
+
function createTransportCallLog(name, protocol) {
|
|
67
|
+
return { name, protocol };
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
const { parseConfigure, ProtocolV1, check } = transport__default["default"];
|
|
63
71
|
const PACKET_SIZE = transport.PROTOCOL_V1_USB_PACKET_SIZE;
|
|
64
72
|
const REPORT_ID = transport.PROTOCOL_V1_REPORT_ID;
|
|
@@ -71,7 +79,7 @@ const TRANSFER_TIMEOUT_MS = 30000;
|
|
|
71
79
|
const SERIAL_READ_TIMEOUT_MS = 5000;
|
|
72
80
|
const PACKET_IO_MAX_RETRIES = 3;
|
|
73
81
|
const PACKET_IO_RETRY_DELAY = 300;
|
|
74
|
-
const PROTOCOL_PROBE_TIMEOUT =
|
|
82
|
+
const PROTOCOL_PROBE_TIMEOUT = 5000;
|
|
75
83
|
function getBusId(dev) {
|
|
76
84
|
return `usb:${dev.busNumber}:${dev.deviceAddress}`;
|
|
77
85
|
}
|
|
@@ -132,46 +140,6 @@ function readSerialNumber(dev, openDevices) {
|
|
|
132
140
|
}
|
|
133
141
|
});
|
|
134
142
|
}
|
|
135
|
-
function transferInOnce(ep, length) {
|
|
136
|
-
return new Promise((resolve, reject) => {
|
|
137
|
-
ep.transfer(length, (err, data) => {
|
|
138
|
-
if (err)
|
|
139
|
-
return reject(err);
|
|
140
|
-
if (!data || data.length === 0)
|
|
141
|
-
return reject(new Error('Empty USB transfer'));
|
|
142
|
-
resolve(data);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
function transferOutOnce(ep, data) {
|
|
147
|
-
return new Promise((resolve, reject) => {
|
|
148
|
-
ep.transfer(data, (err) => {
|
|
149
|
-
if (err)
|
|
150
|
-
return reject(err);
|
|
151
|
-
resolve();
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
function resetUsbDevice(dev) {
|
|
156
|
-
return new Promise(resolve => {
|
|
157
|
-
const reset = dev.reset;
|
|
158
|
-
if (typeof reset !== 'function') {
|
|
159
|
-
resolve();
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
reset.call(dev, () => resolve());
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
function clearEndpointHalt(ep) {
|
|
166
|
-
return new Promise(resolve => {
|
|
167
|
-
const clearHalt = ep.clearHalt;
|
|
168
|
-
if (typeof clearHalt !== 'function') {
|
|
169
|
-
resolve();
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
clearHalt.call(ep, () => resolve());
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
143
|
function skipReportByte(packet) {
|
|
176
144
|
if (packet[0] === REPORT_ID) {
|
|
177
145
|
return packet.subarray(1);
|
|
@@ -181,8 +149,13 @@ function skipReportByte(packet) {
|
|
|
181
149
|
function toArrayBuffer(buf) {
|
|
182
150
|
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
183
151
|
}
|
|
184
|
-
class NodeUsbTransport {
|
|
152
|
+
class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
|
|
185
153
|
constructor() {
|
|
154
|
+
super({
|
|
155
|
+
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
156
|
+
maxFrameBytes: transport.PROTOCOL_V2_FRAME_MAX_BYTES,
|
|
157
|
+
logPrefix: 'ProtocolV2 NodeUSB',
|
|
158
|
+
});
|
|
186
159
|
this.name = 'NodeUsbTransport';
|
|
187
160
|
this.version = '';
|
|
188
161
|
this.configured = false;
|
|
@@ -190,10 +163,8 @@ class NodeUsbTransport {
|
|
|
190
163
|
this.serialToBusId = new Map();
|
|
191
164
|
this.openDevices = new Map();
|
|
192
165
|
this.deviceProtocol = new Map();
|
|
193
|
-
this.protocolV2Assemblers = new Map();
|
|
194
|
-
this.protocolV2Sessions = new Map();
|
|
195
|
-
this.protocolV2ReadTimeouts = new Map();
|
|
196
166
|
this.reconnectLocks = new Map();
|
|
167
|
+
this.activeTransfers = new Map();
|
|
197
168
|
this.cancelled = false;
|
|
198
169
|
}
|
|
199
170
|
init(logger, emitter) {
|
|
@@ -209,14 +180,38 @@ class NodeUsbTransport {
|
|
|
209
180
|
}
|
|
210
181
|
configureProtocolV2(signedData) {
|
|
211
182
|
var _a;
|
|
183
|
+
const schemaSource = typeof signedData === 'string'
|
|
184
|
+
? signedData
|
|
185
|
+
: (_a = JSON.stringify(signedData)) !== null && _a !== void 0 ? _a : String(signedData);
|
|
186
|
+
if (schemaSource === this.protocolV2SchemaSource)
|
|
187
|
+
return;
|
|
188
|
+
const hadProtocolV2Schema = this.protocolV2SchemaSource !== undefined;
|
|
212
189
|
this.messagesV2 = parseConfigure(signedData);
|
|
213
|
-
this.
|
|
214
|
-
|
|
215
|
-
|
|
190
|
+
this.protocolV2SchemaSource = schemaSource;
|
|
191
|
+
if (!hadProtocolV2Schema)
|
|
192
|
+
return;
|
|
193
|
+
this.invalidateAllProtocolV2UsbLinks('Protocol V2 schema reconfigured').catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] schema link cleanup failed:', error); });
|
|
216
194
|
}
|
|
217
195
|
listen() {
|
|
218
196
|
}
|
|
219
197
|
stop() {
|
|
198
|
+
var _a;
|
|
199
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
200
|
+
this.cancelled = true;
|
|
201
|
+
yield this.cancelActiveTransfers();
|
|
202
|
+
try {
|
|
203
|
+
yield this.disposeProtocolV2UsbLinks('Node USB transport stopped');
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] stop link cleanup failed:', error);
|
|
207
|
+
}
|
|
208
|
+
yield Promise.allSettled(this.reconnectLocks.values());
|
|
209
|
+
yield this.cancelActiveTransfers();
|
|
210
|
+
yield Promise.all(Array.from(this.openDevices.keys(), path => this.closeOpenDevice(path)));
|
|
211
|
+
this.reconnectLocks.clear();
|
|
212
|
+
this.deviceProtocol.clear();
|
|
213
|
+
this.serialToBusId.clear();
|
|
214
|
+
});
|
|
220
215
|
}
|
|
221
216
|
post(path, name, data) {
|
|
222
217
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -274,6 +269,7 @@ class NodeUsbTransport {
|
|
|
274
269
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, 'No device path provided');
|
|
275
270
|
}
|
|
276
271
|
try {
|
|
272
|
+
yield this.rotateProtocolV2UsbGeneration(path, 'Node USB transport acquired');
|
|
277
273
|
yield this.closeOpenDevice(path);
|
|
278
274
|
yield this.openDevice(path);
|
|
279
275
|
yield this.detectProtocol(path, input.expectedProtocol);
|
|
@@ -287,9 +283,81 @@ class NodeUsbTransport {
|
|
|
287
283
|
}
|
|
288
284
|
release(path, _onclose) {
|
|
289
285
|
return __awaiter(this, void 0, void 0, function* () {
|
|
286
|
+
yield this.cancelActiveTransfers(path);
|
|
287
|
+
yield this.invalidateProtocolV2UsbLink(path, 'Node USB transport released');
|
|
290
288
|
yield this.closeOpenDevice(path);
|
|
291
289
|
this.deviceProtocol.delete(path);
|
|
292
|
-
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
cancelActiveTransfers(path) {
|
|
293
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
294
|
+
const transfers = Array.from(this.activeTransfers.entries()).filter(([, active]) => path === undefined || active.path === path);
|
|
295
|
+
transfers.forEach(([transfer]) => {
|
|
296
|
+
try {
|
|
297
|
+
transfer.cancel();
|
|
298
|
+
}
|
|
299
|
+
catch (_a) {
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
yield Promise.allSettled(transfers.map(([, active]) => active.settled));
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
createTrackedTransfer(path, endpoint, callback) {
|
|
306
|
+
let resolveSettled = () => undefined;
|
|
307
|
+
const settled = new Promise(resolve => {
|
|
308
|
+
resolveSettled = resolve;
|
|
309
|
+
});
|
|
310
|
+
const transfer = endpoint.makeTransfer(endpoint.timeout, (error, buffer, actualLength) => {
|
|
311
|
+
this.activeTransfers.delete(transfer);
|
|
312
|
+
resolveSettled();
|
|
313
|
+
callback(error, buffer, actualLength);
|
|
314
|
+
});
|
|
315
|
+
this.activeTransfers.set(transfer, { path, settled, resolveSettled });
|
|
316
|
+
return transfer;
|
|
317
|
+
}
|
|
318
|
+
transferInOnce(path, ep, length) {
|
|
319
|
+
return new Promise((resolve, reject) => {
|
|
320
|
+
const buffer = Buffer.alloc(length);
|
|
321
|
+
const transfer = this.createTrackedTransfer(path, ep, (error, _submittedBuffer, actualLength) => {
|
|
322
|
+
if (error) {
|
|
323
|
+
reject(error);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (actualLength <= 0) {
|
|
327
|
+
reject(new Error('Empty USB transfer'));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
resolve(buffer.subarray(0, actualLength));
|
|
331
|
+
});
|
|
332
|
+
try {
|
|
333
|
+
transfer.submit(buffer);
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
const active = this.activeTransfers.get(transfer);
|
|
337
|
+
this.activeTransfers.delete(transfer);
|
|
338
|
+
active === null || active === void 0 ? void 0 : active.resolveSettled();
|
|
339
|
+
reject(error);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
transferOutOnce(path, ep, data) {
|
|
344
|
+
return new Promise((resolve, reject) => {
|
|
345
|
+
const transfer = this.createTrackedTransfer(path, ep, error => {
|
|
346
|
+
if (error) {
|
|
347
|
+
reject(error);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
resolve();
|
|
351
|
+
});
|
|
352
|
+
try {
|
|
353
|
+
transfer.submit(data);
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
const active = this.activeTransfers.get(transfer);
|
|
357
|
+
this.activeTransfers.delete(transfer);
|
|
358
|
+
active === null || active === void 0 ? void 0 : active.resolveSettled();
|
|
359
|
+
reject(error);
|
|
360
|
+
}
|
|
293
361
|
});
|
|
294
362
|
}
|
|
295
363
|
closeOpenDevice(path) {
|
|
@@ -320,7 +388,7 @@ class NodeUsbTransport {
|
|
|
320
388
|
});
|
|
321
389
|
}
|
|
322
390
|
call(path, name, data, options) {
|
|
323
|
-
var _a
|
|
391
|
+
var _a;
|
|
324
392
|
return __awaiter(this, void 0, void 0, function* () {
|
|
325
393
|
this.cancelled = false;
|
|
326
394
|
if (!this.messages) {
|
|
@@ -333,11 +401,8 @@ class NodeUsbTransport {
|
|
|
333
401
|
if (!protocol) {
|
|
334
402
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
|
|
335
403
|
}
|
|
336
|
-
if (
|
|
337
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('NodeUsbTransport call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
|
|
404
|
+
if (!shouldSuppressHighVolumeCallLog(name)) {
|
|
405
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('transport call', createTransportCallLog(name, protocol));
|
|
341
406
|
}
|
|
342
407
|
if (protocol === 'V2') {
|
|
343
408
|
return this.callProtocolV2(path, name, data, options);
|
|
@@ -362,8 +427,6 @@ class NodeUsbTransport {
|
|
|
362
427
|
});
|
|
363
428
|
}
|
|
364
429
|
cancel() {
|
|
365
|
-
var _a;
|
|
366
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('NodeUsbTransport cancel');
|
|
367
430
|
this.cancelled = true;
|
|
368
431
|
}
|
|
369
432
|
getOpenDevice(path) {
|
|
@@ -386,6 +449,9 @@ class NodeUsbTransport {
|
|
|
386
449
|
}
|
|
387
450
|
isRetryableError(error) {
|
|
388
451
|
const message = this.getErrorMessage(error).toLowerCase();
|
|
452
|
+
if (message.includes('cancelled') || message.includes('canceled')) {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
389
455
|
return (message.includes('libusb') ||
|
|
390
456
|
message.includes('transfer') ||
|
|
391
457
|
message.includes('disconnected') ||
|
|
@@ -397,6 +463,10 @@ class NodeUsbTransport {
|
|
|
397
463
|
message.includes('timeout') ||
|
|
398
464
|
message.includes('interrupt'));
|
|
399
465
|
}
|
|
466
|
+
isUsbTransferTimeout(error) {
|
|
467
|
+
const message = this.getErrorMessage(error).toLowerCase();
|
|
468
|
+
return message.includes('timeout') || message.includes('timed_out');
|
|
469
|
+
}
|
|
400
470
|
getDeviceInterface(dev) {
|
|
401
471
|
var _a;
|
|
402
472
|
const { interfaces } = dev;
|
|
@@ -416,6 +486,7 @@ class NodeUsbTransport {
|
|
|
416
486
|
var _a, _b;
|
|
417
487
|
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] transfer${direction} failed, retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(error)}`);
|
|
418
488
|
yield hdShared.wait(attempt * PACKET_IO_RETRY_DELAY);
|
|
489
|
+
yield this.rotateProtocolV2UsbGeneration(path, `Node USB Protocol V1 ${direction} reconnect attempt ${attempt}`);
|
|
419
490
|
try {
|
|
420
491
|
yield this.closeOpenDevice(path);
|
|
421
492
|
}
|
|
@@ -449,7 +520,7 @@ class NodeUsbTransport {
|
|
|
449
520
|
const packet = new Uint8Array(PACKET_SIZE);
|
|
450
521
|
packet[0] = REPORT_ID;
|
|
451
522
|
packet.set(new Uint8Array(buffer), 1);
|
|
452
|
-
yield transferOutOnce(this.getOpenDevice(path).epOut, Buffer.from(packet));
|
|
523
|
+
yield this.transferOutOnce(path, this.getOpenDevice(path).epOut, Buffer.from(packet));
|
|
453
524
|
}
|
|
454
525
|
return;
|
|
455
526
|
}
|
|
@@ -472,7 +543,7 @@ class NodeUsbTransport {
|
|
|
472
543
|
throw lastError;
|
|
473
544
|
});
|
|
474
545
|
}
|
|
475
|
-
transferInWithRetry(path, openDev, length) {
|
|
546
|
+
transferInWithRetry(path, openDev, length, options) {
|
|
476
547
|
var _a;
|
|
477
548
|
return __awaiter(this, void 0, void 0, function* () {
|
|
478
549
|
let lastError;
|
|
@@ -482,21 +553,26 @@ class NodeUsbTransport {
|
|
|
482
553
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
|
|
483
554
|
}
|
|
484
555
|
try {
|
|
485
|
-
return yield transferInOnce(currentDev.epIn, length);
|
|
556
|
+
return yield this.transferInOnce(path, currentDev.epIn, length);
|
|
486
557
|
}
|
|
487
558
|
catch (error) {
|
|
488
559
|
lastError = error;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
throw error;
|
|
492
|
-
}
|
|
493
|
-
try {
|
|
494
|
-
currentDev = yield this.reconnectForRetry(path, 'in', attempt, error);
|
|
560
|
+
if ((options === null || options === void 0 ? void 0 : options.waitIndefinitelyOnTimeout) && this.isUsbTransferTimeout(error)) {
|
|
561
|
+
attempt -= 1;
|
|
495
562
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
(
|
|
499
|
-
|
|
563
|
+
else {
|
|
564
|
+
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryableError(error);
|
|
565
|
+
if (!shouldRetry) {
|
|
566
|
+
throw error;
|
|
567
|
+
}
|
|
568
|
+
try {
|
|
569
|
+
currentDev = yield this.reconnectForRetry(path, 'in', attempt, error);
|
|
570
|
+
}
|
|
571
|
+
catch (reconnectError) {
|
|
572
|
+
lastError = reconnectError;
|
|
573
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] reconnect failed on retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(reconnectError)}`);
|
|
574
|
+
break;
|
|
575
|
+
}
|
|
500
576
|
}
|
|
501
577
|
}
|
|
502
578
|
}
|
|
@@ -518,13 +594,6 @@ class NodeUsbTransport {
|
|
|
518
594
|
try {
|
|
519
595
|
dev.open();
|
|
520
596
|
dev.timeout = TRANSFER_TIMEOUT_MS;
|
|
521
|
-
yield resetUsbDevice(dev);
|
|
522
|
-
try {
|
|
523
|
-
dev.open();
|
|
524
|
-
}
|
|
525
|
-
catch (_d) {
|
|
526
|
-
}
|
|
527
|
-
dev.timeout = TRANSFER_TIMEOUT_MS;
|
|
528
597
|
const iface = this.getDeviceInterface(dev);
|
|
529
598
|
if (process.platform === 'linux') {
|
|
530
599
|
try {
|
|
@@ -532,7 +601,7 @@ class NodeUsbTransport {
|
|
|
532
601
|
iface.detachKernelDriver();
|
|
533
602
|
}
|
|
534
603
|
}
|
|
535
|
-
catch (
|
|
604
|
+
catch (_d) {
|
|
536
605
|
}
|
|
537
606
|
}
|
|
538
607
|
iface.claim();
|
|
@@ -543,29 +612,27 @@ class NodeUsbTransport {
|
|
|
543
612
|
}
|
|
544
613
|
epIn.timeout = TRANSFER_TIMEOUT_MS;
|
|
545
614
|
epOut.timeout = TRANSFER_TIMEOUT_MS;
|
|
546
|
-
yield
|
|
547
|
-
yield clearEndpointHalt(epOut);
|
|
548
|
-
yield this.drainStaleInput(epIn);
|
|
615
|
+
yield this.drainStaleInput(path, epIn);
|
|
549
616
|
this.openDevices.set(path, { device: dev, iface, epIn, epOut });
|
|
550
617
|
}
|
|
551
618
|
catch (err) {
|
|
552
619
|
try {
|
|
553
620
|
dev.close();
|
|
554
621
|
}
|
|
555
|
-
catch (
|
|
622
|
+
catch (_e) {
|
|
556
623
|
}
|
|
557
624
|
throw err;
|
|
558
625
|
}
|
|
559
626
|
});
|
|
560
627
|
}
|
|
561
|
-
drainStaleInput(epIn) {
|
|
628
|
+
drainStaleInput(path, epIn) {
|
|
562
629
|
return __awaiter(this, void 0, void 0, function* () {
|
|
563
630
|
const originalTimeout = epIn.timeout;
|
|
564
631
|
epIn.timeout = 50;
|
|
565
632
|
try {
|
|
566
633
|
for (let index = 0; index < 16; index += 1) {
|
|
567
634
|
try {
|
|
568
|
-
yield transferInOnce(epIn, PACKET_SIZE);
|
|
635
|
+
yield this.transferInOnce(path, epIn, PACKET_SIZE);
|
|
569
636
|
}
|
|
570
637
|
catch (_a) {
|
|
571
638
|
break;
|
|
@@ -584,37 +651,33 @@ class NodeUsbTransport {
|
|
|
584
651
|
return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Unable to detect USB protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
|
|
585
652
|
}
|
|
586
653
|
detectProtocol(path, expectedProtocol) {
|
|
587
|
-
var _a
|
|
654
|
+
var _a;
|
|
588
655
|
return __awaiter(this, void 0, void 0, function* () {
|
|
589
|
-
if (expectedProtocol === 'V1') {
|
|
590
|
-
if (yield this.probeProtocolV1(path)) {
|
|
591
|
-
this.deviceProtocol.set(path, 'V1');
|
|
592
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
|
|
593
|
-
return 'V1';
|
|
594
|
-
}
|
|
595
|
-
throw this.createProtocolMismatchError(expectedProtocol);
|
|
596
|
-
}
|
|
597
656
|
if (expectedProtocol === 'V2') {
|
|
598
657
|
if (yield this.probeProtocolV2(path)) {
|
|
599
658
|
this.deviceProtocol.set(path, 'V2');
|
|
600
|
-
(
|
|
659
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
|
|
601
660
|
return 'V2';
|
|
602
661
|
}
|
|
603
662
|
throw this.createProtocolMismatchError(expectedProtocol);
|
|
604
663
|
}
|
|
664
|
+
if (expectedProtocol === 'V1') {
|
|
665
|
+
if (yield this.probeProtocolV1(path)) {
|
|
666
|
+
this.deviceProtocol.set(path, 'V1');
|
|
667
|
+
return 'V1';
|
|
668
|
+
}
|
|
669
|
+
throw this.createProtocolMismatchError(expectedProtocol);
|
|
670
|
+
}
|
|
605
671
|
if (this.deviceProtocol.get(path) === 'V2' && (yield this.probeProtocolV2(path))) {
|
|
606
672
|
this.deviceProtocol.set(path, 'V2');
|
|
607
|
-
(_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
|
|
608
673
|
return 'V2';
|
|
609
674
|
}
|
|
610
675
|
if (yield this.probeProtocolV1(path)) {
|
|
611
676
|
this.deviceProtocol.set(path, 'V1');
|
|
612
|
-
(_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1`);
|
|
613
677
|
return 'V1';
|
|
614
678
|
}
|
|
615
679
|
if (yield this.probeProtocolV2(path)) {
|
|
616
680
|
this.deviceProtocol.set(path, 'V2');
|
|
617
|
-
(_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2`);
|
|
618
681
|
return 'V2';
|
|
619
682
|
}
|
|
620
683
|
this.deviceProtocol.delete(path);
|
|
@@ -622,16 +685,15 @@ class NodeUsbTransport {
|
|
|
622
685
|
});
|
|
623
686
|
}
|
|
624
687
|
resetConnectionAfterProbe(path) {
|
|
625
|
-
var _a
|
|
688
|
+
var _a;
|
|
626
689
|
return __awaiter(this, void 0, void 0, function* () {
|
|
627
|
-
|
|
628
|
-
this.protocolV2Sessions.delete(path);
|
|
629
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
690
|
+
yield this.rotateProtocolV2UsbGeneration(path, 'Node USB protocol probe reset');
|
|
630
691
|
try {
|
|
692
|
+
yield this.cancelActiveTransfers(path);
|
|
631
693
|
yield this.closeOpenDevice(path);
|
|
632
694
|
}
|
|
633
695
|
catch (error) {
|
|
634
|
-
(
|
|
696
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] close after protocol probe error:', error);
|
|
635
697
|
}
|
|
636
698
|
yield this.enumerate();
|
|
637
699
|
yield this.openDevice(path);
|
|
@@ -675,7 +737,6 @@ class NodeUsbTransport {
|
|
|
675
737
|
});
|
|
676
738
|
}
|
|
677
739
|
probeProtocolV1(path) {
|
|
678
|
-
var _a;
|
|
679
740
|
return __awaiter(this, void 0, void 0, function* () {
|
|
680
741
|
if (!this.messages) {
|
|
681
742
|
return false;
|
|
@@ -684,8 +745,7 @@ class NodeUsbTransport {
|
|
|
684
745
|
yield this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
|
|
685
746
|
return true;
|
|
686
747
|
}
|
|
687
|
-
catch (
|
|
688
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] Protocol V1 Initialize probe failed:', error);
|
|
748
|
+
catch (_error) {
|
|
689
749
|
return false;
|
|
690
750
|
}
|
|
691
751
|
});
|
|
@@ -704,96 +764,61 @@ class NodeUsbTransport {
|
|
|
704
764
|
});
|
|
705
765
|
});
|
|
706
766
|
}
|
|
707
|
-
|
|
708
|
-
|
|
767
|
+
getProtocolV2UsbSchemas() {
|
|
768
|
+
if (!this.messages || !this.messagesV2) {
|
|
769
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
770
|
+
}
|
|
771
|
+
return {
|
|
772
|
+
protocolV1: this.messages,
|
|
773
|
+
protocolV2: this.messagesV2,
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
getProtocolV2UsbLogger() {
|
|
777
|
+
return this.Log;
|
|
778
|
+
}
|
|
779
|
+
writeProtocolV2UsbPacket(path, frame, _context) {
|
|
709
780
|
return __awaiter(this, void 0, void 0, function* () {
|
|
710
|
-
|
|
711
|
-
|
|
781
|
+
if (this.cancelled) {
|
|
782
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
|
|
783
|
+
}
|
|
784
|
+
yield this.transferOutOnce(path, this.getOpenDevice(path).epOut, Buffer.from(frame));
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
readProtocolV2UsbPacket(path, _context) {
|
|
788
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
789
|
+
for (;;) {
|
|
712
790
|
if (this.cancelled) {
|
|
713
791
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
|
|
714
792
|
}
|
|
715
793
|
try {
|
|
716
|
-
yield
|
|
717
|
-
return;
|
|
794
|
+
const packet = yield this.transferInOnce(path, this.getOpenDevice(path).epIn, transport.PROTOCOL_V2_FRAME_MAX_BYTES);
|
|
795
|
+
return new Uint8Array(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
|
|
718
796
|
}
|
|
719
797
|
catch (error) {
|
|
720
|
-
|
|
721
|
-
const shouldRetry = attempt < PACKET_IO_MAX_RETRIES && this.isRetryableError(error);
|
|
722
|
-
if (!shouldRetry) {
|
|
798
|
+
if (!this.isUsbTransferTimeout(error)) {
|
|
723
799
|
throw error;
|
|
724
800
|
}
|
|
725
|
-
try {
|
|
726
|
-
yield this.reconnectForRetry(path, 'out', attempt, error);
|
|
727
|
-
}
|
|
728
|
-
catch (reconnectError) {
|
|
729
|
-
lastError = reconnectError;
|
|
730
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] Protocol V2 write reconnect failed on retry ${attempt}/${PACKET_IO_MAX_RETRIES}: ${this.getErrorMessage(reconnectError)}`);
|
|
731
|
-
break;
|
|
732
|
-
}
|
|
733
801
|
}
|
|
734
802
|
}
|
|
735
|
-
throw lastError;
|
|
736
803
|
});
|
|
737
804
|
}
|
|
738
|
-
|
|
805
|
+
resetProtocolV2UsbNativeLink(path, _reason) {
|
|
739
806
|
return __awaiter(this, void 0, void 0, function* () {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
assembler = new transport.ProtocolV2FrameAssembler(transport.PROTOCOL_V2_FRAME_MAX_BYTES);
|
|
743
|
-
this.protocolV2Assemblers.set(path, assembler);
|
|
744
|
-
}
|
|
745
|
-
let frame = assembler.push(new Uint8Array(0));
|
|
746
|
-
const deadline = timeoutMs ? Date.now() + timeoutMs : undefined;
|
|
747
|
-
while (!frame) {
|
|
748
|
-
const transferIn = this.transferInWithRetry(path, this.getOpenDevice(path), transport.PROTOCOL_V2_FRAME_MAX_BYTES);
|
|
749
|
-
const packet = deadline
|
|
750
|
-
? yield this.withProtocolReadTimeout(path, transferIn, Math.max(deadline - Date.now(), 1), 'V2')
|
|
751
|
-
: yield transferIn;
|
|
752
|
-
const bytes = new Uint8Array(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
|
|
753
|
-
try {
|
|
754
|
-
frame = assembler.push(bytes);
|
|
755
|
-
}
|
|
756
|
-
catch (error) {
|
|
757
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, error instanceof Error ? error.message : String(error));
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
return frame;
|
|
807
|
+
yield this.cancelActiveTransfers(path);
|
|
808
|
+
yield this.closeOpenDevice(path);
|
|
761
809
|
});
|
|
762
810
|
}
|
|
763
|
-
|
|
811
|
+
onProtocolV2UsbLinkInvalidated(path, reason) {
|
|
764
812
|
var _a;
|
|
813
|
+
this.deviceProtocol.delete(path);
|
|
814
|
+
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] Protocol V2 link invalidated: ${path}`, reason);
|
|
815
|
+
}
|
|
816
|
+
createProtocolV2UsbTimeoutError(name, timeoutMs) {
|
|
817
|
+
return new transport.ProtocolV2LinkError('response-timeout', `Protocol V2 response timeout after ${timeoutMs}ms for ${name}`);
|
|
818
|
+
}
|
|
819
|
+
callProtocolV2(path, name, data, options) {
|
|
765
820
|
return __awaiter(this, void 0, void 0, function* () {
|
|
766
|
-
|
|
767
|
-
if (!this.messagesV2) {
|
|
768
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured, 'Protocol V2 schema not configured');
|
|
769
|
-
}
|
|
770
|
-
if (!protocolV1Messages) {
|
|
771
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
772
|
-
}
|
|
773
|
-
let session = this.protocolV2Sessions.get(path);
|
|
774
|
-
if (!session) {
|
|
775
|
-
session = new transport.ProtocolV2Session({
|
|
776
|
-
schemas: {
|
|
777
|
-
protocolV1: protocolV1Messages,
|
|
778
|
-
protocolV2: this.messagesV2,
|
|
779
|
-
},
|
|
780
|
-
router: transport.PROTOCOL_V2_CHANNEL_USB,
|
|
781
|
-
writeFrame: (frame) => this.writeProtocolV2Frame(path, frame),
|
|
782
|
-
readFrame: () => this.receiveProtocolV2Frame(path, this.protocolV2ReadTimeouts.get(path)),
|
|
783
|
-
logger: this.Log,
|
|
784
|
-
logPrefix: 'ProtocolV2 NodeUSB',
|
|
785
|
-
createTimeoutError: (messageName, timeoutMs) => new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${messageName}`),
|
|
786
|
-
});
|
|
787
|
-
this.protocolV2Sessions.set(path, session);
|
|
788
|
-
}
|
|
789
|
-
this.protocolV2ReadTimeouts.set(path, options === null || options === void 0 ? void 0 : options.timeoutMs);
|
|
790
|
-
(_a = this.protocolV2Assemblers.get(path)) === null || _a === void 0 ? void 0 : _a.reset();
|
|
791
|
-
try {
|
|
792
|
-
return yield session.call(name, data, options);
|
|
793
|
-
}
|
|
794
|
-
finally {
|
|
795
|
-
this.protocolV2ReadTimeouts.delete(path);
|
|
796
|
-
}
|
|
821
|
+
return this.callProtocolV2Usb(path, name, data, options);
|
|
797
822
|
});
|
|
798
823
|
}
|
|
799
824
|
receiveData(path, dev, timeoutMs) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProtocolType } from '@onekeyfe/hd-transport';
|
|
2
|
+
export declare function shouldSuppressHighVolumeCallLog(name: string): boolean;
|
|
3
|
+
export declare function createTransportCallLog(name: string, protocol: ProtocolType): {
|
|
4
|
+
name: string;
|
|
5
|
+
protocol: ProtocolType;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=transportLog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transportLog.d.ts","sourceRoot":"","sources":["../src/transportLog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAI3D,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,WAE3D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY;;;EAE1E"}
|
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-usb",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.30",
|
|
4
4
|
"description": "OneKey hardware wallet direct USB transport plugin (libusb)",
|
|
5
5
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,14 +16,15 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w",
|
|
18
18
|
"build": "rimraf dist && rollup -c ../../build/rollup.config.js",
|
|
19
|
+
"test": "jest",
|
|
19
20
|
"lint": "eslint .",
|
|
20
21
|
"lint:fix": "eslint . --fix"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
23
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
24
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
24
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.30",
|
|
25
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.30",
|
|
25
26
|
"bytebuffer": "^5.0.1",
|
|
26
27
|
"usb": "^2.14.0"
|
|
27
28
|
},
|
|
28
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "07405d2e3dcdd0ba351ae98b7cf4f61690ef2913"
|
|
29
30
|
}
|