@onekeyfe/hd-transport-usb 1.2.0-alpha.11 → 1.2.0-alpha.111

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.
@@ -62,6 +62,7 @@ const createHarness = () => {
62
62
  const path = '6136';
63
63
  const responseQueue: Buffer[] = [];
64
64
  const sentSeqs: number[] = [];
65
+ let cancelledTransferCount = 0;
65
66
  let writeError: Error | undefined;
66
67
  let holdNextRead:
67
68
  | {
@@ -71,29 +72,84 @@ const createHarness = () => {
71
72
  }
72
73
  | undefined;
73
74
 
75
+ const performInTransfer = (callback: (error?: Error, data?: Buffer) => void) => {
76
+ if (epIn.timeout === 50) {
77
+ callback(new Error('LIBUSB_TRANSFER_TIMED_OUT'));
78
+ return;
79
+ }
80
+ if (holdNextRead) {
81
+ const pending = holdNextRead;
82
+ holdNextRead = undefined;
83
+ pending.callback = callback;
84
+ pending.markStarted();
85
+ return;
86
+ }
87
+ const response = responseQueue.shift();
88
+ if (!response) {
89
+ callback(new Error('LIBUSB_TRANSFER_TIMED_OUT'));
90
+ return;
91
+ }
92
+ callback(undefined, response);
93
+ };
94
+
74
95
  const epIn = {
75
96
  direction: 'in',
76
97
  address: 0x81,
77
98
  timeout: 30_000,
78
99
  transfer: jest.fn((_length: number, callback: (error?: Error, data?: Buffer) => void) => {
79
- if (epIn.timeout === 50) {
80
- callback(new Error('LIBUSB_TRANSFER_TIMED_OUT'));
81
- return;
82
- }
83
- if (holdNextRead) {
84
- const pending = holdNextRead;
85
- holdNextRead = undefined;
86
- pending.callback = callback;
87
- pending.markStarted();
88
- return;
89
- }
90
- const response = responseQueue.shift();
91
- if (!response) {
92
- callback(new Error('LIBUSB_TRANSFER_TIMED_OUT'));
93
- return;
94
- }
95
- callback(undefined, response);
100
+ performInTransfer(callback);
96
101
  }),
102
+ makeTransfer: jest.fn(
103
+ (
104
+ _timeout: number,
105
+ callback: (error: Error | undefined, data: Buffer, actualLength: number) => void
106
+ ) => {
107
+ let settled = false;
108
+ const finish = (error?: Error, data = Buffer.alloc(0)) => {
109
+ if (settled) return;
110
+ settled = true;
111
+ callback(error, data, data.length);
112
+ };
113
+ return {
114
+ submit: jest.fn((buffer: Buffer) => {
115
+ performInTransfer((error, data) => {
116
+ if (error) {
117
+ finish(error);
118
+ return;
119
+ }
120
+ data?.copy(buffer);
121
+ finish(undefined, buffer.subarray(0, data?.length ?? 0));
122
+ });
123
+ }),
124
+ cancel: jest.fn(() => {
125
+ cancelledTransferCount += 1;
126
+ finish(new Error('LIBUSB_TRANSFER_CANCELLED'));
127
+ }),
128
+ };
129
+ }
130
+ ),
131
+ };
132
+
133
+ const performOutTransfer = (data: Buffer, callback: (error?: Error) => void) => {
134
+ const seq = data[6];
135
+ sentSeqs.push(seq);
136
+ if (writeError) {
137
+ const error = writeError;
138
+ writeError = undefined;
139
+ callback(error);
140
+ return;
141
+ }
142
+ responseQueue.push(
143
+ Buffer.from(
144
+ ProtocolV2.encodeFrame(
145
+ schemas,
146
+ 'Success',
147
+ { message: 'ok' },
148
+ { router: PROTOCOL_V2_CHANNEL_USB, seq }
149
+ )
150
+ )
151
+ );
152
+ callback();
97
153
  };
98
154
 
99
155
  const epOut = {
@@ -101,33 +157,40 @@ const createHarness = () => {
101
157
  address: 0x01,
102
158
  timeout: 30_000,
103
159
  transfer: jest.fn((data: Buffer, callback: (error?: Error) => void) => {
104
- const seq = data[6];
105
- sentSeqs.push(seq);
106
- if (writeError) {
107
- const error = writeError;
108
- writeError = undefined;
109
- callback(error);
110
- return;
111
- }
112
- responseQueue.push(
113
- Buffer.from(
114
- ProtocolV2.encodeFrame(
115
- schemas,
116
- 'Success',
117
- { message: 'ok' },
118
- { router: PROTOCOL_V2_CHANNEL_USB, seq }
119
- )
120
- )
121
- );
122
- callback();
160
+ performOutTransfer(data, callback);
123
161
  }),
162
+ makeTransfer: jest.fn(
163
+ (
164
+ _timeout: number,
165
+ callback: (error: Error | undefined, data: Buffer, actualLength: number) => void
166
+ ) => {
167
+ let settled = false;
168
+ const finish = (error: Error | undefined, data: Buffer) => {
169
+ if (settled) return;
170
+ settled = true;
171
+ callback(error, data, data.length);
172
+ };
173
+ return {
174
+ submit: jest.fn((data: Buffer) => {
175
+ performOutTransfer(data, error => finish(error, data));
176
+ }),
177
+ cancel: jest.fn(() => {
178
+ cancelledTransferCount += 1;
179
+ finish(new Error('LIBUSB_TRANSFER_CANCELLED'), Buffer.alloc(0));
180
+ }),
181
+ };
182
+ }
183
+ ),
124
184
  };
125
185
 
126
186
  const iface = {
127
187
  descriptor: { bInterfaceClass: 0xff, bInterfaceNumber: 0 },
128
188
  endpoints: [epIn, epOut],
129
189
  claim: jest.fn(),
130
- release: jest.fn((callback: () => void) => callback()),
190
+ release: jest.fn((closeEndpointsOrCallback: boolean | (() => void), callback?: () => void) => {
191
+ if (typeof closeEndpointsOrCallback === 'function') closeEndpointsOrCallback();
192
+ else callback?.();
193
+ }),
131
194
  isKernelDriverActive: jest.fn(() => false),
132
195
  detachKernelDriver: jest.fn(),
133
196
  };
@@ -164,6 +227,7 @@ const createHarness = () => {
164
227
  epIn,
165
228
  epOut,
166
229
  sentSeqs,
230
+ getCancelledTransferCount: () => cancelledTransferCount,
167
231
  async acquire() {
168
232
  await transport.enumerate();
169
233
  await transport.acquire({ path, expectedProtocol: 'V2' });
@@ -189,7 +253,108 @@ const createHarness = () => {
189
253
  };
190
254
 
191
255
  describe('NodeUsbTransport Protocol V2 link lifecycle', () => {
192
- test('keeps seq across probe, call and reacquire', async () => {
256
+ test('falls back to Protocol V2 when a cached V1 hint is stale', async () => {
257
+ const transport = new NodeUsbTransport() as any;
258
+ const events: string[] = [];
259
+ transport.probeProtocolV1 = jest.fn().mockImplementation(() => {
260
+ events.push('probe-v1');
261
+ return Promise.resolve(false);
262
+ });
263
+ transport.resetConnectionAfterProbe = jest.fn().mockImplementation(() => {
264
+ events.push('reset');
265
+ return Promise.resolve();
266
+ });
267
+ transport.probeProtocolV2 = jest.fn().mockImplementation(() => {
268
+ events.push('probe-v2');
269
+ return Promise.resolve(true);
270
+ });
271
+
272
+ await expect(transport.detectProtocol('pro-usb', undefined, 'V1')).resolves.toBe('V2');
273
+
274
+ expect(events).toEqual(['probe-v1', 'reset', 'probe-v2']);
275
+ expect(transport.getProtocolType('pro-usb')).toBe('V2');
276
+ });
277
+
278
+ test('keeps active links when the Protocol V2 schema is configured repeatedly', () => {
279
+ const transport = new NodeUsbTransport() as any;
280
+ transport.invalidateAllProtocolV2UsbLinks = jest.fn().mockResolvedValue(undefined);
281
+ const schemaSource = JSON.stringify(protocolV2Schema);
282
+
283
+ transport.configureProtocolV2(schemaSource);
284
+ transport.configureProtocolV2(schemaSource);
285
+
286
+ expect(transport.invalidateAllProtocolV2UsbLinks).not.toHaveBeenCalled();
287
+ });
288
+
289
+ test('does not retry a native transfer cancelled by probe cleanup', () => {
290
+ const { transport } = createHarness();
291
+
292
+ expect((transport as any).isRetryableError(new Error('LIBUSB_TRANSFER_CANCELLED'))).toBe(false);
293
+ });
294
+
295
+ test('cancels pending native transfers before closing a timed-out protocol probe', async () => {
296
+ const harness = createHarness();
297
+ const { transport, path } = harness;
298
+ await harness.acquire();
299
+ const cancelActiveTransfers = jest.spyOn(transport as any, 'cancelActiveTransfers');
300
+ const closeOpenDevice = jest.spyOn(transport as any, 'closeOpenDevice');
301
+
302
+ await (transport as any).resetConnectionAfterProbe(path);
303
+
304
+ expect(cancelActiveTransfers).toHaveBeenCalledWith(path);
305
+ expect(closeOpenDevice).toHaveBeenCalledWith(path);
306
+ expect(cancelActiveTransfers.mock.invocationCallOrder[0]).toBeLessThan(
307
+ closeOpenDevice.mock.invocationCallOrder[0]
308
+ );
309
+ });
310
+
311
+ test('releases the USB interface when protocol detection rejects acquire', async () => {
312
+ const harness = createHarness();
313
+ const { transport, path, device, iface } = harness;
314
+ await transport.enumerate();
315
+ device.close.mockClear();
316
+ iface.release.mockClear();
317
+ jest
318
+ .spyOn(transport as any, 'detectProtocol')
319
+ .mockRejectedValueOnce(new Error('terminal protocol probe failure'));
320
+
321
+ await expect(transport.acquire({ path })).rejects.toMatchObject({
322
+ errorCode: expect.any(Number),
323
+ });
324
+
325
+ expect(iface.release).toHaveBeenCalledTimes(1);
326
+ expect(device.close).toHaveBeenCalledTimes(1);
327
+ expect((transport as any).openDevices.has(path)).toBe(false);
328
+ });
329
+
330
+ test('stop releases an acquired USB interface even before a Protocol V2 call', async () => {
331
+ const harness = createHarness();
332
+ const { transport, path, device, iface } = harness;
333
+ await transport.enumerate();
334
+ await transport.acquire({ path, expectedProtocol: 'V2' });
335
+ device.close.mockClear();
336
+ iface.release.mockClear();
337
+
338
+ await transport.stop();
339
+
340
+ expect(iface.release).toHaveBeenCalledTimes(1);
341
+ expect(device.close).toHaveBeenCalledTimes(1);
342
+ expect(transport.getProtocolType(path)).toBeUndefined();
343
+ });
344
+
345
+ test('actively probes explicit Protocol V2 during bootloader reconnect', async () => {
346
+ const harness = createHarness();
347
+ const { transport, path, epOut } = harness;
348
+
349
+ await transport.enumerate();
350
+ await transport.acquire({ path, expectedProtocol: 'V2' });
351
+
352
+ expect(epOut.makeTransfer).toHaveBeenCalledTimes(1);
353
+ expect(transport.getProtocolType(path)).toBe('V2');
354
+ await transport.release(path);
355
+ });
356
+
357
+ test('keeps seq across calls and actively probed reacquire', async () => {
193
358
  const harness = createHarness();
194
359
  const { transport, path, sentSeqs } = harness;
195
360
 
@@ -207,14 +372,14 @@ describe('NodeUsbTransport Protocol V2 link lifecycle', () => {
207
372
  const harness = createHarness();
208
373
  const { transport, path, epOut } = harness;
209
374
  await harness.acquire();
210
- epOut.transfer.mockClear();
375
+ epOut.makeTransfer.mockClear();
211
376
  harness.failNextWrite(new Error('LIBUSB_ERROR_IO'));
212
377
 
213
378
  await expect(transport.call(path, 'Ping', { message: 'write-failure' })).rejects.toThrow(
214
379
  'LIBUSB_ERROR_IO'
215
380
  );
216
381
 
217
- expect(epOut.transfer).toHaveBeenCalledTimes(1);
382
+ expect(epOut.makeTransfer).toHaveBeenCalledTimes(1);
218
383
  });
219
384
 
220
385
  test('rejects a pending read when release invalidates the link', async () => {
@@ -241,6 +406,20 @@ describe('NodeUsbTransport Protocol V2 link lifecycle', () => {
241
406
  expect(settled).not.toBe('still pending');
242
407
  });
243
408
 
409
+ test('stop cancels an in-flight native USB transfer before releasing the interface', async () => {
410
+ const harness = createHarness();
411
+ const { transport, path } = harness;
412
+ await harness.acquire();
413
+ const pendingRead = harness.holdRead();
414
+
415
+ const call = transport.call(path, 'Ping', { message: 'pending' }, { timeoutMs: 5000 });
416
+ await pendingRead.started;
417
+ await transport.stop();
418
+
419
+ await expect(call).rejects.toThrow();
420
+ expect(harness.getCancelledTransferCount()).toBe(1);
421
+ });
422
+
244
423
  test('keeps the cursor after a response timeout rebuilds the USB connection', async () => {
245
424
  const harness = createHarness();
246
425
  const { transport, path, sentSeqs } = harness;
package/dist/index.d.ts CHANGED
@@ -2,48 +2,119 @@ import * as transport from '@onekeyfe/hd-transport';
2
2
  import transport__default, { ProtocolV2UsbTransportBase, OneKeyDeviceInfo, AcquireInput, TransportCallOptions, ProtocolV2Schemas, ProtocolV2CallContext, ProtocolType } from '@onekeyfe/hd-transport';
3
3
  import EventEmitter from 'events';
4
4
 
5
+ /**
6
+ * Node.js USB Transport — complete transport implementation using libusb.
7
+ *
8
+ * Unlike the old UsbPlugin (which was a LowlevelTransportSharedPlugin piped
9
+ * through LowlevelTransport), this class is a standalone transport that handles
10
+ * both protocol encoding/decoding and USB I/O directly.
11
+ *
12
+ * Modeled after WebUsbTransport.
13
+ */
5
14
  declare class NodeUsbTransport extends ProtocolV2UsbTransportBase<string> {
6
15
  messages: ReturnType<typeof transport__default.parseConfigure> | undefined;
16
+ /** Protobuf schema for Protocol V2 transports. */
7
17
  messagesV2: ReturnType<typeof transport__default.parseConfigure> | undefined;
18
+ private protocolV2SchemaSource;
8
19
  name: string;
9
20
  version: string;
10
21
  configured: boolean;
11
22
  isOutdated: boolean;
12
23
  Log?: any;
13
24
  emitter?: EventEmitter;
25
+ /** serial → bus id, built during enumerate */
14
26
  private serialToBusId;
27
+ /** path → opened device state */
15
28
  private openDevices;
29
+ /** Per-path protocol type detected by active wire-level probe. */
16
30
  private deviceProtocol;
31
+ /** per-path reconnect lock to prevent concurrent reconnects */
17
32
  private reconnectLocks;
33
+ /**
34
+ * Retain the low-level Transfer so release()/stop() can cancel native pending reads.
35
+ * Endpoint.transfer() hides it and may keep the CLI alive after output completes.
36
+ */
37
+ private activeTransfers;
38
+ /** set to true when cancel() is called; checked by retry loops */
18
39
  private cancelled;
19
40
  constructor();
41
+ /**
42
+ * Initialize transport.
43
+ * Signature matches the Transport.init interface (logger, emitter).
44
+ */
20
45
  init(logger: any, emitter?: EventEmitter): Promise<string>;
21
46
  configure(signedData: any): Promise<void>;
22
47
  configureProtocolV2(signedData: any): void;
23
48
  listen(): void;
24
- stop(): void;
49
+ stop(): Promise<void>;
50
+ /**
51
+ * Low-level post (send only, no response). Not used by NodeUsbTransport
52
+ * since call() handles the full send+receive cycle, but required by the Transport interface.
53
+ */
25
54
  post(path: string, name: string, data: Record<string, unknown>): Promise<void>;
55
+ /**
56
+ * Low-level read (receive only). Not used by NodeUsbTransport
57
+ * since call() handles the full send+receive cycle, but required by the Transport interface.
58
+ */
26
59
  read(path: string): Promise<{
27
60
  message: {
28
61
  [key: string]: any;
29
62
  };
30
63
  type: string;
31
64
  }>;
65
+ /**
66
+ * Enumerate connected OneKey USB devices.
67
+ * Opens each device briefly to read its serial number (used as `path`),
68
+ * then closes it. acquire() re-opens from a fresh getDeviceList().
69
+ */
32
70
  enumerate(): Promise<OneKeyDeviceInfo[]>;
71
+ /**
72
+ * Acquire device — open USB device, claim interface, return path (string).
73
+ */
33
74
  acquire(input: AcquireInput): Promise<string>;
75
+ /**
76
+ * Release device — release interface and close.
77
+ */
34
78
  release(path: string, _onclose?: boolean): Promise<void>;
79
+ private cancelActiveTransfers;
80
+ private createTrackedTransfer;
81
+ private transferInOnce;
82
+ private transferOutOnce;
35
83
  private closeOpenDevice;
84
+ /**
85
+ * Call device method — encode protobuf, send packets, receive response.
86
+ * This is the core method that replaces LowlevelTransport's call + UsbPlugin's send/receive.
87
+ */
36
88
  call(path: string, name: string, data: Record<string, unknown>, options?: TransportCallOptions): Promise<transport.MessageFromOneKey>;
37
89
  private callProtocolV1;
38
90
  cancel(): void;
91
+ /**
92
+ * Get the current open device for a path, re-resolving from the map
93
+ * so callers always use a fresh reference after reconnect.
94
+ */
39
95
  private getOpenDevice;
40
96
  private getErrorMessage;
41
97
  private isRetryableError;
42
98
  private isUsbTransferTimeout;
43
99
  private getDeviceInterface;
100
+ /**
101
+ * Reconnect device before retrying a failed transfer (aligned with WebUsbTransport).
102
+ * Uses per-path lock to prevent concurrent reconnects to the same device.
103
+ */
44
104
  private reconnectForRetry;
105
+ /**
106
+ * Send all encoded chunks to the device with retry.
107
+ * If a chunk fails and triggers reconnect, the entire sequence restarts
108
+ * from chunk 0 because the device resets protocol state on reconnect.
109
+ */
45
110
  private sendAllChunksWithRetry;
111
+ /**
112
+ * USB IN transfer with retry and reconnect (aligned with WebUsbTransport).
113
+ */
46
114
  private transferInWithRetry;
115
+ /**
116
+ * Open a USB device by path (serial number), claim interface, cache endpoints.
117
+ */
47
118
  private openDevice;
48
119
  private drainStaleInput;
49
120
  private createProtocolMismatchError;
@@ -61,6 +132,10 @@ declare class NodeUsbTransport extends ProtocolV2UsbTransportBase<string> {
61
132
  protected onProtocolV2UsbLinkInvalidated(path: string, reason: string): void;
62
133
  protected createProtocolV2UsbTimeoutError(name: string, timeoutMs: number): Error;
63
134
  private callProtocolV2;
135
+ /**
136
+ * Receive a complete protobuf response from the device.
137
+ * Reads 64-byte packets, strips 0x3F marker, reassembles into hex string.
138
+ */
64
139
  private receiveData;
65
140
  getProtocolType(path: string): ProtocolType | undefined;
66
141
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -132,26 +132,6 @@ function readSerialNumber(dev, openDevices) {
132
132
  }
133
133
  });
134
134
  }
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
135
  function skipReportByte(packet) {
156
136
  if (packet[0] === REPORT_ID) {
157
137
  return packet.subarray(1);
@@ -176,6 +156,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
176
156
  this.openDevices = new Map();
177
157
  this.deviceProtocol = new Map();
178
158
  this.reconnectLocks = new Map();
159
+ this.activeTransfers = new Map();
179
160
  this.cancelled = false;
180
161
  }
181
162
  init(logger, emitter) {
@@ -191,14 +172,38 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
191
172
  }
192
173
  configureProtocolV2(signedData) {
193
174
  var _a;
175
+ const schemaSource = typeof signedData === 'string'
176
+ ? signedData
177
+ : (_a = JSON.stringify(signedData)) !== null && _a !== void 0 ? _a : String(signedData);
178
+ if (schemaSource === this.protocolV2SchemaSource)
179
+ return;
180
+ const hadProtocolV2Schema = this.protocolV2SchemaSource !== undefined;
194
181
  this.messagesV2 = parseConfigure(signedData);
182
+ this.protocolV2SchemaSource = schemaSource;
183
+ if (!hadProtocolV2Schema)
184
+ return;
195
185
  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); });
196
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] Protocol V2 schema configured');
197
186
  }
198
187
  listen() {
199
188
  }
200
189
  stop() {
201
- this.disposeProtocolV2UsbLinks('Node USB transport stopped').catch(error => { var _a; return (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] stop link cleanup failed:', error); });
190
+ var _a;
191
+ return __awaiter(this, void 0, void 0, function* () {
192
+ this.cancelled = true;
193
+ yield this.cancelActiveTransfers();
194
+ try {
195
+ yield this.disposeProtocolV2UsbLinks('Node USB transport stopped');
196
+ }
197
+ catch (error) {
198
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] stop link cleanup failed:', error);
199
+ }
200
+ yield Promise.allSettled(this.reconnectLocks.values());
201
+ yield this.cancelActiveTransfers();
202
+ yield Promise.all(Array.from(this.openDevices.keys(), path => this.closeOpenDevice(path)));
203
+ this.reconnectLocks.clear();
204
+ this.deviceProtocol.clear();
205
+ this.serialToBusId.clear();
206
+ });
202
207
  }
203
208
  post(path, name, data) {
204
209
  return __awaiter(this, void 0, void 0, function* () {
@@ -248,7 +253,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
248
253
  });
249
254
  }
250
255
  acquire(input) {
251
- var _a, _b, _c;
256
+ var _a, _b, _c, _d;
252
257
  return __awaiter(this, void 0, void 0, function* () {
253
258
  this.cancelled = false;
254
259
  const path = (_a = input.path) !== null && _a !== void 0 ? _a : '';
@@ -259,22 +264,100 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
259
264
  yield this.rotateProtocolV2UsbGeneration(path, 'Node USB transport acquired');
260
265
  yield this.closeOpenDevice(path);
261
266
  yield this.openDevice(path);
262
- yield this.detectProtocol(path, input.expectedProtocol);
267
+ yield this.detectProtocol(path, input.expectedProtocol, input.protocolHint);
263
268
  return path;
264
269
  }
265
270
  catch (error) {
266
271
  (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('NodeUsbTransport acquire error: ', error);
267
- throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, (_c = error.message) !== null && _c !== void 0 ? _c : String(error));
272
+ try {
273
+ yield this.release(path);
274
+ }
275
+ catch (cleanupError) {
276
+ (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug('NodeUsbTransport acquire cleanup error: ', cleanupError);
277
+ }
278
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, (_d = error.message) !== null && _d !== void 0 ? _d : String(error));
268
279
  }
269
280
  });
270
281
  }
271
282
  release(path, _onclose) {
272
283
  return __awaiter(this, void 0, void 0, function* () {
284
+ yield this.cancelActiveTransfers(path);
273
285
  yield this.invalidateProtocolV2UsbLink(path, 'Node USB transport released');
274
286
  yield this.closeOpenDevice(path);
275
287
  this.deviceProtocol.delete(path);
276
288
  });
277
289
  }
290
+ cancelActiveTransfers(path) {
291
+ return __awaiter(this, void 0, void 0, function* () {
292
+ const transfers = Array.from(this.activeTransfers.entries()).filter(([, active]) => path === undefined || active.path === path);
293
+ transfers.forEach(([transfer]) => {
294
+ try {
295
+ transfer.cancel();
296
+ }
297
+ catch (_a) {
298
+ }
299
+ });
300
+ yield Promise.allSettled(transfers.map(([, active]) => active.settled));
301
+ });
302
+ }
303
+ createTrackedTransfer(path, endpoint, callback) {
304
+ let resolveSettled = () => undefined;
305
+ const settled = new Promise(resolve => {
306
+ resolveSettled = resolve;
307
+ });
308
+ const transfer = endpoint.makeTransfer(endpoint.timeout, (error, buffer, actualLength) => {
309
+ this.activeTransfers.delete(transfer);
310
+ resolveSettled();
311
+ callback(error, buffer, actualLength);
312
+ });
313
+ this.activeTransfers.set(transfer, { path, settled, resolveSettled });
314
+ return transfer;
315
+ }
316
+ transferInOnce(path, ep, length) {
317
+ return new Promise((resolve, reject) => {
318
+ const buffer = Buffer.alloc(length);
319
+ const transfer = this.createTrackedTransfer(path, ep, (error, _submittedBuffer, actualLength) => {
320
+ if (error) {
321
+ reject(error);
322
+ return;
323
+ }
324
+ if (actualLength <= 0) {
325
+ reject(new Error('Empty USB transfer'));
326
+ return;
327
+ }
328
+ resolve(buffer.subarray(0, actualLength));
329
+ });
330
+ try {
331
+ transfer.submit(buffer);
332
+ }
333
+ catch (error) {
334
+ const active = this.activeTransfers.get(transfer);
335
+ this.activeTransfers.delete(transfer);
336
+ active === null || active === void 0 ? void 0 : active.resolveSettled();
337
+ reject(error);
338
+ }
339
+ });
340
+ }
341
+ transferOutOnce(path, ep, data) {
342
+ return new Promise((resolve, reject) => {
343
+ const transfer = this.createTrackedTransfer(path, ep, error => {
344
+ if (error) {
345
+ reject(error);
346
+ return;
347
+ }
348
+ resolve();
349
+ });
350
+ try {
351
+ transfer.submit(data);
352
+ }
353
+ catch (error) {
354
+ const active = this.activeTransfers.get(transfer);
355
+ this.activeTransfers.delete(transfer);
356
+ active === null || active === void 0 ? void 0 : active.resolveSettled();
357
+ reject(error);
358
+ }
359
+ });
360
+ }
278
361
  closeOpenDevice(path) {
279
362
  return __awaiter(this, void 0, void 0, function* () {
280
363
  const openDev = this.openDevices.get(path);
@@ -303,7 +386,6 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
303
386
  });
304
387
  }
305
388
  call(path, name, data, options) {
306
- var _a, _b;
307
389
  return __awaiter(this, void 0, void 0, function* () {
308
390
  this.cancelled = false;
309
391
  if (!this.messages) {
@@ -316,12 +398,6 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
316
398
  if (!protocol) {
317
399
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, `Device protocol has not been detected for ${path}`);
318
400
  }
319
- if (transport.LogBlockCommand.has(name)) {
320
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('NodeUsbTransport call-', ' name: ', name, ' protocol: ', protocol);
321
- }
322
- else {
323
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('NodeUsbTransport call-', ' name: ', name, ' data: ', data, ' protocol: ', protocol);
324
- }
325
401
  if (protocol === 'V2') {
326
402
  return this.callProtocolV2(path, name, data, options);
327
403
  }
@@ -345,8 +421,6 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
345
421
  });
346
422
  }
347
423
  cancel() {
348
- var _a;
349
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('NodeUsbTransport cancel');
350
424
  this.cancelled = true;
351
425
  }
352
426
  getOpenDevice(path) {
@@ -369,6 +443,9 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
369
443
  }
370
444
  isRetryableError(error) {
371
445
  const message = this.getErrorMessage(error).toLowerCase();
446
+ if (message.includes('cancelled') || message.includes('canceled')) {
447
+ return false;
448
+ }
372
449
  return (message.includes('libusb') ||
373
450
  message.includes('transfer') ||
374
451
  message.includes('disconnected') ||
@@ -437,7 +514,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
437
514
  const packet = new Uint8Array(PACKET_SIZE);
438
515
  packet[0] = REPORT_ID;
439
516
  packet.set(new Uint8Array(buffer), 1);
440
- yield transferOutOnce(this.getOpenDevice(path).epOut, Buffer.from(packet));
517
+ yield this.transferOutOnce(path, this.getOpenDevice(path).epOut, Buffer.from(packet));
441
518
  }
442
519
  return;
443
520
  }
@@ -470,7 +547,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
470
547
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
471
548
  }
472
549
  try {
473
- return yield transferInOnce(currentDev.epIn, length);
550
+ return yield this.transferInOnce(path, currentDev.epIn, length);
474
551
  }
475
552
  catch (error) {
476
553
  lastError = error;
@@ -529,7 +606,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
529
606
  }
530
607
  epIn.timeout = TRANSFER_TIMEOUT_MS;
531
608
  epOut.timeout = TRANSFER_TIMEOUT_MS;
532
- yield this.drainStaleInput(epIn);
609
+ yield this.drainStaleInput(path, epIn);
533
610
  this.openDevices.set(path, { device: dev, iface, epIn, epOut });
534
611
  }
535
612
  catch (err) {
@@ -542,14 +619,14 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
542
619
  }
543
620
  });
544
621
  }
545
- drainStaleInput(epIn) {
622
+ drainStaleInput(path, epIn) {
546
623
  return __awaiter(this, void 0, void 0, function* () {
547
624
  const originalTimeout = epIn.timeout;
548
625
  epIn.timeout = 50;
549
626
  try {
550
627
  for (let index = 0; index < 16; index += 1) {
551
628
  try {
552
- yield transferInOnce(epIn, PACKET_SIZE);
629
+ yield this.transferInOnce(path, epIn, PACKET_SIZE);
553
630
  }
554
631
  catch (_a) {
555
632
  break;
@@ -567,39 +644,34 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
567
644
  createProtocolDetectionError() {
568
645
  return hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Unable to detect USB protocol: device did not respond to Protocol V1 Initialize or Protocol V2 Ping');
569
646
  }
570
- detectProtocol(path, expectedProtocol) {
571
- var _a, _b, _c, _d, _e;
647
+ detectProtocol(path, expectedProtocol, protocolHint) {
648
+ var _a;
572
649
  return __awaiter(this, void 0, void 0, function* () {
573
- if (expectedProtocol === 'V1') {
574
- if (yield this.probeProtocolV1(path)) {
575
- this.deviceProtocol.set(path, 'V1');
576
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
577
- return 'V1';
578
- }
579
- throw this.createProtocolMismatchError(expectedProtocol);
580
- }
581
650
  if (expectedProtocol === 'V2') {
582
651
  if (yield this.probeProtocolV2(path)) {
583
652
  this.deviceProtocol.set(path, 'V2');
584
- (_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
653
+ (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (expected)`);
585
654
  return 'V2';
586
655
  }
587
656
  throw this.createProtocolMismatchError(expectedProtocol);
588
657
  }
589
- if (this.deviceProtocol.get(path) === 'V2' && (yield this.probeProtocolV2(path))) {
590
- this.deviceProtocol.set(path, 'V2');
591
- (_c = this.Log) === null || _c === void 0 ? void 0 : _c.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
592
- return 'V2';
593
- }
594
- if (yield this.probeProtocolV1(path)) {
595
- this.deviceProtocol.set(path, 'V1');
596
- (_d = this.Log) === null || _d === void 0 ? void 0 : _d.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1`);
597
- return 'V1';
658
+ if (expectedProtocol === 'V1') {
659
+ if (yield this.probeProtocolV1(path)) {
660
+ this.deviceProtocol.set(path, 'V1');
661
+ return 'V1';
662
+ }
663
+ throw this.createProtocolMismatchError(expectedProtocol);
598
664
  }
599
- if (yield this.probeProtocolV2(path)) {
600
- this.deviceProtocol.set(path, 'V2');
601
- (_e = this.Log) === null || _e === void 0 ? void 0 : _e.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2`);
602
- return 'V2';
665
+ const probeOrder = protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
666
+ for (const [index, protocol] of probeOrder.entries()) {
667
+ if (index > 0) {
668
+ yield this.resetConnectionAfterProbe(path);
669
+ }
670
+ const detected = protocol === 'V1' ? yield this.probeProtocolV1(path) : yield this.probeProtocolV2(path);
671
+ if (detected) {
672
+ this.deviceProtocol.set(path, protocol);
673
+ return protocol;
674
+ }
603
675
  }
604
676
  this.deviceProtocol.delete(path);
605
677
  throw this.createProtocolDetectionError();
@@ -610,6 +682,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
610
682
  return __awaiter(this, void 0, void 0, function* () {
611
683
  yield this.rotateProtocolV2UsbGeneration(path, 'Node USB protocol probe reset');
612
684
  try {
685
+ yield this.cancelActiveTransfers(path);
613
686
  yield this.closeOpenDevice(path);
614
687
  }
615
688
  catch (error) {
@@ -657,7 +730,6 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
657
730
  });
658
731
  }
659
732
  probeProtocolV1(path) {
660
- var _a;
661
733
  return __awaiter(this, void 0, void 0, function* () {
662
734
  if (!this.messages) {
663
735
  return false;
@@ -666,8 +738,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
666
738
  yield this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
667
739
  return true;
668
740
  }
669
- catch (error) {
670
- (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('[NodeUsbTransport] Protocol V1 Initialize probe failed:', error);
741
+ catch (_error) {
671
742
  return false;
672
743
  }
673
744
  });
@@ -703,7 +774,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
703
774
  if (this.cancelled) {
704
775
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
705
776
  }
706
- yield transferOutOnce(this.getOpenDevice(path).epOut, Buffer.from(frame));
777
+ yield this.transferOutOnce(path, this.getOpenDevice(path).epOut, Buffer.from(frame));
707
778
  });
708
779
  }
709
780
  readProtocolV2UsbPacket(path, _context) {
@@ -713,7 +784,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
713
784
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
714
785
  }
715
786
  try {
716
- const packet = yield transferInOnce(this.getOpenDevice(path).epIn, transport.PROTOCOL_V2_FRAME_MAX_BYTES);
787
+ const packet = yield this.transferInOnce(path, this.getOpenDevice(path).epIn, transport.PROTOCOL_V2_FRAME_MAX_BYTES);
717
788
  return new Uint8Array(packet.buffer.slice(packet.byteOffset, packet.byteOffset + packet.byteLength));
718
789
  }
719
790
  catch (error) {
@@ -726,6 +797,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
726
797
  }
727
798
  resetProtocolV2UsbNativeLink(path, _reason) {
728
799
  return __awaiter(this, void 0, void 0, function* () {
800
+ yield this.cancelActiveTransfers(path);
729
801
  yield this.closeOpenDevice(path);
730
802
  });
731
803
  }
@@ -735,7 +807,7 @@ class NodeUsbTransport extends transport.ProtocolV2UsbTransportBase {
735
807
  (_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug(`[NodeUsbTransport] Protocol V2 link invalidated: ${path}`, reason);
736
808
  }
737
809
  createProtocolV2UsbTimeoutError(name, timeoutMs) {
738
- return new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${name}`);
810
+ return new transport.ProtocolV2LinkError('response-timeout', `Protocol V2 response timeout after ${timeoutMs}ms for ${name}`);
739
811
  }
740
812
  callProtocolV2(path, name, data, options) {
741
813
  return __awaiter(this, void 0, void 0, function* () {
@@ -0,0 +1,2 @@
1
+ export { createTransportCallLog, shouldSuppressHighVolumeCallLog } from '@onekeyfe/hd-transport';
2
+ //# sourceMappingURL=transportLog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transportLog.d.ts","sourceRoot":"","sources":["../src/transportLog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAC"}
package/jest.config.js ADDED
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ preset: '../../jest.config.js',
3
+ testEnvironment: 'node',
4
+ modulePathIgnorePatterns: ['node_modules', '<rootDir>/dist'],
5
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-transport-usb",
3
- "version": "1.2.0-alpha.11",
3
+ "version": "1.2.0-alpha.111",
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.11",
24
- "@onekeyfe/hd-transport": "1.2.0-alpha.11",
24
+ "@onekeyfe/hd-shared": "1.2.0-alpha.111",
25
+ "@onekeyfe/hd-transport": "1.2.0-alpha.111",
25
26
  "bytebuffer": "^5.0.1",
26
27
  "usb": "^2.14.0"
27
28
  },
28
- "gitHead": "05d3421ec00e07e99f6665e6436a612503e32dfc"
29
+ "gitHead": "7619f316471ad423fb83f68e82e1aec89f34ac0a"
29
30
  }
package/src/index.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import ByteBuffer from 'bytebuffer';
2
2
  import * as usb from 'usb';
3
3
  import transport, {
4
- LogBlockCommand,
5
4
  PROTOCOL_V1_CHUNK_PAYLOAD_SIZE,
6
5
  PROTOCOL_V1_MESSAGE_HEADER_SIZE,
7
6
  PROTOCOL_V1_REPORT_ID,
8
7
  PROTOCOL_V1_USB_PACKET_SIZE,
9
8
  PROTOCOL_V2_CHANNEL_USB,
10
9
  PROTOCOL_V2_FRAME_MAX_BYTES,
10
+ ProtocolV2LinkError,
11
11
  ProtocolV2UsbTransportBase,
12
12
  probeProtocolV2 as probeProtocolV2Helper,
13
13
  } from '@onekeyfe/hd-transport';
@@ -130,31 +130,6 @@ function readSerialNumber(dev: usb.Device, openDevices?: Map<string, OpenDevice>
130
130
  });
131
131
  }
132
132
 
133
- /**
134
- * Promisified USB IN transfer (single attempt).
135
- */
136
- function transferInOnce(ep: usb.InEndpoint, length: number): Promise<Buffer> {
137
- return new Promise((resolve, reject) => {
138
- ep.transfer(length, (err: Error | undefined, data: Buffer | undefined) => {
139
- if (err) return reject(err);
140
- if (!data || data.length === 0) return reject(new Error('Empty USB transfer'));
141
- resolve(data);
142
- });
143
- });
144
- }
145
-
146
- /**
147
- * Promisified USB OUT transfer (single attempt).
148
- */
149
- function transferOutOnce(ep: usb.OutEndpoint, data: Buffer): Promise<void> {
150
- return new Promise((resolve, reject) => {
151
- ep.transfer(data, (err: Error | undefined) => {
152
- if (err) return reject(err);
153
- resolve();
154
- });
155
- });
156
- }
157
-
158
133
  /**
159
134
  * Skip the 0x3F protocol marker byte from a USB packet.
160
135
  */
@@ -187,6 +162,8 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
187
162
  /** Protobuf schema for Protocol V2 transports. */
188
163
  messagesV2: ReturnType<typeof transport.parseConfigure> | undefined;
189
164
 
165
+ private protocolV2SchemaSource: string | undefined;
166
+
190
167
  name = 'NodeUsbTransport';
191
168
 
192
169
  version = '';
@@ -211,6 +188,15 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
211
188
  /** per-path reconnect lock to prevent concurrent reconnects */
212
189
  private reconnectLocks = new Map<string, Promise<OpenDevice>>();
213
190
 
191
+ /**
192
+ * Retain the low-level Transfer so release()/stop() can cancel native pending reads.
193
+ * Endpoint.transfer() hides it and may keep the CLI alive after output completes.
194
+ */
195
+ private activeTransfers = new Map<
196
+ usb.Transfer,
197
+ { path: string; settled: Promise<void>; resolveSettled: () => void }
198
+ >();
199
+
214
200
  /** set to true when cancel() is called; checked by retry loops */
215
201
  private cancelled = false;
216
202
 
@@ -240,21 +226,47 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
240
226
  }
241
227
 
242
228
  configureProtocolV2(signedData: any) {
229
+ const schemaSource =
230
+ typeof signedData === 'string'
231
+ ? signedData
232
+ : JSON.stringify(signedData) ?? String(signedData);
233
+ if (schemaSource === this.protocolV2SchemaSource) return;
234
+
235
+ const hadProtocolV2Schema = this.protocolV2SchemaSource !== undefined;
243
236
  this.messagesV2 = parseConfigure(signedData);
237
+ this.protocolV2SchemaSource = schemaSource;
238
+ if (!hadProtocolV2Schema) return;
239
+
244
240
  this.invalidateAllProtocolV2UsbLinks('Protocol V2 schema reconfigured').catch(error =>
245
241
  this.Log?.debug('[NodeUsbTransport] schema link cleanup failed:', error)
246
242
  );
247
- this.Log?.debug('[NodeUsbTransport] Protocol V2 schema configured');
248
243
  }
249
244
 
250
245
  listen() {
251
246
  // empty — could add hotplug events via usb.on('attach'/'detach')
252
247
  }
253
248
 
254
- stop() {
255
- this.disposeProtocolV2UsbLinks('Node USB transport stopped').catch(error =>
256
- this.Log?.debug('[NodeUsbTransport] stop link cleanup failed:', error)
257
- );
249
+ async stop(): Promise<void> {
250
+ this.cancelled = true;
251
+
252
+ await this.cancelActiveTransfers();
253
+
254
+ try {
255
+ await this.disposeProtocolV2UsbLinks('Node USB transport stopped');
256
+ } catch (error) {
257
+ this.Log?.debug('[NodeUsbTransport] stop link cleanup failed:', error);
258
+ }
259
+
260
+ // Reconnect may already be in flight when dispose starts. Wait for it to
261
+ // settle, then close every remaining handle so it cannot reopen USB after
262
+ // the first cleanup pass.
263
+ await Promise.allSettled(this.reconnectLocks.values());
264
+ await this.cancelActiveTransfers();
265
+ await Promise.all(Array.from(this.openDevices.keys(), path => this.closeOpenDevice(path)));
266
+
267
+ this.reconnectLocks.clear();
268
+ this.deviceProtocol.clear();
269
+ this.serialToBusId.clear();
258
270
  }
259
271
 
260
272
  /**
@@ -332,10 +344,15 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
332
344
  await this.rotateProtocolV2UsbGeneration(path, 'Node USB transport acquired');
333
345
  await this.closeOpenDevice(path);
334
346
  await this.openDevice(path);
335
- await this.detectProtocol(path, input.expectedProtocol);
347
+ await this.detectProtocol(path, input.expectedProtocol, input.protocolHint);
336
348
  return path;
337
349
  } catch (error: any) {
338
350
  this.Log?.debug('NodeUsbTransport acquire error: ', error);
351
+ try {
352
+ await this.release(path);
353
+ } catch (cleanupError) {
354
+ this.Log?.debug('NodeUsbTransport acquire cleanup error: ', cleanupError);
355
+ }
339
356
  throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, error.message ?? String(error));
340
357
  }
341
358
  }
@@ -344,11 +361,93 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
344
361
  * Release device — release interface and close.
345
362
  */
346
363
  async release(path: string, _onclose?: boolean): Promise<void> {
364
+ await this.cancelActiveTransfers(path);
347
365
  await this.invalidateProtocolV2UsbLink(path, 'Node USB transport released');
348
366
  await this.closeOpenDevice(path);
349
367
  this.deviceProtocol.delete(path);
350
368
  }
351
369
 
370
+ private async cancelActiveTransfers(path?: string): Promise<void> {
371
+ const transfers = Array.from(this.activeTransfers.entries()).filter(
372
+ ([, active]) => path === undefined || active.path === path
373
+ );
374
+ transfers.forEach(([transfer]) => {
375
+ try {
376
+ transfer.cancel();
377
+ } catch {
378
+ // A transfer may finish between the snapshot and cancel().
379
+ }
380
+ });
381
+ await Promise.allSettled(transfers.map(([, active]) => active.settled));
382
+ }
383
+
384
+ private createTrackedTransfer(
385
+ path: string,
386
+ endpoint: usb.InEndpoint | usb.OutEndpoint,
387
+ callback: (error: Error | undefined, buffer: Buffer, actualLength: number) => void
388
+ ): usb.Transfer {
389
+ let resolveSettled: () => void = () => undefined;
390
+ const settled = new Promise<void>(resolve => {
391
+ resolveSettled = resolve;
392
+ });
393
+ const transfer = endpoint.makeTransfer(endpoint.timeout, (error, buffer, actualLength) => {
394
+ this.activeTransfers.delete(transfer);
395
+ resolveSettled();
396
+ callback(error, buffer, actualLength);
397
+ });
398
+ this.activeTransfers.set(transfer, { path, settled, resolveSettled });
399
+ return transfer;
400
+ }
401
+
402
+ private transferInOnce(path: string, ep: usb.InEndpoint, length: number): Promise<Buffer> {
403
+ return new Promise((resolve, reject) => {
404
+ const buffer = Buffer.alloc(length);
405
+ const transfer = this.createTrackedTransfer(
406
+ path,
407
+ ep,
408
+ (error, _submittedBuffer, actualLength) => {
409
+ if (error) {
410
+ reject(error);
411
+ return;
412
+ }
413
+ if (actualLength <= 0) {
414
+ reject(new Error('Empty USB transfer'));
415
+ return;
416
+ }
417
+ resolve(buffer.subarray(0, actualLength));
418
+ }
419
+ );
420
+ try {
421
+ transfer.submit(buffer);
422
+ } catch (error) {
423
+ const active = this.activeTransfers.get(transfer);
424
+ this.activeTransfers.delete(transfer);
425
+ active?.resolveSettled();
426
+ reject(error);
427
+ }
428
+ });
429
+ }
430
+
431
+ private transferOutOnce(path: string, ep: usb.OutEndpoint, data: Buffer): Promise<void> {
432
+ return new Promise((resolve, reject) => {
433
+ const transfer = this.createTrackedTransfer(path, ep, error => {
434
+ if (error) {
435
+ reject(error);
436
+ return;
437
+ }
438
+ resolve();
439
+ });
440
+ try {
441
+ transfer.submit(data);
442
+ } catch (error) {
443
+ const active = this.activeTransfers.get(transfer);
444
+ this.activeTransfers.delete(transfer);
445
+ active?.resolveSettled();
446
+ reject(error);
447
+ }
448
+ });
449
+ }
450
+
352
451
  private async closeOpenDevice(path: string): Promise<void> {
353
452
  const openDev = this.openDevices.get(path);
354
453
  if (!openDev) return;
@@ -401,20 +500,6 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
401
500
  `Device protocol has not been detected for ${path}`
402
501
  );
403
502
  }
404
- if (LogBlockCommand.has(name)) {
405
- this.Log?.debug('NodeUsbTransport call-', ' name: ', name, ' protocol: ', protocol);
406
- } else {
407
- this.Log?.debug(
408
- 'NodeUsbTransport call-',
409
- ' name: ',
410
- name,
411
- ' data: ',
412
- data,
413
- ' protocol: ',
414
- protocol
415
- );
416
- }
417
-
418
503
  if (protocol === 'V2') {
419
504
  return this.callProtocolV2(path, name, data, options);
420
505
  }
@@ -449,7 +534,6 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
449
534
  }
450
535
 
451
536
  cancel() {
452
- this.Log?.debug('NodeUsbTransport cancel');
453
537
  this.cancelled = true;
454
538
  }
455
539
 
@@ -479,6 +563,12 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
479
563
 
480
564
  private isRetryableError(error: unknown): boolean {
481
565
  const message = this.getErrorMessage(error).toLowerCase();
566
+ // cancelActiveTransfers() terminates timed-out or releasing native requests.
567
+ // LIBUSB_TRANSFER_CANCELLED is not transient. Retrying would start another pending
568
+ // read on the rebuilt interface and race protocol probing against release.
569
+ if (message.includes('cancelled') || message.includes('canceled')) {
570
+ return false;
571
+ }
482
572
  return (
483
573
  message.includes('libusb') ||
484
574
  message.includes('transfer') ||
@@ -583,7 +673,7 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
583
673
  const packet = new Uint8Array(PACKET_SIZE);
584
674
  packet[0] = REPORT_ID;
585
675
  packet.set(new Uint8Array(buffer), 1);
586
- await transferOutOnce(this.getOpenDevice(path).epOut, Buffer.from(packet));
676
+ await this.transferOutOnce(path, this.getOpenDevice(path).epOut, Buffer.from(packet));
587
677
  }
588
678
  return; // all chunks sent successfully
589
679
  } catch (error) {
@@ -626,7 +716,7 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
626
716
  throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
627
717
  }
628
718
  try {
629
- return await transferInOnce(currentDev.epIn, length);
719
+ return await this.transferInOnce(path, currentDev.epIn, length);
630
720
  } catch (error) {
631
721
  lastError = error;
632
722
  if (options?.waitIndefinitelyOnTimeout && this.isUsbTransferTimeout(error)) {
@@ -706,7 +796,7 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
706
796
  epIn.timeout = TRANSFER_TIMEOUT_MS;
707
797
  epOut.timeout = TRANSFER_TIMEOUT_MS;
708
798
 
709
- await this.drainStaleInput(epIn);
799
+ await this.drainStaleInput(path, epIn);
710
800
 
711
801
  this.openDevices.set(path, { device: dev, iface, epIn, epOut });
712
802
  } catch (err) {
@@ -719,14 +809,14 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
719
809
  }
720
810
  }
721
811
 
722
- private async drainStaleInput(epIn: usb.InEndpoint): Promise<void> {
812
+ private async drainStaleInput(path: string, epIn: usb.InEndpoint): Promise<void> {
723
813
  const originalTimeout = epIn.timeout;
724
814
  epIn.timeout = 50;
725
815
  try {
726
816
  // Drain a small bounded number of packets left by the previous USB session.
727
817
  for (let index = 0; index < 16; index += 1) {
728
818
  try {
729
- await transferInOnce(epIn, PACKET_SIZE);
819
+ await this.transferInOnce(path, epIn, PACKET_SIZE);
730
820
  } catch {
731
821
  break;
732
822
  }
@@ -752,17 +842,9 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
752
842
 
753
843
  private async detectProtocol(
754
844
  path: string,
755
- expectedProtocol?: ProtocolType
845
+ expectedProtocol?: ProtocolType,
846
+ protocolHint?: ProtocolType
756
847
  ): Promise<ProtocolType> {
757
- if (expectedProtocol === 'V1') {
758
- if (await this.probeProtocolV1(path)) {
759
- this.deviceProtocol.set(path, 'V1');
760
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1 (expected)`);
761
- return 'V1';
762
- }
763
- throw this.createProtocolMismatchError(expectedProtocol);
764
- }
765
-
766
848
  if (expectedProtocol === 'V2') {
767
849
  if (await this.probeProtocolV2(path)) {
768
850
  this.deviceProtocol.set(path, 'V2');
@@ -772,22 +854,27 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
772
854
  throw this.createProtocolMismatchError(expectedProtocol);
773
855
  }
774
856
 
775
- if (this.deviceProtocol.get(path) === 'V2' && (await this.probeProtocolV2(path))) {
776
- this.deviceProtocol.set(path, 'V2');
777
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2 (cached)`);
778
- return 'V2';
857
+ if (expectedProtocol === 'V1') {
858
+ if (await this.probeProtocolV1(path)) {
859
+ this.deviceProtocol.set(path, 'V1');
860
+ return 'V1';
861
+ }
862
+ throw this.createProtocolMismatchError(expectedProtocol);
779
863
  }
780
864
 
781
- if (await this.probeProtocolV1(path)) {
782
- this.deviceProtocol.set(path, 'V1');
783
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V1`);
784
- return 'V1';
785
- }
865
+ const probeOrder: ProtocolType[] =
866
+ protocolHint === 'V2' || this.deviceProtocol.get(path) === 'V2' ? ['V2', 'V1'] : ['V1', 'V2'];
786
867
 
787
- if (await this.probeProtocolV2(path)) {
788
- this.deviceProtocol.set(path, 'V2');
789
- this.Log?.debug(`[NodeUsbTransport] detectProtocol: path=${path} -> V2`);
790
- return 'V2';
868
+ for (const [index, protocol] of probeOrder.entries()) {
869
+ if (index > 0) {
870
+ await this.resetConnectionAfterProbe(path);
871
+ }
872
+ const detected =
873
+ protocol === 'V1' ? await this.probeProtocolV1(path) : await this.probeProtocolV2(path);
874
+ if (detected) {
875
+ this.deviceProtocol.set(path, protocol);
876
+ return protocol;
877
+ }
791
878
  }
792
879
 
793
880
  this.deviceProtocol.delete(path);
@@ -798,6 +885,10 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
798
885
  await this.rotateProtocolV2UsbGeneration(path, 'Node USB protocol probe reset');
799
886
 
800
887
  try {
888
+ // A timed-out probe may leave an IN transfer pending. Cancel and await it before
889
+ // closing the interface, or libusb may never callback and release()/stop() will
890
+ // wait forever, surfacing as Polling timeout (809).
891
+ await this.cancelActiveTransfers(path);
801
892
  await this.closeOpenDevice(path);
802
893
  } catch (error) {
803
894
  this.Log?.debug('[NodeUsbTransport] close after protocol probe error:', error);
@@ -857,8 +948,7 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
857
948
  try {
858
949
  await this.callProtocolV1(path, 'Initialize', {}, { timeoutMs: PROTOCOL_PROBE_TIMEOUT });
859
950
  return true;
860
- } catch (error) {
861
- this.Log?.debug('[NodeUsbTransport] Protocol V1 Initialize probe failed:', error);
951
+ } catch (_error) {
862
952
  return false;
863
953
  }
864
954
  }
@@ -899,7 +989,7 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
899
989
  if (this.cancelled) {
900
990
  throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
901
991
  }
902
- await transferOutOnce(this.getOpenDevice(path).epOut, Buffer.from(frame));
992
+ await this.transferOutOnce(path, this.getOpenDevice(path).epOut, Buffer.from(frame));
903
993
  }
904
994
 
905
995
  protected async readProtocolV2UsbPacket(
@@ -911,7 +1001,8 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
911
1001
  throw ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromOutside, 'Cancelled');
912
1002
  }
913
1003
  try {
914
- const packet = await transferInOnce(
1004
+ const packet = await this.transferInOnce(
1005
+ path,
915
1006
  this.getOpenDevice(path).epIn,
916
1007
  PROTOCOL_V2_FRAME_MAX_BYTES
917
1008
  );
@@ -927,6 +1018,7 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
927
1018
  }
928
1019
 
929
1020
  protected async resetProtocolV2UsbNativeLink(path: string, _reason: string): Promise<void> {
1021
+ await this.cancelActiveTransfers(path);
930
1022
  await this.closeOpenDevice(path);
931
1023
  }
932
1024
 
@@ -936,7 +1028,10 @@ export default class NodeUsbTransport extends ProtocolV2UsbTransportBase<string>
936
1028
  }
937
1029
 
938
1030
  protected createProtocolV2UsbTimeoutError(name: string, timeoutMs: number): Error {
939
- return new Error(`Protocol V2 response timeout after ${timeoutMs}ms for ${name}`);
1031
+ return new ProtocolV2LinkError(
1032
+ 'response-timeout',
1033
+ `Protocol V2 response timeout after ${timeoutMs}ms for ${name}`
1034
+ );
940
1035
  }
941
1036
 
942
1037
  private async callProtocolV2(
@@ -0,0 +1 @@
1
+ export { createTransportCallLog, shouldSuppressHighVolumeCallLog } from '@onekeyfe/hd-transport';