| // Copyright 2017 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| 'use strict'; |
| |
| var twinkie = {}; |
| |
| // USB identifiers. |
| twinkie.USB_VENDOR_ID = 0x18d1; |
| twinkie.USB_PRODUCT_ID = 0x500a; |
| |
| twinkie.EP_SNIFFER = 3; |
| |
| twinkie.isTwinkie = function(device) { |
| return device.vendorId === twinkie.USB_VENDOR_ID && |
| device.productId === twinkie.USB_PRODUCT_ID; |
| }; |
| |
| twinkie.Dongle = function(device) { |
| this.device_ = device; |
| this.enc_ = new TextEncoder('utf-8'); |
| this.dec_ = new TextDecoder(); |
| this.vbus_re_ = new RegExp('VBUS = (-?[0-9]+) mV ; (-?[0-9]+) mA'); |
| this.tracing_mode_ = false; |
| console.log(device); |
| }; |
| |
| twinkie.Dongle.prototype.connect = function() { |
| let readLoop = () => { |
| this.device_.transferIn(twinkie.EP_SNIFFER, 64).then(result => { |
| if (this.tracing_mode_ && result.data.byteLength) { |
| var pkt = new Uint32Array(result.data.buffer); |
| var sop = pkt[2] >> 16; |
| var head = pkt[2] & 0xffff; |
| var cnt = (head >> 12) & 0x7; |
| var tstamp = pkt[0]; |
| this.onPdPacket(tstamp, sop, head, pkt.slice(3, 3 + cnt)); |
| } |
| readLoop(); |
| }, error => { |
| this.onReceiveError(error); |
| }); |
| }; |
| |
| return this.device_.open() |
| .then(() => { |
| if (this.device_.configuration === null) { |
| return this.device_.selectConfiguration(1); |
| } |
| }) |
| .then(() => { |
| // Default to the first interface if there is no vendor one |
| var ifaces = this.device_.configuration.interfaces; |
| var vendor = ifaces[0]; |
| for (var iface = 0; iface < ifaces.length; iface++) { |
| if (ifaces[iface].alternates[0].interfaceClass === 255 && |
| ifaces[iface].alternates[0].interfaceSubclass === 0) { |
| vendor = ifaces[iface]; |
| break; |
| } |
| } |
| this.ifaceNum_ = vendor.interfaceNumber; |
| var eps = vendor.alternates[0].endpoints; |
| this.epIn_ = 1; |
| this.epOut_ = 1; |
| for (var ep = 0; ep < eps.length; ep++) { |
| if (eps[ep].direction === 'in' && eps[ep].type === 'bulk') { |
| this.epIn_ = eps[ep].endpointNumber; |
| } else if (eps[ep].direction === 'out' && eps[ep].type === 'bulk') { |
| this.epOut_ = eps[ep].endpointNumber; |
| } |
| } |
| }) |
| .then(() => this.device_.claimInterface(this.ifaceNum_)) |
| .then(() => this.device_.claimInterface(1)) |
| .then(() => this.getVersion()) |
| .then(ver => { |
| console.log('Version: ' + ver); |
| this.fwVersion = ver; |
| return this.getCopy(); |
| }) |
| .then(cpy => { |
| console.log('FW Copy: ' + cpy); |
| this.fwCopy = cpy; |
| readLoop(); |
| }); |
| }; |
| |
| twinkie.Dongle.prototype.disconnect = function() { |
| return this.device_.close(); |
| }; |
| |
| twinkie.Dongle.prototype.send = function(text) { |
| return this.device_.transferOut(this.epOut_, this.enc_.encode(text).buffer); |
| }; |
| |
| twinkie.Dongle.prototype.command = function(cmd) { |
| return new Promise(function(resolve, reject) { |
| var resp = ''; |
| |
| let readResponse = () => { |
| this.device_.transferIn(this.epIn_, 64).then(result => { |
| if (result.status !== 'ok') { |
| reject('Read failed: ' + result.status); |
| } |
| var len = result.data.byteLength; |
| if (len) { |
| resp += this.dec_.decode(result.data); |
| } |
| if (len === 64) { |
| /* more to come */ |
| readResponse(); |
| } else { |
| /* response fully received */ |
| resolve(resp); |
| } |
| }, error => { |
| reject('Read error: ' + error); |
| }); |
| }; |
| |
| this.device_.transferOut(this.epOut_, this.enc_.encode(cmd).buffer) |
| .then(() => { |
| readResponse(); |
| }); |
| }.bind(this)); |
| }; |
| |
| twinkie.Dongle.prototype.vbusDone = function(info) { |
| var mtch = info.match(this.vbus_re_); |
| if (mtch && mtch.length === 3) { |
| var vbus_pair = [mtch[1], mtch[2]]; |
| this.onVBusMeasure(vbus_pair); |
| } |
| if (this.tracing_mode_ === true) { |
| this.command('tw vbus').then(this.vbusDone.bind(this)); |
| } |
| }; |
| |
| twinkie.Dongle.prototype.trace_mode = function(enable) { |
| if (this.tracing_mode_ === enable) { |
| return; |
| } |
| |
| if (enable) { |
| this.send('tw trace raw'); |
| } else { |
| this.send('tw trace off'); |
| } |
| this.tracing_mode_ = enable; |
| this.command('tw vbus').then(this.vbusDone.bind(this)); |
| }; |
| |
| twinkie.Dongle.prototype.getVersion = function() { |
| return this.command('version').then(function(ver) { |
| var versionRE = new RegExp('^Build:\\s+(\\S+)$', 'm'); |
| var mtch = ver.match(versionRE); |
| if (mtch && mtch.length === 2) { |
| return mtch[1]; |
| } |
| |
| return 'unknown'; |
| }); |
| }; |
| |
| twinkie.Dongle.prototype.getCopy = function() { |
| return this.command('sysinfo').then(function(info) { |
| var copyRE = new RegExp('^Copy:\\s+(\\S+)$', 'm'); |
| var mtch = info.match(copyRE); |
| if (mtch && mtch.length === 2) { |
| return mtch[1]; |
| } |
| |
| return 'RO'; |
| }); |
| }; |
| |
| twinkie.Dongle.prototype.goPartition = function(part) { |
| this.send('sysjump ' + part); |
| }; |
| |
| twinkie.Dongle.prototype.readVbus = function() { |
| return this.command('tw vbus').then(function(info) { |
| var mtch = info.match(this.vbus_re_); |
| if (mtch && mtch.length === 3) { |
| var vbus_pair = [mtch[1], mtch[2]]; |
| this.onVBusMeasure(vbus_pair); |
| return vbus_pair; |
| } |
| return [0, 0]; |
| }.bind(this)); |
| }; |