Skip to content
This repository has been archived by the owner on Aug 7, 2019. It is now read-only.

Commit

Permalink
Add async queue around "sendRaw"
Browse files Browse the repository at this point in the history
* manager: Don't add devices that are already managed
* webUsbDevice: Simplify loop to join packets
  • Loading branch information
Christopher committed Jan 15, 2019
1 parent 113bb19 commit 1bf3a52
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 34 deletions.
6 changes: 5 additions & 1 deletion example/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,13 @@ window.connectWebUSB = function () {
Object.keys(manager.keepkeys).forEach((deviceID) => {
loggers[deviceID] = window.debug(deviceID)
})
manager.deviceEvents.offAny()
manager.deviceEvents.onAny((_, [deviceID, msg]) => {
loggers[deviceID](msg)
})
})
.catch(console.error)
.catch(e => {
console.error('ConnectWebUSB Error')
console.error(String(e))
})
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"device-protocol": "git+https://[email protected]/KeepKey/device-protocol.git#4ee29339fb8a9c916bcba9079aebd5254a16df08",
"eventemitter2": "^5.0.1",
"google-protobuf": "^3.6.1",
"p-queue": "^3.0.0",
"protobufjs": "^6.8.8",
"rxjs": "^6.3.3"
},
Expand All @@ -104,6 +105,7 @@
"@types/google-protobuf": "^3.2.7",
"@types/jest": "^23.3.2",
"@types/node": "^10.11.0",
"@types/p-queue": "^3.0.0",
"colors": "^1.3.2",
"commitizen": "^3.0.0",
"coveralls": "^3.0.2",
Expand Down
25 changes: 16 additions & 9 deletions src/keepkeyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import eventemitter2 from 'eventemitter2'
import { WebUSBDeviceConfig } from './webUSBDevice'

export type USBDeviceEventCallback = (deviceID: string) => void
const defaultUSBDeviceCallback = (deviceID: string) => { return }
const defaultUSBDeviceCallback = () => {} // tslint:disable-line:no-empty

export interface KeepKeyManagerConfig {
onConnectCallback?: USBDeviceEventCallback
Expand Down Expand Up @@ -37,20 +37,27 @@ export default class KeepKeyManager {
devices?: USBDevice[]
): Promise<number> {
if (!window.navigator.usb) throw new Error('WebUSB not supported in your browser!')
let devicesToInitialize = devices
if (!devicesToInitialize) devicesToInitialize = await window.navigator.usb.getDevices()
for (let i = 0; i < devicesToInitialize.length; i++) {
const usbDevice = devicesToInitialize[i]

const devicesToInitialize = devices || (await window.navigator.usb.getDevices())
.filter((dev) => !(this.keepkeys[dev.serialNumber]))

for (const usbDevice of devicesToInitialize) {
let k = KeepKey.withWebUSB({ usbDevice, ...webusbConfig })
const features = await k.initialize()
if (features) this.add(k, features.deviceId)
if (features) this.add(k, usbDevice.serialNumber)
}

return this.initializedCount
}

public add (keepkey: KeepKey, deviceID?: string) {
this.keepkeys[deviceID || keepkey.features.deviceId] = keepkey
this.decorateEvents(deviceID, keepkey.device.events)
public add (keepkey: KeepKey, deviceID?: string): boolean {
const id = deviceID || keepkey.features.deviceId
if (!(this.keepkeys[id])) {
this.keepkeys[id] = keepkey
this.decorateEvents(deviceID, keepkey.device.events)
return true
}
return false
}

public async exec (method: string, ...args): Promise<{ [deviceID: string]: any }> {
Expand Down
46 changes: 22 additions & 24 deletions src/webUSBDevice.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import ByteBuffer from 'bytebuffer'
import * as jspb from 'google-protobuf'
import eventemitter2 from 'eventemitter2'
import { default as PQueue } from 'p-queue'

import Device from './device'

export interface WebUSBDeviceConfig {
Expand All @@ -11,7 +13,7 @@ export interface WebUSBDeviceConfig {
const SEGMENT_SIZE = 63

export default class WebUSBDevice extends Device {

private queue: PQueue
public usbDevice: USBDevice
public events: eventemitter2.EventEmitter2

Expand All @@ -25,16 +27,19 @@ export default class WebUSBDevice extends Device {
super()
this.usbDevice = config.usbDevice
this.events = config.events || new eventemitter2.EventEmitter2()
this.queue = new PQueue({ concurrency: 1 })
}

public get isInitialized (): boolean {
return this.usbDevice.opened
}

public async initialize (): Promise<void> {
await this.usbDevice.open()
if (this.usbDevice.configuration === null) await this.usbDevice.selectConfiguration(1)
await this.usbDevice.claimInterface(0)
if (!this.isInitialized) {
await this.usbDevice.open()
if (this.usbDevice.configuration === null) await this.usbDevice.selectConfiguration(1)
await this.usbDevice.claimInterface(0)
}
}

public async disconnect (): Promise<void> {
Expand All @@ -50,8 +55,10 @@ export default class WebUSBDevice extends Device {
msgTypeEnum: number,
msg: jspb.Message
): Promise<[number, jspb.Message]> {
await this.write(this.toMessageBuffer(msgTypeEnum, msg))
return this.fromMessageBuffer(await this.read())
return this.queue.add(async () => {
await this.write(this.toMessageBuffer(msgTypeEnum, msg))
return this.fromMessageBuffer(await this.read())
})
}

protected async write (buff: ByteBuffer): Promise<void> {
Expand All @@ -75,24 +82,23 @@ export default class WebUSBDevice extends Device {
const msgLength = first.getUint32(5)
if (valid && msgLength > 0 && msgLength < 4194304) { // 4 MB sanity check
// FIXME: why doesn't ByteBuffer.concat() work?
let buffer = new Uint8Array(9 + 2 + msgLength)
const buffer = new Uint8Array(9 + 2 + msgLength)
for (let k = 0; k < first.byteLength; k++) {
buffer[k] = first.getUint8(k)
}
let offset = first.byteLength

if (msgLength > SEGMENT_SIZE) {
let max = Math.ceil((msgLength - first.byteLength) / SEGMENT_SIZE)
for (let i = 0; i < max; i += 1) {
let next = await this.readChunk()
for (let k = 1; k < next.byteLength; k++) {
buffer[(i + 1) * SEGMENT_SIZE + k] = next.getUint8(k)
}
while (offset < buffer.length) {
const next = await this.readChunk()
for (let k = 1; (k < next.byteLength && offset < buffer.length); k++) {
buffer[offset] = next.getUint8(k)
offset++
}
}

return ByteBuffer.wrap(buffer)
} else {
console.error('Invalid message', { msgLength, valid, first})
console.error('Invalid message', { msgLength, valid, first })
return new ByteBuffer(0)
}
}
Expand All @@ -102,20 +108,12 @@ export default class WebUSBDevice extends Device {
}

private async readChunk (): Promise<DataView> {
const result = await this.usbDevice.transferIn(1, 64)
const result = await this.usbDevice.transferIn(1, SEGMENT_SIZE + 1)

if (result.status === 'stall') {
await this.usbDevice.clearHalt('out', 1)
}

return Promise.resolve(result.data)
}

protected toByteBuffer (view: DataView): ByteBuffer {
let bb = new ByteBuffer(view.byteLength)
for (let i = 0; i < view.byteLength; i++) {
bb[i] = view.getUint8(i)
}
return bb
}
}

0 comments on commit 1bf3a52

Please sign in to comment.