Commit 1e117d08 authored by Ryan Day's avatar Ryan Day

new parser working for sign-on!

parent 89a1c16c
var test = require('tape');
var stk500 = require('../stk500');
var serialport = require('serialport');
var intel_hex = require('intel-hex');
var fs = require('fs');
var data = fs.readFileSync(__dirname+'/../../panda-attack/bootstrap.hex')+'';
var hex = intel_hex.parse(data).data;
var pageSize = 256;
var baud = 115200;
var delay1 = 10; //minimum is 2.5us, so anything over 1 fine?
var delay2 = 1;
var signature = new Buffer([0x1e, 0x98, 0x01]);
var options = {
timeout:0xc8,
stabDelay:0x64,
cmdexeDelay:0x19,
synchLoops:0x20,
byteDelay:0x00,
pollValue:0x53,
pollIndex:0x03
};
var comName = '/dev/ttyACM0';
var serialPort = new serialport.SerialPort(comName, {
baudrate: baud,
parser: serialport.parsers.raw
}, function(){
console.log('opened!',serialPort.opened);
console.log(serialPort);
});
var flasher = stk500(serialPort);
flasher.parser.on('rawinput',function(buf){
console.log("->",buf.toString('hex'));
})
flasher.parser.on('raw',function(buf){
console.log("<-",buf.toString('hex'));
})
flasher.sync(2,function(err,data){
console.log('callback',err,data)
})
//([0x10, 0xc8, 0x64, 0x19, 0x20, 0x00, 0x53, 0x03, ---- 0xac, 0x53, 0x00, 0x00], function(err, resp) {
//default to v2
module.exports = require('./stk500v2');
module.exports.v2 = module.exports;
module.exports.v1 = false;//todo
// STK message constants
module.exports.MESSAGE_START = '\x1B'
module.exports.TOKEN = '\x0E'
module.exports.MESSAGE_START = 0x1B
module.exports.TOKEN = 0x0E
// STK general command constants
module.exports.CMD_SIGN_ON = '\x01'
module.exports.CMD_SET_PARAMETER = '\x02'
module.exports.CMD_GET_PARAMETER = '\x03'
module.exports.CMD_SET_DEVICE_PARAMETERS = '\x04'
module.exports.CMD_OSCCAL = '\x05'
module.exports.CMD_LOAD_ADDRESS = '\x06'
module.exports.CMD_FIRMWARE_UPGRADE = '\x07'
module.exports.CMD_SIGN_ON = 0x01
module.exports.CMD_SET_PARAMETER = 0x02
module.exports.CMD_GET_PARAMETER = 0x03
module.exports.CMD_SET_DEVICE_PARAMETERS = 0x04
module.exports.CMD_OSCCAL = 0x05
module.exports.CMD_LOAD_ADDRESS = 0x06
module.exports.CMD_FIRMWARE_UPGRADE = 0x07
// STK ISP command constants
module.exports.CMD_ENTER_PROGMODE_ISP = '\x10'
module.exports.CMD_LEAVE_PROGMODE_ISP = '\x11'
module.exports.CMD_CHIP_ERASE_ISP = '\x12'
module.exports.CMD_PROGRAM_FLASH_ISP = '\x13'
module.exports.CMD_READ_FLASH_ISP = '\x14'
module.exports.CMD_PROGRAM_EEPROM_ISP = '\x15'
module.exports.CMD_READ_EEPROM_ISP = '\x16'
module.exports.CMD_PROGRAM_FUSE_ISP = '\x17'
module.exports.CMD_READ_FUSE_ISP = '\x18'
module.exports.CMD_PROGRAM_LOCK_ISP = '\x19'
module.exports.CMD_READ_LOCK_ISP = '\x1A'
module.exports.CMD_READ_SIGNATURE_ISP = '\x1B'
module.exports.CMD_READ_OSCCAL_ISP = '\x1C'
module.exports.CMD_SPI_MULTI = '\x1D'
module.exports.CMD_ENTER_PROGMODE_ISP = 0x10
module.exports.CMD_LEAVE_PROGMODE_ISP = 0x11
module.exports.CMD_CHIP_ERASE_ISP = 0x12
module.exports.CMD_PROGRAM_FLASH_ISP = 0x13
module.exports.CMD_READ_FLASH_ISP = 0x14
module.exports.CMD_PROGRAM_EEPROM_ISP = 0x15
module.exports.CMD_READ_EEPROM_ISP = 0x16
module.exports.CMD_PROGRAM_FUSE_ISP = 0x17
module.exports.CMD_READ_FUSE_ISP = 0x18
module.exports.CMD_PROGRAM_LOCK_ISP = 0x19
module.exports.CMD_READ_LOCK_ISP = 0x1A
module.exports.CMD_READ_SIGNATURE_ISP = 0x1B
module.exports.CMD_READ_OSCCAL_ISP = 0x1C
module.exports.CMD_SPI_MULTI = 0x1D
// STK PP command constants
module.exports.CMD_ENTER_PROGMODE_PP = '\x20'
module.exports.CMD_LEAVE_PROGMODE_PP = '\x21'
module.exports.CMD_CHIP_ERASE_PP = '\x22'
module.exports.CMD_PROGRAM_FLASH_PP = '\x23'
module.exports.CMD_READ_FLASH_PP = '\x24'
module.exports.CMD_PROGRAM_EEPROM_PP = '\x25'
module.exports.CMD_READ_EEPROM_PP = '\x26'
module.exports.CMD_PROGRAM_FUSE_PP = '\x27'
module.exports.CMD_READ_FUSE_PP = '\x28'
module.exports.CMD_PROGRAM_LOCK_PP = '\x29'
module.exports.CMD_READ_LOCK_PP = '\x2A'
module.exports.CMD_READ_SIGNATURE_PP = '\x2B'
module.exports.CMD_READ_OSCCAL_PP = '\x2C'
module.exports.CMD_SET_CONTROL_STACK = '\x2D'
module.exports.CMD_ENTER_PROGMODE_PP = 0x20
module.exports.CMD_LEAVE_PROGMODE_PP = 0x21
module.exports.CMD_CHIP_ERASE_PP = 0x22
module.exports.CMD_PROGRAM_FLASH_PP = 0x23
module.exports.CMD_READ_FLASH_PP = 0x24
module.exports.CMD_PROGRAM_EEPROM_PP = 0x25
module.exports.CMD_READ_EEPROM_PP = 0x26
module.exports.CMD_PROGRAM_FUSE_PP = 0x27
module.exports.CMD_READ_FUSE_PP = 0x28
module.exports.CMD_PROGRAM_LOCK_PP = 0x29
module.exports.CMD_READ_LOCK_PP = 0x2A
module.exports.CMD_READ_SIGNATURE_PP = 0x2B
module.exports.CMD_READ_OSCCAL_PP = 0x2C
module.exports.CMD_SET_CONTROL_STACK = 0x2D
// STK HVSP command constants
module.exports.CMD_ENTER_PROGMODE_HVSP = '\x30'
module.exports.CMD_LEAVE_PROGMODE_HVSP = '\x31'
module.exports.CMD_CHIP_ERASE_HVSP = '\x32'
module.exports.CMD_PROGRAM_FLASH_HVSP = '\x33'
module.exports.CMD_READ_FLASH_HVSP = '\x34'
module.exports.CMD_PROGRAM_EEPROM_HVSP = '\x35'
module.exports.CMD_READ_EEPROM_HVSP = '\x36'
module.exports.CMD_PROGRAM_FUSE_HVSP = '\x37'
module.exports.CMD_READ_FUSE_HVSP = '\x38'
module.exports.CMD_PROGRAM_LOCK_HVSP = '\x39'
module.exports.CMD_READ_LOCK_HVSP = '\x3A'
module.exports.CMD_READ_SIGNATURE_HVSP = '\x3B'
module.exports.CMD_READ_OSCCAL_HVSP = '\x3C'
module.exports.CMD_ENTER_PROGMODE_HVSP = 0x30
module.exports.CMD_LEAVE_PROGMODE_HVSP = 0x31
module.exports.CMD_CHIP_ERASE_HVSP = 0x32
module.exports.CMD_PROGRAM_FLASH_HVSP = 0x33
module.exports.CMD_READ_FLASH_HVSP = 0x34
module.exports.CMD_PROGRAM_EEPROM_HVSP = 0x35
module.exports.CMD_READ_EEPROM_HVSP = 0x36
module.exports.CMD_PROGRAM_FUSE_HVSP = 0x37
module.exports.CMD_READ_FUSE_HVSP = 0x38
module.exports.CMD_PROGRAM_LOCK_HVSP = 0x39
module.exports.CMD_READ_LOCK_HVSP = 0x3A
module.exports.CMD_READ_SIGNATURE_HVSP = 0x3B
module.exports.CMD_READ_OSCCAL_HVSP = 0x3C
// STK status constants
// Success
module.exports.STATUS_CMD_OK = '\x00'
module.exports.STATUS_CMD_OK = 0x00
// Warnings
module.exports.STATUS_CMD_TOUT = '\x80'
module.exports.STATUS_RDY_BSY_TOUT = '\x81'
module.exports.STATUS_SET_PARAM_MISSING = '\x82'
module.exports.STATUS_CMD_TOUT = 0x80
module.exports.STATUS_RDY_BSY_TOUT = 0x81
module.exports.STATUS_SET_PARAM_MISSING = 0x82
// Errors
module.exports.STATUS_CMD_FAILED = '\xC0'
module.exports.STATUS_CKSUM_ERROR = '\xC1'
module.exports.STATUS_CMD_UNKNOWN = '\xC9'
module.exports.STATUS_CMD_FAILED = 0xC0
module.exports.STATUS_CKSUM_ERROR = 0xC1
module.exports.STATUS_CMD_UNKNOWN = 0xC9
// STK parameter constants
module.exports.STATUS_BUILD_NUMBER_LOW = '\x80'
module.exports.STATUS_BUILD_NUMBER_HIGH = '\x81'
module.exports.STATUS_HW_VER = '\x90'
module.exports.STATUS_SW_MAJOR = '\x91'
module.exports.STATUS_SW_MINOR = '\x92'
module.exports.STATUS_VTARGET = '\x94'
module.exports.STATUS_VADJUST = '\x95'
module.exports.STATUS_OSC_PSCALE = '\x96'
module.exports.STATUS_OSC_CMATCH = '\x97'
module.exports.STATUS_SCK_DURATION = '\x98'
module.exports.STATUS_TOPCARD_DETECT = '\x9A'
module.exports.STATUS_STATUS = '\x9C'
module.exports.STATUS_DATA = '\x9D'
module.exports.STATUS_RESET_POLARITY = '\x9E'
module.exports.STATUS_CONTROLLER_INIT = '\x9F'
module.exports.STATUS_BUILD_NUMBER_LOW = 0x80
module.exports.STATUS_BUILD_NUMBER_HIGH = 0x81
module.exports.STATUS_HW_VER = 0x90
module.exports.STATUS_SW_MAJOR = 0x91
module.exports.STATUS_SW_MINOR = 0x92
module.exports.STATUS_VTARGET = 0x94
module.exports.STATUS_VADJUST = 0x95
module.exports.STATUS_OSC_PSCALE = 0x96
module.exports.STATUS_OSC_CMATCH = 0x97
module.exports.STATUS_SCK_DURATION = 0x98
module.exports.STATUS_TOPCARD_DETECT = 0x9A
module.exports.STATUS_STATUS = 0x9C
module.exports.STATUS_DATA = 0x9D
module.exports.STATUS_RESET_POLARITY = 0x9E
module.exports.STATUS_CONTROLLER_INIT = 0x9F
// STK answer constants
module.exports.ANSWER_CKSUM_ERROR = '\xB0'
module.exports.ANSWER_CKSUM_ERROR = 0xB0
var c = require('./constants-v2');
var EventEmitter = require("events").EventEmitter;
module.exports = function(serialport){
module.exports = function(serialPort){
var o = ext(
new EventEmitter,
{
constants:c,
port:serialport,
port:serialPort,
boundOpen:false,
closed:false,
// write
_inc:-1,
......@@ -30,22 +31,27 @@ module.exports = function(serialport){
var timeout = this._commandTimeout(body[0]);
var messageLen = new Buffer([0,0]);
messageLen.writeUInt16BE(body.length);
messageLen.writeUInt16BE(body.length,0);
//MESSAGE_START,SEQUENCE_NUMBER,MESSAGE_SIZE,TOKEN,MESSAGE_BODY,CMD_READ/PROGRAM_FLASH/EEPROM,CHECKSUM
var out = Buffer.concat(new Buffer([c.MESSAGE_START,this._seq(),messageLen[0],messageLen[1],c.TOKEN]),body);
var out = Buffer.concat([new Buffer([c.MESSAGE_START,this._seq(),messageLen[0],messageLen[1],c.TOKEN]),body]);
var checksum = this.checksum(out);
this.queue.push({buf:Buffer.concat(out,new Buffer([checksum])),seq:this._inc,cb:cb,timeout:timeout});
this._queue.push({buf:Buffer.concat([out,new Buffer([checksum])]),seq:this._inc,cb:cb,timeout:timeout});
// if not waiting for another command to return. send this command
this._send();
},
checksum:function(buf){
var checksum = 0;
for(var i=0;i<buf.length;++i){
checksum ^= buf[i];
}
return checksum;
},
_seq:function(){
......@@ -59,7 +65,7 @@ module.exports = function(serialport){
//seconds for the CMD_READ/PROGRAM_FLASH/EEPROM commands, and 1
//second for all other commands.
timeout = 1000;
if(body[0] === c.CMD_SIGN_ON) timeout = 200;
if(typeByte === c.CMD_SIGN_ON) timeout = 200;
else {
// grab the constant names.
var keys = Object.keys(c);
......@@ -79,6 +85,15 @@ module.exports = function(serialport){
if(this._current) return;
if(!this._queue.length) return;
// if the serialport is not open yet.
if(!serialPort.fd){
var z = this;
if(!this.boundOpen) serialPort.once('open',function(){
z._send();
});
return;
}
var message = this._queue.shift();
var current = this._current = {
timeout:false,
......@@ -90,7 +105,7 @@ module.exports = function(serialport){
this.state = 0;
var z = this;
this.port.write(buf);
this.port.write(message.buf);
this.port.drain(function(){
if(current !== z._current) return z.emit('log',"current was no longer the current message after drain callback");
current.timeout = setTimeout(function(){
......@@ -99,7 +114,7 @@ module.exports = function(serialport){
z._resolveCurrent(err);
},message.timeout);
});
this.emit('rawinput',buf);
this.emit('rawinput',message.buf);
},
_handle:function(data){
var current = this._current;
......@@ -115,16 +130,17 @@ module.exports = function(serialport){
return {
seq:-1,
len:[],
raw:[],
message:[],
checksum:0,
}
},
_stateMachine:function(curByte){
switch(state) {
var pkt = this.pkt;
switch(this.state) {
case 0:
// always reset packet.
this.pkt = this._pkt();
pkt = this.pkt = this._pkt();
if (curByte !== 0x1b) {
// the spec says "update statistics".
......@@ -139,26 +155,26 @@ module.exports = function(serialport){
this.state = 0;
return this.emit('log','parser',"Invalid sequence number. back to start. got: " + curByte);
}
this.pkt.seq = curByte;
pkt.seq = curByte;
++this.state;
break;
case 2:
pkt.len.push(curByte);
++state;
++this.state;
break;
case 3:
pkt.len.push(curByte);
pkt.len = (pkt.messageLen[0] << 8) | pkt.messageLen[1];
++state;
pkt.len = (pkt.len[0] << 8) | pkt.len[1];
++this.state;
break;
case 4:
if (curByte !== 0x0e) {
this.state = 0;
this.pkt.error = new Error("Invalid message token byte. got: " + curByte);
this.pkt.error.code = "E_PARSE";
pkt.error = new Error("Invalid message token byte. got: " + curByte);
pkt.error.code = "E_PARSE";
return this.emit('log','parser',this.pkt.error);
}
++state;
++this.state;
// can stk500 send empty messages? probably not. avrdude doesnt support it.
if(!pkt.len) ++state;
break;
......@@ -167,18 +183,20 @@ module.exports = function(serialport){
// the message was corrupted in transit or some such error.
// i could retry send the message for these errors!
// i dont buffer the message right now TODO
this.pkt.error = new Error("send checksum error");
this.pkt.error.code = "E_STATUS_CKSUM";
pkt.error = new Error("send checksum error");
pkt.error.code = "E_STATUS_CKSUM";
// TODO check to see if the first byte of all messages is a has errored byte.
// this checksum error is the only error checked for in avrdude source
}
pkt.message.push(curByte);
if (--pkt.len == 0) ++state;
if (--pkt.len == 0) ++this.state;
break;
case 6:
pkt.checksum = this.checksum(this.pkt.message);
pkt.checksum = this.checksum(pkt.raw);
pkt.checksum = (pkt.checksum === curByte) ? true : false;
if(!pkt.checksum){
pkt.error = new Error("recv cecksum didn't match");
......@@ -188,9 +206,12 @@ module.exports = function(serialport){
pkt.message = new Buffer(pkt.message);
this.emit('data',pkt);
this.state++;// sets state to 7. the parser is not interested in any other bytes until a message is queued.
this._resolveCurrent(s.pkt.error?this.pkt.error:false,pkt);
this._resolveCurrent(pkt.error?pkt.error:false,pkt);
break;
}
pkt.raw.push(curByte);
},
_resolveCurrent:function(err,pkt){
var toCall = this._current;
......@@ -205,6 +226,8 @@ module.exports = function(serialport){
this.queue = [];
}
clearTimeout(toCall.timeout);
toCall.cb(err,pkt);
if(q){
var e = err;
......@@ -220,7 +243,7 @@ module.exports = function(serialport){
}
});
serialPort.on('data',dataHandler).on('error',cleanup).on('close',cleanup);
serialPort.on('data',dataHandler).once('error',cleanup).once('close',cleanup);
return o;
......@@ -229,12 +252,11 @@ module.exports = function(serialport){
};
function cleanup(err){
// prevent new commands from writing to serial.
o.closed = true;
// stop sending data
serialPort.removeListener('data',dataHandler);
serialPort.removeListener('error',cleanup);
serialPort.removeListener('close',cleanup);
if(!err) {
err = new Error('serial closed.');
......
......@@ -4,7 +4,7 @@
"description": "STK500 in javascript",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "tape test/*.js"
},
"repository": {
"type": "git",
......@@ -29,5 +29,13 @@
"bugs": {
"url": "https://github.com/Pinoccio/js-stk500/issues"
},
"homepage": "https://github.com/Pinoccio/js-stk500"
"homepage": "https://github.com/Pinoccio/js-stk500",
"dependencies": {
"buffer-equal": "0.0.1",
"intel-hex": "^0.1.1",
},
"devDependencies": {
"serialport": "^1.3.1",
"tape": "^2.12.2"
}
}
//use strict might have screwed up my this context, or might not have..
var async = require("async");
var bufferEqual = require('buffer-equal');
var parser = require('./lib/parser-v2.js');
var c = require('./lib/constants-v2.js');
var CMD_SIGN_ON = 0x01;
var CMD_LOAD_ADDRESS = 0x06;
var CMD_ENTER_PROGMODE_ISP = 0x10;
var CMD_LEAVE_PROGMODE_ISP = 0x11;
var CMD_PROGRAM_FLASH_ISP = 0x13;
var CMD_SPI_MULTI = 0x1D;
var _options = {
timeout:0xc8,
stabDelay:0x64,
cmdexeDelay:0x19,
synchLoops:0x20,
byteDelay:0x00,
pollValue:0x53,
pollIndex:0x03
};
function stk500(port) {
if (!(this instanceof stk500))
return new stk500(port);
var self = this;
self.parser = parser(port);
// use these constants. instead of requiring them above because that should be a different module.
self.constants = self.parser.constants;
self.serialPort = port;
};
stk500.prototype.reset = function(delay1, delay2, done){
console.log("reset");
var self = this;
async.series([
function(cbdone) {
console.log("asserting");
self.serialPort.set({rts:true, dtr:true}, function(result){
console.log("asserted");
if(result) cbdone(result);
else cbdone();
});
},
function(cbdone) {
console.log("wait");
setTimeout(cbdone, delay1);
},
function(cbdone) {
console.log("clearing");
self.serialPort.set({rts:false, dtr:false}, function(result){
console.log("clear");
if(result) cbdone(result);
else cbdone();
});
},
function(cbdone) {
console.log("wait");
setTimeout(cbdone, delay2);
}],
function(error) {
done(error);
}
);
};
stk500.prototype.sync = function(attempts, done) {
console.log("sync");
var self = this;
var tries = 1;
var cmd = new Buffer([CMD_SIGN_ON]);
attempt();
function attempt(){
tries=tries+1;
self.parser.send(cmd, function(error, results){
console.log("confirm sync");
if(error) {
if(tries<=attempts){
console.log("failed attempt again");
attempt();
}else{
done(error);
}
}else{
console.log("confirmed sync");
done();
}
// });
});
}
};
stk500.prototype.verifySignature = function(signature, done) {
console.log("verify signature");
this.getSignature(function(error, reportedSignature){
console.log(reportedSignature);
console.log(signature);
if(!bufferEqual(signature, reportedSignature)){
done(new Error("signature doesnt match. Found: " + reportedSignature.toString('hex'), error));
}else{
done();
}
});
}
stk500.prototype.getSignature = function(done) {
console.log("read signature");
var self = this;
var reportedSignature = new Buffer(3);
async.series([
function(cbdone){
var numTx = 0x04;
var numRx = 0x04;
var rxStartAddr = 0x00;
var cmd = new Buffer([CMD_SPI_MULTI, numTx, numRx, rxStartAddr, 0x30, 0x00, 0x00, 0x00]);
self.parser.send(cmd, function(error, pkt) {
console.log("sent sig1");
if (pkt && pkt.message && pkt.message.length >= 6)
{
var sig = pkt.message[5];
reportedSignature.writeUInt8(sig, 0);
}
// self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(error){
cbdone(error);
// });
});
},
function(cbdone){
var numTx = 0x04;
var numRx = 0x04;
var rxStartAddr = 0x00;
var cmd = new Buffer([CMD_SPI_MULTI, numTx, numRx, rxStartAddr, 0x30, 0x00, 0x01, 0x00]);
self.parser.send(cmd, function(error, pkt) {
console.log("sent sig2");
if (pkt && pkt.message && pkt.message.length >= 6)
{
var sig = pkt.message[5];
reportedSignature.writeUInt8(sig, 1);
}
// self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(error){
cbdone(error);