@onekeyfe/hd-transport-usb 1.1.26-alpha.0 → 1.1.26-alpha.100
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/dist/constants.d.ts +0 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.d.ts +3 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -214
- package/package.json +4 -4
- package/src/constants.ts +0 -3
- package/src/index.ts +117 -284
package/dist/constants.d.ts
CHANGED
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,WAAW,KAAK,CAAC;AAG9B,eAAO,MAAM,SAAS,KAAO,CAAC;AAG9B,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,WAAW,KAAK,CAAC;AAG9B,eAAO,MAAM,SAAS,KAAO,CAAC;AAG9B,eAAO,MAAM,aAAa,IAAI,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,27 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import transport__default, { OneKeyDeviceInfo, AcquireInput } from '@onekeyfe/hd-transport';
|
|
3
|
-
import EventEmitter from 'events';
|
|
1
|
+
import { LowlevelTransportSharedPlugin } from '@onekeyfe/hd-transport';
|
|
4
2
|
|
|
5
3
|
declare const PACKET_SIZE = 64;
|
|
6
4
|
|
|
7
|
-
declare
|
|
8
|
-
messages: ReturnType<typeof transport__default.parseConfigure> | undefined;
|
|
9
|
-
name: string;
|
|
10
|
-
configured: boolean;
|
|
11
|
-
Log?: any;
|
|
12
|
-
emitter?: EventEmitter;
|
|
13
|
-
private serialToBusId;
|
|
14
|
-
private openDevices;
|
|
15
|
-
init(logger: any, emitter?: EventEmitter): void;
|
|
16
|
-
configure(signedData: any): void;
|
|
17
|
-
listen(): void;
|
|
18
|
-
enumerate(): Promise<OneKeyDeviceInfo[]>;
|
|
19
|
-
acquire(input: AcquireInput): Promise<string>;
|
|
20
|
-
release(path: string, _onclose?: boolean): Promise<void>;
|
|
21
|
-
call(path: string, name: string, data: Record<string, unknown>): Promise<transport.MessageFromOneKey>;
|
|
22
|
-
cancel(): void;
|
|
23
|
-
private openDevice;
|
|
24
|
-
private receiveData;
|
|
25
|
-
}
|
|
5
|
+
declare const UsbPlugin: LowlevelTransportSharedPlugin;
|
|
26
6
|
|
|
27
|
-
export { PACKET_SIZE,
|
|
7
|
+
export { PACKET_SIZE, UsbPlugin, UsbPlugin as default };
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAkB,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AAoG5F,eAAO,MAAM,SAAS,EAAE,6BA+JvB,CAAC;AAEF,eAAe,SAAS,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -63,59 +63,19 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
63
63
|
|
|
64
64
|
const PACKET_SIZE = 64;
|
|
65
65
|
const REPORT_ID = 0x3f;
|
|
66
|
-
const PAYLOAD_SIZE = PACKET_SIZE - 1;
|
|
67
66
|
const HEADER_LENGTH = 6;
|
|
68
67
|
|
|
69
|
-
const {
|
|
68
|
+
const { decodeProtocol } = transport__default["default"];
|
|
70
69
|
const INTERFACE_NUMBER = 0;
|
|
71
70
|
const ENDPOINT_IN = 0x81;
|
|
72
71
|
const ENDPOINT_OUT = 0x01;
|
|
73
72
|
const TRANSFER_TIMEOUT_MS = 30000;
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
function
|
|
73
|
+
let activeDevice = null;
|
|
74
|
+
const openDevices = new Map();
|
|
75
|
+
function getDeviceId(dev) {
|
|
77
76
|
return `usb:${dev.busNumber}:${dev.deviceAddress}`;
|
|
78
77
|
}
|
|
79
|
-
function
|
|
80
|
-
const { iSerialNumber } = dev.deviceDescriptor;
|
|
81
|
-
if (!iSerialNumber)
|
|
82
|
-
return Promise.resolve(getBusId(dev));
|
|
83
|
-
const busId = getBusId(dev);
|
|
84
|
-
if (openDevices) {
|
|
85
|
-
for (const [serial, od] of openDevices) {
|
|
86
|
-
if (od.device === dev || getBusId(od.device) === busId) {
|
|
87
|
-
return Promise.resolve(serial);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return new Promise(resolve => {
|
|
92
|
-
try {
|
|
93
|
-
dev.open();
|
|
94
|
-
try {
|
|
95
|
-
dev.getStringDescriptor(iSerialNumber, (_err, data) => {
|
|
96
|
-
try {
|
|
97
|
-
dev.close();
|
|
98
|
-
}
|
|
99
|
-
catch (_a) {
|
|
100
|
-
}
|
|
101
|
-
resolve(data || busId);
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
catch (_a) {
|
|
105
|
-
try {
|
|
106
|
-
dev.close();
|
|
107
|
-
}
|
|
108
|
-
catch (_b) {
|
|
109
|
-
}
|
|
110
|
-
resolve(busId);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
catch (_c) {
|
|
114
|
-
resolve(busId);
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
function transferInOnce(ep, length) {
|
|
78
|
+
function transferIn(ep, length) {
|
|
119
79
|
return new Promise((resolve, reject) => {
|
|
120
80
|
ep.transfer(length, (err, data) => {
|
|
121
81
|
if (err)
|
|
@@ -126,7 +86,7 @@ function transferInOnce(ep, length) {
|
|
|
126
86
|
});
|
|
127
87
|
});
|
|
128
88
|
}
|
|
129
|
-
function
|
|
89
|
+
function transferOut(ep, data) {
|
|
130
90
|
return new Promise((resolve, reject) => {
|
|
131
91
|
ep.transfer(data, (err) => {
|
|
132
92
|
if (err)
|
|
@@ -135,43 +95,9 @@ function transferOutOnce(ep, data) {
|
|
|
135
95
|
});
|
|
136
96
|
});
|
|
137
97
|
}
|
|
138
|
-
function
|
|
139
|
-
return new Promise(resolve => {
|
|
140
|
-
setTimeout(resolve, ms);
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
function transferIn(ep, length) {
|
|
98
|
+
function readPacket(dev) {
|
|
144
99
|
return __awaiter(this, void 0, void 0, function* () {
|
|
145
|
-
|
|
146
|
-
for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt++) {
|
|
147
|
-
try {
|
|
148
|
-
return yield transferInOnce(ep, length);
|
|
149
|
-
}
|
|
150
|
-
catch (err) {
|
|
151
|
-
lastError = err;
|
|
152
|
-
if (attempt < PACKET_IO_MAX_RETRIES) {
|
|
153
|
-
yield wait(attempt * PACKET_IO_RETRY_DELAY);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
throw lastError;
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
function transferOut(ep, data) {
|
|
161
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
162
|
-
let lastError;
|
|
163
|
-
for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt++) {
|
|
164
|
-
try {
|
|
165
|
-
return yield transferOutOnce(ep, data);
|
|
166
|
-
}
|
|
167
|
-
catch (err) {
|
|
168
|
-
lastError = err;
|
|
169
|
-
if (attempt < PACKET_IO_MAX_RETRIES) {
|
|
170
|
-
yield wait(attempt * PACKET_IO_RETRY_DELAY);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
throw lastError;
|
|
100
|
+
return transferIn(dev.epIn, PACKET_SIZE);
|
|
175
101
|
});
|
|
176
102
|
}
|
|
177
103
|
function skipReportByte(packet) {
|
|
@@ -183,24 +109,12 @@ function skipReportByte(packet) {
|
|
|
183
109
|
function toArrayBuffer(buf) {
|
|
184
110
|
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
185
111
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
this
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
init(logger, emitter) {
|
|
194
|
-
this.Log = logger;
|
|
195
|
-
this.emitter = emitter;
|
|
196
|
-
}
|
|
197
|
-
configure(signedData) {
|
|
198
|
-
const messages = parseConfigure(signedData);
|
|
199
|
-
this.configured = true;
|
|
200
|
-
this.messages = messages;
|
|
201
|
-
}
|
|
202
|
-
listen() {
|
|
203
|
-
}
|
|
112
|
+
const UsbPlugin = {
|
|
113
|
+
version: '1.0.0',
|
|
114
|
+
init() {
|
|
115
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
});
|
|
117
|
+
},
|
|
204
118
|
enumerate() {
|
|
205
119
|
return __awaiter(this, void 0, void 0, function* () {
|
|
206
120
|
const allDevices = usb__namespace.getDeviceList();
|
|
@@ -208,115 +122,24 @@ class NodeUsbTransport {
|
|
|
208
122
|
const { idVendor, idProduct } = d.deviceDescriptor;
|
|
209
123
|
return hdShared.ONEKEY_WEBUSB_FILTER.some(f => idVendor === f.vendorId && idProduct === f.productId);
|
|
210
124
|
});
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
newSerialToBusId.set(serial, busId);
|
|
217
|
-
results.push({
|
|
218
|
-
path: serial,
|
|
219
|
-
id: serial,
|
|
220
|
-
name: 'OneKey',
|
|
221
|
-
commType: 'usb',
|
|
222
|
-
debug: false,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
this.serialToBusId = newSerialToBusId;
|
|
226
|
-
return results;
|
|
125
|
+
return onekeyDevices.map(d => ({
|
|
126
|
+
id: getDeviceId(d),
|
|
127
|
+
name: 'OneKey',
|
|
128
|
+
commType: 'usb',
|
|
129
|
+
}));
|
|
227
130
|
});
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
var _a, _b, _c;
|
|
231
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
232
|
-
const path = (_a = input.path) !== null && _a !== void 0 ? _a : '';
|
|
233
|
-
if (!path) {
|
|
234
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, 'No device path provided');
|
|
235
|
-
}
|
|
236
|
-
try {
|
|
237
|
-
yield this.openDevice(path);
|
|
238
|
-
return path;
|
|
239
|
-
}
|
|
240
|
-
catch (error) {
|
|
241
|
-
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('NodeUsbTransport acquire error: ', error);
|
|
242
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, (_c = error.message) !== null && _c !== void 0 ? _c : String(error));
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
release(path, _onclose) {
|
|
131
|
+
},
|
|
132
|
+
connect(uuid) {
|
|
247
133
|
return __awaiter(this, void 0, void 0, function* () {
|
|
248
|
-
const
|
|
249
|
-
if (
|
|
134
|
+
const existing = openDevices.get(uuid);
|
|
135
|
+
if (existing) {
|
|
136
|
+
activeDevice = existing;
|
|
250
137
|
return;
|
|
251
|
-
try {
|
|
252
|
-
yield new Promise(resolve => {
|
|
253
|
-
openDev.iface.release(() => {
|
|
254
|
-
try {
|
|
255
|
-
openDev.device.close();
|
|
256
|
-
}
|
|
257
|
-
catch (_a) {
|
|
258
|
-
}
|
|
259
|
-
resolve();
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
catch (_a) {
|
|
264
|
-
try {
|
|
265
|
-
openDev.device.close();
|
|
266
|
-
}
|
|
267
|
-
catch (_b) {
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
this.openDevices.delete(path);
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
call(path, name, data) {
|
|
274
|
-
var _a, _b;
|
|
275
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
276
|
-
if (!this.messages) {
|
|
277
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
278
|
-
}
|
|
279
|
-
const openDev = this.openDevices.get(path);
|
|
280
|
-
if (!openDev) {
|
|
281
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceNotFound, `Device not acquired: ${path}`);
|
|
282
|
-
}
|
|
283
|
-
const { messages } = this;
|
|
284
|
-
if (transport.LogBlockCommand.has(name)) {
|
|
285
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('NodeUsbTransport call-', ' name: ', name);
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
(_b = this.Log) === null || _b === void 0 ? void 0 : _b.debug('NodeUsbTransport call-', ' name: ', name, ' data: ', data);
|
|
289
138
|
}
|
|
290
|
-
const encodeBuffers = buildEncodeBuffers(messages, name, data);
|
|
291
|
-
for (const buffer of encodeBuffers) {
|
|
292
|
-
const packet = new Uint8Array(PACKET_SIZE);
|
|
293
|
-
packet[0] = REPORT_ID;
|
|
294
|
-
packet.set(new Uint8Array(buffer), 1);
|
|
295
|
-
yield transferOut(openDev.epOut, Buffer.from(packet));
|
|
296
|
-
}
|
|
297
|
-
const resData = yield this.receiveData(openDev);
|
|
298
|
-
if (typeof resData !== 'string') {
|
|
299
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
300
|
-
}
|
|
301
|
-
const jsonData = receiveOne(messages, resData);
|
|
302
|
-
return check.call(jsonData);
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
cancel() {
|
|
306
|
-
var _a;
|
|
307
|
-
(_a = this.Log) === null || _a === void 0 ? void 0 : _a.debug('NodeUsbTransport cancel');
|
|
308
|
-
}
|
|
309
|
-
openDevice(path) {
|
|
310
|
-
var _a;
|
|
311
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
312
|
-
const existing = this.openDevices.get(path);
|
|
313
|
-
if (existing)
|
|
314
|
-
return;
|
|
315
|
-
const busId = (_a = this.serialToBusId.get(path)) !== null && _a !== void 0 ? _a : path;
|
|
316
139
|
const allDevices = usb__namespace.getDeviceList();
|
|
317
|
-
const dev = allDevices.find(d =>
|
|
140
|
+
const dev = allDevices.find(d => getDeviceId(d) === uuid);
|
|
318
141
|
if (!dev) {
|
|
319
|
-
throw
|
|
142
|
+
throw new Error(`USB device not found: ${uuid}`);
|
|
320
143
|
}
|
|
321
144
|
dev.open();
|
|
322
145
|
dev.timeout = TRANSFER_TIMEOUT_MS;
|
|
@@ -327,7 +150,7 @@ class NodeUsbTransport {
|
|
|
327
150
|
iface.detachKernelDriver();
|
|
328
151
|
}
|
|
329
152
|
}
|
|
330
|
-
catch (
|
|
153
|
+
catch (_a) {
|
|
331
154
|
}
|
|
332
155
|
}
|
|
333
156
|
iface.claim();
|
|
@@ -335,16 +158,60 @@ class NodeUsbTransport {
|
|
|
335
158
|
const epOut = iface.endpoints.find((e) => e.direction === 'out' && e.address === ENDPOINT_OUT);
|
|
336
159
|
if (!epIn || !epOut) {
|
|
337
160
|
dev.close();
|
|
338
|
-
throw
|
|
161
|
+
throw new Error('USB endpoints not found (expected IN 0x81, OUT 0x01)');
|
|
339
162
|
}
|
|
340
163
|
epIn.timeout = TRANSFER_TIMEOUT_MS;
|
|
341
164
|
epOut.timeout = TRANSFER_TIMEOUT_MS;
|
|
342
|
-
|
|
165
|
+
const openDev = { device: dev, iface, epIn, epOut };
|
|
166
|
+
openDevices.set(uuid, openDev);
|
|
167
|
+
activeDevice = openDev;
|
|
343
168
|
});
|
|
344
|
-
}
|
|
345
|
-
|
|
169
|
+
},
|
|
170
|
+
disconnect(uuid) {
|
|
171
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
172
|
+
const openDev = openDevices.get(uuid);
|
|
173
|
+
if (openDev) {
|
|
174
|
+
try {
|
|
175
|
+
openDev.iface.release(() => {
|
|
176
|
+
try {
|
|
177
|
+
openDev.device.close();
|
|
178
|
+
}
|
|
179
|
+
catch (_a) {
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
catch (_a) {
|
|
184
|
+
try {
|
|
185
|
+
openDev.device.close();
|
|
186
|
+
}
|
|
187
|
+
catch (_b) {
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
openDevices.delete(uuid);
|
|
191
|
+
if (activeDevice === openDev) {
|
|
192
|
+
activeDevice = null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
},
|
|
197
|
+
send(uuid, data) {
|
|
198
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
199
|
+
const openDev = openDevices.get(uuid);
|
|
200
|
+
if (!openDev) {
|
|
201
|
+
throw new Error(`Device not connected: ${uuid}`);
|
|
202
|
+
}
|
|
203
|
+
activeDevice = openDev;
|
|
204
|
+
const dataBuffer = Buffer.from(data, 'hex');
|
|
205
|
+
yield transferOut(openDev.epOut, dataBuffer);
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
receive() {
|
|
346
209
|
return __awaiter(this, void 0, void 0, function* () {
|
|
347
|
-
|
|
210
|
+
if (!activeDevice) {
|
|
211
|
+
throw new Error('No active device for receive');
|
|
212
|
+
}
|
|
213
|
+
const dev = activeDevice;
|
|
214
|
+
const firstPacket = yield readPacket(dev);
|
|
348
215
|
const firstData = skipReportByte(firstPacket);
|
|
349
216
|
const { length, typeId, restBuffer } = decodeProtocol.decodeChunked(toArrayBuffer(firstData));
|
|
350
217
|
const lengthWithHeader = Number(length) + HEADER_LENGTH;
|
|
@@ -355,10 +222,10 @@ class NodeUsbTransport {
|
|
|
355
222
|
decoded.append(restBuffer);
|
|
356
223
|
}
|
|
357
224
|
while (decoded.offset < lengthWithHeader) {
|
|
358
|
-
const packet = yield
|
|
225
|
+
const packet = yield readPacket(dev);
|
|
359
226
|
const pktData = skipReportByte(packet);
|
|
360
227
|
const buf = toArrayBuffer(pktData);
|
|
361
|
-
if (lengthWithHeader - decoded.offset >=
|
|
228
|
+
if (lengthWithHeader - decoded.offset >= PACKET_SIZE) {
|
|
362
229
|
decoded.append(buf);
|
|
363
230
|
}
|
|
364
231
|
else {
|
|
@@ -369,8 +236,9 @@ class NodeUsbTransport {
|
|
|
369
236
|
const result = decoded.toBuffer();
|
|
370
237
|
return Buffer.from(result).toString('hex');
|
|
371
238
|
});
|
|
372
|
-
}
|
|
373
|
-
}
|
|
239
|
+
},
|
|
240
|
+
};
|
|
374
241
|
|
|
375
242
|
exports.PACKET_SIZE = PACKET_SIZE;
|
|
376
|
-
exports
|
|
243
|
+
exports.UsbPlugin = UsbPlugin;
|
|
244
|
+
exports["default"] = UsbPlugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-transport-usb",
|
|
3
|
-
"version": "1.1.26-alpha.
|
|
3
|
+
"version": "1.1.26-alpha.100",
|
|
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",
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
"lint:fix": "eslint . --fix"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@onekeyfe/hd-shared": "1.1.26-alpha.
|
|
24
|
-
"@onekeyfe/hd-transport": "1.1.26-alpha.
|
|
23
|
+
"@onekeyfe/hd-shared": "1.1.26-alpha.100",
|
|
24
|
+
"@onekeyfe/hd-transport": "1.1.26-alpha.100",
|
|
25
25
|
"bytebuffer": "^5.0.1",
|
|
26
26
|
"usb": "^2.14.0"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "02e9376cb38df57b5743b674326c742fa74d1e8b"
|
|
29
29
|
}
|
package/src/constants.ts
CHANGED
|
@@ -4,8 +4,5 @@ export const PACKET_SIZE = 64;
|
|
|
4
4
|
/** Protocol marker byte (0x3F = '?') — first byte of every packet */
|
|
5
5
|
export const REPORT_ID = 0x3f;
|
|
6
6
|
|
|
7
|
-
/** Usable payload per packet after stripping the 0x3F report byte */
|
|
8
|
-
export const PAYLOAD_SIZE = PACKET_SIZE - 1;
|
|
9
|
-
|
|
10
7
|
/** Protocol header length: typeId (2 bytes) + length (4 bytes) */
|
|
11
8
|
export const HEADER_LENGTH = 6;
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import ByteBuffer from 'bytebuffer';
|
|
2
2
|
import * as usb from 'usb';
|
|
3
|
-
import transport
|
|
4
|
-
import {
|
|
3
|
+
import transport from '@onekeyfe/hd-transport';
|
|
4
|
+
import { ONEKEY_WEBUSB_FILTER } from '@onekeyfe/hd-shared';
|
|
5
5
|
|
|
6
|
-
import { HEADER_LENGTH, PACKET_SIZE,
|
|
6
|
+
import { HEADER_LENGTH, PACKET_SIZE, REPORT_ID } from './constants';
|
|
7
7
|
|
|
8
|
-
import type
|
|
9
|
-
import type { AcquireInput, OneKeyDeviceInfo } from '@onekeyfe/hd-transport';
|
|
8
|
+
import type { LowLevelDevice, LowlevelTransportSharedPlugin } from '@onekeyfe/hd-transport';
|
|
10
9
|
|
|
11
|
-
const {
|
|
10
|
+
const { decodeProtocol } = transport;
|
|
12
11
|
|
|
13
12
|
/** USB interface number for vendor-specific communication */
|
|
14
13
|
const INTERFACE_NUMBER = 0;
|
|
@@ -19,10 +18,6 @@ const ENDPOINT_OUT = 0x01;
|
|
|
19
18
|
/** Transfer timeout in milliseconds */
|
|
20
19
|
const TRANSFER_TIMEOUT_MS = 30000;
|
|
21
20
|
|
|
22
|
-
/** Packet I/O retry configuration (matches WebUsbTransport) */
|
|
23
|
-
const PACKET_IO_MAX_RETRIES = 3;
|
|
24
|
-
const PACKET_IO_RETRY_DELAY = 300;
|
|
25
|
-
|
|
26
21
|
/**
|
|
27
22
|
* Opened device state — holds the USB device, claimed interface, and endpoints.
|
|
28
23
|
*/
|
|
@@ -34,62 +29,26 @@ interface OpenDevice {
|
|
|
34
29
|
}
|
|
35
30
|
|
|
36
31
|
/**
|
|
37
|
-
*
|
|
32
|
+
* The currently active device — used by receive() since
|
|
33
|
+
* LowlevelTransportSharedPlugin.receive() takes no uuid parameter.
|
|
38
34
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
let activeDevice: OpenDevice | null = null;
|
|
36
|
+
|
|
37
|
+
/** Map of uuid → open device state */
|
|
38
|
+
const openDevices = new Map<string, OpenDevice>();
|
|
42
39
|
|
|
43
40
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* Falls back to bus path if serial cannot be read.
|
|
41
|
+
* Build a unique identifier for a USB device.
|
|
42
|
+
* Uses bus number + device address which is stable within a session.
|
|
47
43
|
*/
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
if (!iSerialNumber) return Promise.resolve(getBusId(dev));
|
|
51
|
-
|
|
52
|
-
// If the device is already open (acquired), read serial without open/close
|
|
53
|
-
const busId = getBusId(dev);
|
|
54
|
-
if (openDevices) {
|
|
55
|
-
for (const [serial, od] of openDevices) {
|
|
56
|
-
if (od.device === dev || getBusId(od.device) === busId) {
|
|
57
|
-
return Promise.resolve(serial);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return new Promise(resolve => {
|
|
63
|
-
try {
|
|
64
|
-
dev.open();
|
|
65
|
-
try {
|
|
66
|
-
dev.getStringDescriptor(iSerialNumber, (_err: Error | undefined, data?: string) => {
|
|
67
|
-
try {
|
|
68
|
-
dev.close();
|
|
69
|
-
} catch {
|
|
70
|
-
/* ignore */
|
|
71
|
-
}
|
|
72
|
-
resolve(data || busId);
|
|
73
|
-
});
|
|
74
|
-
} catch {
|
|
75
|
-
try {
|
|
76
|
-
dev.close();
|
|
77
|
-
} catch {
|
|
78
|
-
/* ignore */
|
|
79
|
-
}
|
|
80
|
-
resolve(busId);
|
|
81
|
-
}
|
|
82
|
-
} catch {
|
|
83
|
-
// dev.open() failed (e.g. LIBUSB_ERROR_BUSY if already open elsewhere)
|
|
84
|
-
resolve(busId);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
44
|
+
function getDeviceId(dev: usb.Device): string {
|
|
45
|
+
return `usb:${dev.busNumber}:${dev.deviceAddress}`;
|
|
87
46
|
}
|
|
88
47
|
|
|
89
48
|
/**
|
|
90
|
-
* Promisified USB IN transfer
|
|
49
|
+
* Promisified USB IN transfer.
|
|
91
50
|
*/
|
|
92
|
-
function
|
|
51
|
+
function transferIn(ep: usb.InEndpoint, length: number): Promise<Buffer> {
|
|
93
52
|
return new Promise((resolve, reject) => {
|
|
94
53
|
ep.transfer(length, (err: Error | undefined, data: Buffer | undefined) => {
|
|
95
54
|
if (err) return reject(err);
|
|
@@ -100,9 +59,9 @@ function transferInOnce(ep: usb.InEndpoint, length: number): Promise<Buffer> {
|
|
|
100
59
|
}
|
|
101
60
|
|
|
102
61
|
/**
|
|
103
|
-
* Promisified USB OUT transfer
|
|
62
|
+
* Promisified USB OUT transfer.
|
|
104
63
|
*/
|
|
105
|
-
function
|
|
64
|
+
function transferOut(ep: usb.OutEndpoint, data: Buffer): Promise<void> {
|
|
106
65
|
return new Promise((resolve, reject) => {
|
|
107
66
|
ep.transfer(data, (err: Error | undefined) => {
|
|
108
67
|
if (err) return reject(err);
|
|
@@ -111,50 +70,16 @@ function transferOutOnce(ep: usb.OutEndpoint, data: Buffer): Promise<void> {
|
|
|
111
70
|
});
|
|
112
71
|
}
|
|
113
72
|
|
|
114
|
-
function wait(ms: number): Promise<void> {
|
|
115
|
-
return new Promise(resolve => {
|
|
116
|
-
setTimeout(resolve, ms);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
73
|
/**
|
|
121
|
-
*
|
|
74
|
+
* Read a single 64-byte packet from the device via libusb bulk/interrupt transfer.
|
|
122
75
|
*/
|
|
123
|
-
async function
|
|
124
|
-
|
|
125
|
-
for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt++) {
|
|
126
|
-
try {
|
|
127
|
-
return await transferInOnce(ep, length);
|
|
128
|
-
} catch (err) {
|
|
129
|
-
lastError = err;
|
|
130
|
-
if (attempt < PACKET_IO_MAX_RETRIES) {
|
|
131
|
-
await wait(attempt * PACKET_IO_RETRY_DELAY);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
throw lastError;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* USB OUT transfer with retry.
|
|
140
|
-
*/
|
|
141
|
-
async function transferOut(ep: usb.OutEndpoint, data: Buffer): Promise<void> {
|
|
142
|
-
let lastError: unknown;
|
|
143
|
-
for (let attempt = 1; attempt <= PACKET_IO_MAX_RETRIES; attempt++) {
|
|
144
|
-
try {
|
|
145
|
-
return await transferOutOnce(ep, data);
|
|
146
|
-
} catch (err) {
|
|
147
|
-
lastError = err;
|
|
148
|
-
if (attempt < PACKET_IO_MAX_RETRIES) {
|
|
149
|
-
await wait(attempt * PACKET_IO_RETRY_DELAY);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
throw lastError;
|
|
76
|
+
async function readPacket(dev: OpenDevice): Promise<Buffer> {
|
|
77
|
+
return transferIn(dev.epIn, PACKET_SIZE);
|
|
154
78
|
}
|
|
155
79
|
|
|
156
80
|
/**
|
|
157
81
|
* Skip the 0x3F protocol marker byte from a USB packet.
|
|
82
|
+
* The device sends 0x3F as the first byte of every response packet.
|
|
158
83
|
*/
|
|
159
84
|
function skipReportByte(packet: Buffer): Buffer {
|
|
160
85
|
if (packet[0] === REPORT_ID) {
|
|
@@ -171,56 +96,24 @@ function toArrayBuffer(buf: Buffer): ArrayBuffer {
|
|
|
171
96
|
}
|
|
172
97
|
|
|
173
98
|
/**
|
|
174
|
-
* Node.js USB
|
|
99
|
+
* Node.js USB plugin for LowlevelTransport.
|
|
175
100
|
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
101
|
+
* Implements the 6-method LowlevelTransportSharedPlugin interface
|
|
102
|
+
* using the `usb` (libusb) library for direct USB communication
|
|
103
|
+
* with OneKey hardware wallets.
|
|
179
104
|
*
|
|
180
|
-
*
|
|
105
|
+
* Uses libusb to access the vendor-specific interface (class 255)
|
|
106
|
+
* which is NOT visible to the HID framework on macOS.
|
|
181
107
|
*/
|
|
182
|
-
export
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
name = 'NodeUsbTransport';
|
|
186
|
-
|
|
187
|
-
configured = false;
|
|
188
|
-
|
|
189
|
-
Log?: any;
|
|
190
|
-
|
|
191
|
-
emitter?: EventEmitter;
|
|
192
|
-
|
|
193
|
-
/** serial → bus id, built during enumerate */
|
|
194
|
-
private serialToBusId = new Map<string, string>();
|
|
195
|
-
|
|
196
|
-
/** path → opened device state */
|
|
197
|
-
private openDevices = new Map<string, OpenDevice>();
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Initialize transport.
|
|
201
|
-
* Signature matches the Transport.init interface (logger, emitter).
|
|
202
|
-
*/
|
|
203
|
-
init(logger: any, emitter?: EventEmitter) {
|
|
204
|
-
this.Log = logger;
|
|
205
|
-
this.emitter = emitter;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
configure(signedData: any) {
|
|
209
|
-
const messages = parseConfigure(signedData);
|
|
210
|
-
this.configured = true;
|
|
211
|
-
this.messages = messages;
|
|
212
|
-
}
|
|
108
|
+
export const UsbPlugin: LowlevelTransportSharedPlugin = {
|
|
109
|
+
version: '1.0.0',
|
|
213
110
|
|
|
214
|
-
|
|
215
|
-
//
|
|
216
|
-
}
|
|
111
|
+
async init(): Promise<void> {
|
|
112
|
+
// libusb requires no global initialization
|
|
113
|
+
},
|
|
217
114
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
* Opens each device briefly to read its serial number (used as `path`),
|
|
221
|
-
* then closes it. acquire() re-opens from a fresh getDeviceList().
|
|
222
|
-
*/
|
|
223
|
-
async enumerate(): Promise<OneKeyDeviceInfo[]> {
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
116
|
+
async enumerate(): Promise<LowLevelDevice[]> {
|
|
224
117
|
const allDevices = usb.getDeviceList();
|
|
225
118
|
|
|
226
119
|
const onekeyDevices = allDevices.filter(d => {
|
|
@@ -228,132 +121,26 @@ export default class NodeUsbTransport {
|
|
|
228
121
|
return ONEKEY_WEBUSB_FILTER.some(f => idVendor === f.vendorId && idProduct === f.productId);
|
|
229
122
|
});
|
|
230
123
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
results.push({
|
|
238
|
-
path: serial,
|
|
239
|
-
id: serial,
|
|
240
|
-
name: 'OneKey',
|
|
241
|
-
commType: 'usb',
|
|
242
|
-
debug: false,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
// Atomic swap — concurrent acquire() always sees a complete map
|
|
246
|
-
this.serialToBusId = newSerialToBusId;
|
|
247
|
-
return results;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Acquire device — open USB device, claim interface, return path (string).
|
|
252
|
-
*/
|
|
253
|
-
async acquire(input: AcquireInput): Promise<string> {
|
|
254
|
-
const path = input.path ?? '';
|
|
255
|
-
if (!path) {
|
|
256
|
-
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, 'No device path provided');
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
try {
|
|
260
|
-
await this.openDevice(path);
|
|
261
|
-
return path;
|
|
262
|
-
} catch (error: any) {
|
|
263
|
-
this.Log?.debug('NodeUsbTransport acquire error: ', error);
|
|
264
|
-
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, error.message ?? String(error));
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Release device — release interface and close.
|
|
270
|
-
*/
|
|
271
|
-
async release(path: string, _onclose?: boolean): Promise<void> {
|
|
272
|
-
const openDev = this.openDevices.get(path);
|
|
273
|
-
if (!openDev) return;
|
|
274
|
-
|
|
275
|
-
try {
|
|
276
|
-
await new Promise<void>(resolve => {
|
|
277
|
-
openDev.iface.release(() => {
|
|
278
|
-
try {
|
|
279
|
-
openDev.device.close();
|
|
280
|
-
} catch {
|
|
281
|
-
/* ignore */
|
|
282
|
-
}
|
|
283
|
-
resolve();
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
} catch {
|
|
287
|
-
try {
|
|
288
|
-
openDev.device.close();
|
|
289
|
-
} catch {
|
|
290
|
-
/* ignore */
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
this.openDevices.delete(path);
|
|
294
|
-
}
|
|
124
|
+
return onekeyDevices.map(d => ({
|
|
125
|
+
id: getDeviceId(d),
|
|
126
|
+
name: 'OneKey',
|
|
127
|
+
commType: 'usb' as const,
|
|
128
|
+
}));
|
|
129
|
+
},
|
|
295
130
|
|
|
296
|
-
/**
|
|
297
|
-
* Call device method — encode protobuf, send packets, receive response.
|
|
298
|
-
* This is the core method that replaces LowlevelTransport's call + UsbPlugin's send/receive.
|
|
299
|
-
*/
|
|
300
|
-
async call(path: string, name: string, data: Record<string, unknown>) {
|
|
301
|
-
if (!this.messages) {
|
|
302
|
-
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const openDev = this.openDevices.get(path);
|
|
306
|
-
if (!openDev) {
|
|
307
|
-
throw ERRORS.TypedError(HardwareErrorCode.DeviceNotFound, `Device not acquired: ${path}`);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const { messages } = this;
|
|
311
|
-
if (LogBlockCommand.has(name)) {
|
|
312
|
-
this.Log?.debug('NodeUsbTransport call-', ' name: ', name);
|
|
313
|
-
} else {
|
|
314
|
-
this.Log?.debug('NodeUsbTransport call-', ' name: ', name, ' data: ', data);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Encode protobuf message into 63-byte chunks (same as WebUsbTransport)
|
|
318
|
-
const encodeBuffers = buildEncodeBuffers(messages, name, data);
|
|
319
|
-
|
|
320
|
-
// Send each chunk with 0x3F report ID prefix
|
|
321
|
-
for (const buffer of encodeBuffers) {
|
|
322
|
-
const packet = new Uint8Array(PACKET_SIZE);
|
|
323
|
-
packet[0] = REPORT_ID;
|
|
324
|
-
packet.set(new Uint8Array(buffer), 1);
|
|
325
|
-
await transferOut(openDev.epOut, Buffer.from(packet));
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Receive response
|
|
329
|
-
const resData = await this.receiveData(openDev);
|
|
330
|
-
if (typeof resData !== 'string') {
|
|
331
|
-
throw ERRORS.TypedError(HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
332
|
-
}
|
|
333
|
-
const jsonData = receiveOne(messages, resData);
|
|
334
|
-
return check.call(jsonData);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
cancel() {
|
|
338
|
-
this.Log?.debug('NodeUsbTransport cancel');
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// --- Private helpers ---
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Open a USB device by path (serial number), claim interface, cache endpoints.
|
|
345
|
-
*/
|
|
346
131
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
347
|
-
|
|
348
|
-
const existing =
|
|
349
|
-
if (existing)
|
|
132
|
+
async connect(uuid: string): Promise<void> {
|
|
133
|
+
const existing = openDevices.get(uuid);
|
|
134
|
+
if (existing) {
|
|
135
|
+
activeDevice = existing;
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
350
138
|
|
|
351
|
-
//
|
|
352
|
-
const busId = this.serialToBusId.get(path) ?? path;
|
|
139
|
+
// Find the device by our uuid (bus:address)
|
|
353
140
|
const allDevices = usb.getDeviceList();
|
|
354
|
-
const dev = allDevices.find(d =>
|
|
141
|
+
const dev = allDevices.find(d => getDeviceId(d) === uuid);
|
|
355
142
|
if (!dev) {
|
|
356
|
-
throw
|
|
143
|
+
throw new Error(`USB device not found: ${uuid}`);
|
|
357
144
|
}
|
|
358
145
|
|
|
359
146
|
dev.open();
|
|
@@ -374,6 +161,7 @@ export default class NodeUsbTransport {
|
|
|
374
161
|
|
|
375
162
|
iface.claim();
|
|
376
163
|
|
|
164
|
+
// Find IN and OUT endpoints
|
|
377
165
|
const epIn = iface.endpoints.find(
|
|
378
166
|
(e): e is usb.InEndpoint => e.direction === 'in' && e.address === ENDPOINT_IN
|
|
379
167
|
);
|
|
@@ -383,31 +171,74 @@ export default class NodeUsbTransport {
|
|
|
383
171
|
|
|
384
172
|
if (!epIn || !epOut) {
|
|
385
173
|
dev.close();
|
|
386
|
-
throw
|
|
387
|
-
HardwareErrorCode.DeviceNotFound,
|
|
388
|
-
'USB endpoints not found (expected IN 0x81, OUT 0x01)'
|
|
389
|
-
);
|
|
174
|
+
throw new Error('USB endpoints not found (expected IN 0x81, OUT 0x01)');
|
|
390
175
|
}
|
|
391
176
|
|
|
392
177
|
epIn.timeout = TRANSFER_TIMEOUT_MS;
|
|
393
178
|
epOut.timeout = TRANSFER_TIMEOUT_MS;
|
|
394
179
|
|
|
395
|
-
|
|
396
|
-
|
|
180
|
+
const openDev: OpenDevice = { device: dev, iface, epIn, epOut };
|
|
181
|
+
openDevices.set(uuid, openDev);
|
|
182
|
+
activeDevice = openDev;
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
186
|
+
async disconnect(uuid: string): Promise<void> {
|
|
187
|
+
const openDev = openDevices.get(uuid);
|
|
188
|
+
if (openDev) {
|
|
189
|
+
try {
|
|
190
|
+
openDev.iface.release(() => {
|
|
191
|
+
try {
|
|
192
|
+
openDev.device.close();
|
|
193
|
+
} catch {
|
|
194
|
+
/* ignore */
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
} catch {
|
|
198
|
+
try {
|
|
199
|
+
openDev.device.close();
|
|
200
|
+
} catch {
|
|
201
|
+
/* ignore */
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
openDevices.delete(uuid);
|
|
205
|
+
if (activeDevice === openDev) {
|
|
206
|
+
activeDevice = null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
async send(uuid: string, data: string): Promise<void> {
|
|
212
|
+
const openDev = openDevices.get(uuid);
|
|
213
|
+
if (!openDev) {
|
|
214
|
+
throw new Error(`Device not connected: ${uuid}`);
|
|
215
|
+
}
|
|
216
|
+
activeDevice = openDev;
|
|
217
|
+
|
|
218
|
+
// data is a hex string of a 64-byte packet (0x3F + 63 bytes payload),
|
|
219
|
+
// already framed by LowlevelTransport's buildBuffers().
|
|
220
|
+
const dataBuffer = Buffer.from(data, 'hex');
|
|
397
221
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
222
|
+
// libusb transfers the raw packet directly — no Report ID prepend needed
|
|
223
|
+
// (Report ID is a HID concept; libusb operates at USB level)
|
|
224
|
+
await transferOut(openDev.epOut, dataBuffer);
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
async receive(): Promise<string> {
|
|
228
|
+
if (!activeDevice) {
|
|
229
|
+
throw new Error('No active device for receive');
|
|
230
|
+
}
|
|
231
|
+
const dev = activeDevice;
|
|
232
|
+
|
|
233
|
+
// Mirrors WebUsbTransport.receiveData() exactly:
|
|
234
|
+
// 1. Read first 64-byte packet, skip byte[0] (0x3F marker)
|
|
235
|
+
const firstPacket = await readPacket(dev);
|
|
405
236
|
const firstData = skipReportByte(firstPacket);
|
|
406
237
|
|
|
407
|
-
//
|
|
238
|
+
// 2. Use SDK's decodeChunked to parse ## header → { typeId, length, restBuffer }
|
|
408
239
|
const { length, typeId, restBuffer } = decodeProtocol.decodeChunked(toArrayBuffer(firstData));
|
|
409
240
|
|
|
410
|
-
// Allocate result: typeId(2) + length(4) + payload(length)
|
|
241
|
+
// 3. Allocate result buffer: typeId(2) + length(4) + payload(length)
|
|
411
242
|
const lengthWithHeader = Number(length) + HEADER_LENGTH;
|
|
412
243
|
const decoded = new ByteBuffer(lengthWithHeader);
|
|
413
244
|
decoded.writeUint16(typeId);
|
|
@@ -416,22 +247,24 @@ export default class NodeUsbTransport {
|
|
|
416
247
|
decoded.append(restBuffer);
|
|
417
248
|
}
|
|
418
249
|
|
|
419
|
-
// Read subsequent packets until complete
|
|
250
|
+
// 4. Read subsequent packets until complete
|
|
420
251
|
while (decoded.offset < lengthWithHeader) {
|
|
421
|
-
const packet = await
|
|
252
|
+
const packet = await readPacket(dev);
|
|
422
253
|
const pktData = skipReportByte(packet);
|
|
423
254
|
const buf = toArrayBuffer(pktData);
|
|
424
|
-
if (lengthWithHeader - decoded.offset >=
|
|
255
|
+
if (lengthWithHeader - decoded.offset >= PACKET_SIZE) {
|
|
425
256
|
decoded.append(buf);
|
|
426
257
|
} else {
|
|
427
258
|
decoded.append(buf.slice(0, lengthWithHeader - decoded.offset));
|
|
428
259
|
}
|
|
429
260
|
}
|
|
430
261
|
|
|
262
|
+
// 5. Return as hex string
|
|
431
263
|
decoded.reset();
|
|
432
264
|
const result = decoded.toBuffer();
|
|
433
265
|
return Buffer.from(result as unknown as ArrayBuffer).toString('hex');
|
|
434
|
-
}
|
|
435
|
-
}
|
|
266
|
+
},
|
|
267
|
+
};
|
|
436
268
|
|
|
269
|
+
export default UsbPlugin;
|
|
437
270
|
export { PACKET_SIZE } from './constants';
|