/ *
USB DFU uses :
control transfers for communicating
recipient is interface
request type is class
Descriptors seems to be broken in current chrome . usb API implementation ( writing this while using canary 37.0 . 2040.0
General rule to remember is that DFU doesn 't like running specific operations while the device isn' t in idle state
that being said , it seems that certain level of CLRSTATUS is required before running another type of operation for
example switching from DNLOAD to UPLOAD , etc , clearning the state so device is in dfuIDLE is highly recommended .
* /
'use strict' ;
var STM32DFU _protocol = function ( ) {
this . callback ; // ref
this . hex ; // ref
this . verify _hex ;
this . handle = null ; // connection handle
this . request = {
DETACH : 0x00 , // OUT, Requests the device to leave DFU mode and enter the application.
DNLOAD : 0x01 , // OUT, Requests data transfer from Host to the device in order to load them into device internal Flash. Includes also erase commands
UPLOAD : 0x02 , // IN, Requests data transfer from device to Host in order to load content of device internal Flash into a Host file.
GETSTATUS : 0x03 , // IN, Requests device to send status report to the Host (including status resulting from the last request execution and the state the device will enter immediately after this request).
CLRSTATUS : 0x04 , // OUT, Requests device to clear error status and move to next step
GETSTATE : 0x05 , // IN, Requests the device to send only the state it will enter immediately after this request.
ABORT : 0x06 // OUT, Requests device to exit the current state/operation and enter idle state immediately.
} ;
this . status = {
OK : 0x00 , // No error condition is present.
errTARGET : 0x01 , // File is not targeted for use by this device.
errFILE : 0x02 , // File is for this device but fails some vendor-specific verification test
errWRITE : 0x03 , // Device is unable to write memory.
errERASE : 0x04 , // Memory erase function failed.
errCHECK _ERASED : 0x05 , // Memory erase check failed.
errPROG : 0x06 , // Program memory function failed.
errVERIFY : 0x07 , // Programmed memory failed verification.
errADDRESS : 0x08 , // Cannot program memory due to received address that is out of range.
errNOTDONE : 0x09 , // Received DFU_DNLOAD with wLength = 0, but device does not think it has all of the data yet.
errFIRMWARE : 0x0A , // Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations.
errVENDOR : 0x0B , // iString indicates a vendor-specific error.
errUSBR : 0x0C , // Device detected unexpected USB reset signaling.
errPOR : 0x0D , // Device detected unexpected power on reset.
errUNKNOWN : 0x0E , // Something went wrong, but the device does not know what it was.
errSTALLEDPKT : 0x0F // Device stalled an unexpected request.
} ;
this . state = {
appIDLE : 0 , // Device is running its normal application.
appDETACH : 1 , // Device is running its normal application, has received the DFU_DETACH request, and is waiting for a USB reset.
dfuIDLE : 2 , // Device is operating in the DFU mode and is waiting for requests.
dfuDNLOAD _SYNC : 3 , // Device has received a block and is waiting for the host to solicit the status via DFU_GETSTATUS.
dfuDNBUSY : 4 , // Device is programming a control-write block into its nonvolatile memories.
dfuDNLOAD _IDLE : 5 , // Device is processing a download operation. Expecting DFU_DNLOAD requests.
dfuMANIFEST _SYNC : 6 , // Device has received the final block of firmware from the host and is waiting for receipt of DFU_GETSTATUS to begin the Manifestation phase; or device has completed the Manifestation phase and is waiting for receipt of DFU_GETSTATUS.
dfuMANIFEST : 7 , // Device is in the Manifestation phase. (Not all devices will be able to respond to DFU_GETSTATUS when in this state.)
dfuMANIFEST _WAIT _RESET : 8 , // Device has programmed its memories and is waiting for a USB reset or a power on reset. (Devices that must enter this state clear bitManifestationTolerant to 0.)
dfuUPLOAD _IDLE : 9 , // The device is processing an upload operation. Expecting DFU_UPLOAD requests.
dfuERROR : 10 // An error has occurred. Awaiting the DFU_CLRSTATUS request.
} ;
this . flash _layout = { 'start_address' : 0 , 'total_size' : 0 , 'sectors' : [ ] } ;
} ;
STM32DFU _protocol . prototype . connect = function ( device , hex , options , callback ) {
var self = this ;
self . hex = hex ;
self . callback = callback ;
self . options = {
erase _chip : false
} ;
if ( options . erase _chip ) {
self . options . erase _chip = true ;
}
// reset and set some variables before we start
self . upload _time _start = new Date ( ) . getTime ( ) ;
self . verify _hex = [ ] ;
// reset progress bar to initial state
self . progress _bar _e = $ ( '.progress' ) ;
self . progress _bar _e . val ( 0 ) ;
self . progress _bar _e . removeClass ( 'valid invalid' ) ;
chrome . usb . getDevices ( device , function ( result ) {
if ( result . length ) {
console . log ( 'USB DFU detected with ID: ' + result [ 0 ] . device ) ;
self . openDevice ( result [ 0 ] ) ;
} else {
console . log ( 'USB DFU not found' ) ;
GUI . log ( 'USB DFU not found' ) ;
}
} ) ;
} ;
STM32DFU _protocol . prototype . checkChromeError = function ( ) {
if ( chrome . runtime . lastError ) {
if ( chrome . runtime . lastError . message )
console . log ( chrome . runtime . lastError . message ) ;
else
console . log ( chrome . runtime . lastError ) ;
return true ;
}
return false ;
}
STM32DFU _protocol . prototype . openDevice = function ( device ) {
var self = this ;
chrome . usb . openDevice ( device , function ( handle ) {
if ( self . checkChromeError ( ) ) {
console . log ( 'Failed to open USB device!' ) ;
GUI . log ( chrome . i18n . getMessage ( 'usbDeviceOpenFail' ) ) ;
if ( GUI . operating _system === 'Linux' ) {
GUI . log ( chrome . i18n . getMessage ( 'usbDeviceUdevNotice' ) ) ;
}
return ;
}
self . handle = handle ;
GUI . log ( chrome . i18n . getMessage ( 'usbDeviceOpened' , handle . handle . toString ( ) ) ) ;
console . log ( 'Device opened with Handle ID: ' + handle . handle ) ;
self . claimInterface ( 0 ) ;
} ) ;
} ;
STM32DFU _protocol . prototype . closeDevice = function ( ) {
var self = this ;
chrome . usb . closeDevice ( this . handle , function closed ( ) {
if ( self . checkChromeError ( ) ) {
console . log ( 'Failed to close USB device!' ) ;
GUI . log ( chrome . i18n . getMessage ( 'usbDeviceCloseFail' ) ) ;
}
GUI . log ( chrome . i18n . getMessage ( 'usbDeviceClosed' ) ) ;
console . log ( 'Device closed with Handle ID: ' + self . handle . handle ) ;
self . handle = null ;
} ) ;
} ;
STM32DFU _protocol . prototype . claimInterface = function ( interfaceNumber ) {
var self = this ;
chrome . usb . claimInterface ( this . handle , interfaceNumber , function claimed ( ) {
console . log ( 'Claimed interface: ' + interfaceNumber ) ;
self . upload _procedure ( 1 ) ;
} ) ;
} ;
STM32DFU _protocol . prototype . releaseInterface = function ( interfaceNumber ) {
var self = this ;
chrome . usb . releaseInterface ( this . handle , interfaceNumber , function released ( ) {
console . log ( 'Released interface: ' + interfaceNumber ) ;
self . closeDevice ( ) ;
} ) ;
} ;
STM32DFU _protocol . prototype . resetDevice = function ( callback ) {
chrome . usb . resetDevice ( this . handle , function ( result ) {
console . log ( 'Reset Device: ' + result ) ;
if ( callback ) callback ( ) ;
} ) ;
} ;
STM32DFU _protocol . prototype . getString = function ( index , callback ) {
var self = this ;
chrome . usb . controlTransfer ( self . handle , {
'direction' : 'in' ,
'recipient' : 'device' ,
'requestType' : 'standard' ,
'request' : 6 ,
'value' : 0x300 | index ,
'index' : 0 , // specifies language
'length' : 255 // max length to retreive
} , function ( result ) {
if ( self . checkChromeError ( ) ) {
console . log ( 'USB transfer failed! ' + result . resultCode ) ;
callback ( "" , result . resultCode ) ;
return ;
}
var view = new DataView ( result . data ) ;
var length = view . getUint8 ( 0 ) ;
var descriptor = "" ;
for ( var i = 2 ; i < length ; i += 2 ) {
var charCode = view . getUint16 ( i , true ) ;
descriptor += String . fromCharCode ( charCode ) ;
}
callback ( descriptor , result . resultCode ) ;
} ) ;
}
STM32DFU _protocol . prototype . getInterfaceDescriptor = function ( _interface , callback ) {
var self = this ;
chrome . usb . controlTransfer ( this . handle , {
'direction' : 'in' ,
'recipient' : 'device' ,
'requestType' : 'standard' ,
'request' : 6 ,
'value' : 0x200 ,
'index' : 0 ,
'length' : 18 + _interface * 9
} , function ( result ) {
if ( self . checkChromeError ( ) ) {
console . log ( 'USB transfer failed! ' + result . resultCode ) ;
callback ( { } , result . resultCode ) ;
return ;
}
var buf = new Uint8Array ( result . data , 9 + _interface * 9 ) ;
var descriptor = {
'bLength' : buf [ 0 ] ,
'bDescriptorType' : buf [ 1 ] ,
'bInterfaceNumber' : buf [ 2 ] ,
'bAlternateSetting' : buf [ 3 ] ,
'bNumEndpoints' : buf [ 4 ] ,
'bInterfaceClass' : buf [ 5 ] ,
'bInterfaceSubclass' : buf [ 6 ] ,
'bInterfaceProtocol' : buf [ 7 ] ,
'iInterface' : buf [ 8 ]
} ;
callback ( descriptor , result . resultCode ) ;
} ) ;
}
STM32DFU _protocol . prototype . getFlashInfo = function ( _interface , callback ) {
var self = this ;
self . getInterfaceDescriptor ( 0 , function ( descriptor , resultCode ) {
if ( resultCode ) {
callback ( { } , resultCode ) ;
return ;
}
self . getString ( descriptor . iInterface , function ( str , resultCode ) {
if ( resultCode ) {
callback ( { } , resultCode ) ;
return ;
}
// F303: "@Internal Flash /0x08000000/128*0002Kg"
// F40x: "@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg"
// F72x: "@Internal Flash /0x08000000/04*016Kg,01*64Kg,03*128Kg"
// F74x: "@Internal Flash /0x08000000/04*032Kg,01*128Kg,03*256Kg"
// split main into [location, start_addr, sectors]
var tmp0 = str . replace ( /[^\x20-\x7E]+/g , "" ) ;
var tmp1 = tmp0 . split ( '/' ) ;
if ( tmp1 . length != 3 || ! tmp1 [ 0 ] . startsWith ( "@" ) ) {
return null ;
}
var type = tmp1 [ 0 ] . trim ( ) . replace ( '@' , '' ) ;
var start _address = parseInt ( tmp1 [ 1 ] ) ;
// split sectors into array
var sectors = [ ] ;
var total _size = 0 ;
var tmp2 = tmp1 [ 2 ] . split ( ',' ) ;
if ( tmp2 . length < 1 ) {
callback ( { } , - 2 ) ;
return ;
}
for ( var i = 0 ; i < tmp2 . length ; i ++ ) {
// split into [num_pages, page_size]
var tmp3 = tmp2 [ i ] . split ( '*' ) ;
if ( tmp3 . length != 2 ) {
callback ( { } , - 3 ) ;
return ;
}
var num _pages = parseInt ( tmp3 [ 0 ] ) ;
var page _size = parseInt ( tmp3 [ 1 ] ) ;
if ( ! page _size ) {
callback ( { } , - 4 ) ;
return ;
}
var unit = tmp3 [ 1 ] . slice ( - 2 , - 1 ) ;
switch ( unit ) {
case 'M' :
page _size *= 1024 ; // fall through to K as well
case 'K' :
page _size *= 1024 ;
break ;
default :
callback ( { } , - 4 ) ;
return ;
}
sectors . push ( {
'num_pages' : num _pages ,
'start_address' : start _address + total _size ,
'page_size' : page _size ,
'total_size' : num _pages * page _size
} ) ;
total _size += num _pages * page _size ;
}
var flash = {
'type' : type ,
'start_address' : start _address ,
'sectors' : sectors ,
'total_size' : total _size
}
callback ( flash , resultCode ) ;
} ) ;
} ) ;
}
STM32DFU _protocol . prototype . controlTransfer = function ( direction , request , value , _interface , length , data , callback ) {
var self = this ;
if ( direction == 'in' ) {
// data is ignored
chrome . usb . controlTransfer ( this . handle , {
'direction' : 'in' ,
'recipient' : 'interface' ,
'requestType' : 'class' ,
'request' : request ,
'value' : value ,
'index' : _interface ,
'length' : length
} , function ( result ) {
if ( self . checkChromeError ( ) ) {
console . log ( 'USB transfer failed!' ) ;
}
if ( result . resultCode ) console . log ( result . resultCode ) ;
var buf = new Uint8Array ( result . data ) ;
callback ( buf , result . resultCode ) ;
} ) ;
} else {
// length is ignored
if ( data ) {
var arrayBuf = new ArrayBuffer ( data . length ) ;
var arrayBufView = new Uint8Array ( arrayBuf ) ;
arrayBufView . set ( data ) ;
} else {
var arrayBuf = new ArrayBuffer ( 0 ) ;
}
chrome . usb . controlTransfer ( this . handle , {
'direction' : 'out' ,
'recipient' : 'interface' ,
'requestType' : 'class' ,
'request' : request ,
'value' : value ,
'index' : _interface ,
'data' : arrayBuf
} , function ( result ) {
if ( self . checkChromeError ( ) ) {
console . log ( 'USB transfer failed!' ) ;
}
if ( result . resultCode ) console . log ( result . resultCode ) ;
callback ( result ) ;
} ) ;
}
} ;
// routine calling DFU_CLRSTATUS until device is in dfuIDLE state
STM32DFU _protocol . prototype . clearStatus = function ( callback ) {
var self = this ;
function check _status ( ) {
self . controlTransfer ( 'in' , self . request . GETSTATUS , 0 , 0 , 6 , 0 , function ( data ) {
if ( data [ 4 ] == self . state . dfuIDLE ) {
callback ( data ) ;
} else {
var delay = data [ 1 ] | ( data [ 2 ] << 8 ) | ( data [ 3 ] << 16 ) ;
setTimeout ( clear _status , delay ) ;
}
} ) ;
}
function clear _status ( ) {
self . controlTransfer ( 'out' , self . request . CLRSTATUS , 0 , 0 , 0 , 0 , check _status ) ;
}
check _status ( ) ;
} ;
STM32DFU _protocol . prototype . loadAddress = function ( address , callback ) {
var self = this ;
self . controlTransfer ( 'out' , self . request . DNLOAD , 0 , 0 , 0 , [ 0x21 , address , ( address >> 8 ) , ( address >> 16 ) , ( address >> 24 ) ] , function ( ) {
self . controlTransfer ( 'in' , self . request . GETSTATUS , 0 , 0 , 6 , 0 , function ( data ) {
if ( data [ 4 ] == self . state . dfuDNBUSY ) {
var delay = data [ 1 ] | ( data [ 2 ] << 8 ) | ( data [ 3 ] << 16 ) ;
setTimeout ( function ( ) {
self . controlTransfer ( 'in' , self . request . GETSTATUS , 0 , 0 , 6 , 0 , function ( data ) {
if ( data [ 4 ] == self . state . dfuDNLOAD _IDLE ) {
callback ( data ) ;
} else {
console . log ( 'Failed to execute address load' ) ;
self . upload _procedure ( 99 ) ;
}
} ) ;
} , delay ) ;
} else {
console . log ( 'Failed to request address load' ) ;
self . upload _procedure ( 99 ) ;
}
} ) ;
} ) ;
} ;
// first_array = usually hex_to_flash array
// second_array = usually verify_hex array
// result = true/false
STM32DFU _protocol . prototype . verify _flash = function ( first _array , second _array ) {
for ( var i = 0 ; i < first _array . length ; i ++ ) {
if ( first _array [ i ] != second _array [ i ] ) {
console . log ( 'Verification failed on byte: ' + i + ' expected: 0x' + first _array [ i ] . toString ( 16 ) + ' received: 0x' + second _array [ i ] . toString ( 16 ) ) ;
return false ;
}
}
console . log ( 'Verification successful, matching: ' + first _array . length + ' bytes' ) ;
return true ;
} ;
STM32DFU _protocol . prototype . upload _procedure = function ( step ) {
var self = this ;
switch ( step ) {
case 1 :
self . getFlashInfo ( 0 , function ( flash , resultCode ) {
if ( resultCode != 0 ) {
console . log ( 'Failed to detect chip flash info, resultCode: ' + resultCode ) ;
self . upload _procedure ( 99 ) ;
} else {
self . flash _layout = flash ;
self . available _flash _size = flash . total _size - ( self . hex . start _linear _address - flash . start _address ) ;
GUI . log ( chrome . i18n . getMessage ( 'dfu_device_flash_info' , ( flash . total _size / 1024 ) . toString ( ) ) ) ;
if ( self . hex . bytes _total > self . available _flash _size ) {
GUI . log ( chrome . i18n . getMessage ( 'dfu_error_image_size' ,
[ ( self . hex . bytes _total / 1024.0 ) . toFixed ( 1 ) ,
( self . available _flash _size / 1024.0 ) . toFixed ( 1 ) ] ) ) ;
self . upload _procedure ( 99 ) ;
} else {
self . clearStatus ( function ( ) {
self . upload _procedure ( 2 ) ;
} ) ;
}
}
} ) ;
break ;
case 2 :
// erase
if ( self . options . erase _chip ) {
// full chip erase
console . log ( 'Executing global chip erase' ) ;
$ ( 'span.progressLabel' ) . text ( 'Erasing ...' ) ;
self . controlTransfer ( 'out' , self . request . DNLOAD , 0 , 0 , 0 , [ 0x41 ] , function ( ) {
self . controlTransfer ( 'in' , self . request . GETSTATUS , 0 , 0 , 6 , 0 , function ( data ) {
if ( data [ 4 ] == self . state . dfuDNBUSY ) { // completely normal
var delay = data [ 1 ] | ( data [ 2 ] << 8 ) | ( data [ 3 ] << 16 ) ;
setTimeout ( function ( ) {
self . controlTransfer ( 'in' , self . request . GETSTATUS , 0 , 0 , 6 , 0 , function ( data ) {
if ( data [ 4 ] == self . state . dfuDNLOAD _IDLE ) {
self . upload _procedure ( 4 ) ;
} else {
console . log ( 'Failed to execute global chip erase' ) ;
self . upload _procedure ( 99 ) ;
}
} ) ;
} , delay ) ;
} else {
console . log ( 'Failed to initiate global chip erase' ) ;
self . upload _procedure ( 99 ) ;
}
} ) ;
} ) ;
} else {
// local erase
// find out which pages to erase
var erase _pages = [ ] ;
for ( var i = 0 ; i < self . flash _layout . sectors . length ; i ++ ) {
for ( var j = 0 ; j < self . flash _layout . sectors [ i ] . num _pages ; j ++ ) {
var page _start = self . flash _layout . sectors [ i ] . start _address + j * self . flash _layout . sectors [ i ] . page _size ;
var page _end = page _start + self . flash _layout . sectors [ i ] . page _size - 1 ;
for ( var k = 0 ; k < self . hex . data . length ; k ++ ) {
var starts _in _page = self . hex . data [ k ] . address >= page _start && self . hex . data [ k ] . address <= page _end ;
var end _address = self . hex . data [ k ] . address + self . hex . data [ k ] . bytes - 1 ;
var ends _in _page = end _address >= page _start && end _address <= page _end ;
var spans _page = self . hex . data [ k ] . address < page _start && end _address > page _end ;
if ( starts _in _page || ends _in _page || spans _page ) {
var idx = erase _pages . findIndex ( function ( element , index , array ) {
return element . sector == i && element . page == j ;
} ) ;
if ( idx == - 1 )
erase _pages . push ( { 'sector' : i , 'page' : j } ) ;
}
}
}
}
$ ( 'span.progressLabel' ) . text ( 'Erasing ...' ) ;
console . log ( 'Executing local chip erase' ) ;
var page = 0 ;
var total _erased = 0 ; // bytes
var erase _page = function ( ) {
var page _addr = erase _pages [ page ] . page * self . flash _layout . sectors [ erase _pages [ page ] . sector ] . page _size +
self . flash _layout . sectors [ erase _pages [ page ] . sector ] . start _address ;
var cmd = [ 0x41 , page _addr & 0xff , ( page _addr >> 8 ) & 0xff , ( page _addr >> 16 ) & 0xff , ( page _addr >> 24 ) & 0xff ] ;
total _erased += self . flash _layout . sectors [ erase _pages [ page ] . sector ] . page _size ;
console . log ( 'Erasing. sector ' + erase _pages [ page ] . sector +
', page ' + erase _pages [ page ] . page + ' @ 0x' + page _addr . toString ( 16 ) ) ;
self . controlTransfer ( 'out' , self . request . DNLOAD , 0 , 0 , 0 , cmd , function ( ) {
self . controlTransfer ( 'in' , self . request . GETSTATUS , 0 , 0 , 6 , 0 , function ( data ) {
if ( data [ 4 ] == self . state . dfuDNBUSY ) { // completely normal
var delay = data [ 1 ] | ( data [ 2 ] << 8 ) | ( data [ 3 ] << 16 ) ;
setTimeout ( function ( ) {
self . controlTransfer ( 'in' , self . request . GETSTATUS , 0 , 0 , 6 , 0 , function ( data ) {
if ( data [ 4 ] == self . state . dfuDNLOAD _IDLE ) {
// update progress bar
self . progress _bar _e . val ( ( page + 1 ) / erase _pages . length * 100 ) ;
page ++ ;
if ( page == erase _pages . length ) {
console . log ( "Erase: complete" ) ;
GUI . log ( chrome . i18n . getMessage ( 'dfu_erased_kilobytes' , ( total _erased / 1024 ) . toString ( ) ) ) ;
self . upload _procedure ( 4 ) ;
}
else
erase _page ( ) ;
} else {
console . log ( 'Failed to erase page 0x' + page _addr . toString ( 16 ) ) ;
self . upload _procedure ( 99 ) ;
}
} ) ;
} , delay ) ;
} else {
console . log ( 'Failed to initiate page erase, page 0x' + page _addr . toString ( 16 ) ) ;
self . upload _procedure ( 99 ) ;
}
} ) ;
} ) ;
} ;
// start
erase _page ( ) ;
}
break ;
case 4 :
// upload
// we dont need to clear the state as we are already using DFU_DNLOAD
console . log ( 'Writing data ...' ) ;
$ ( 'span.progressLabel' ) . text ( 'Flashing ...' ) ;
var blocks = self . hex . data . length - 1 ;
var flashing _block = 0 ;
var address = self . hex . data [ flashing _block ] . address ;
var bytes _flashed = 0 ;
var bytes _flashed _total = 0 ; // used for progress bar
var wBlockNum = 2 ; // required by DFU
var write = function ( ) {
if ( bytes _flashed < self . hex . data [ flashing _block ] . bytes ) {
var bytes _to _write = ( ( bytes _flashed + 2048 ) <= self . hex . data [ flashing _block ] . bytes ) ? 2048 : ( self . hex . data [ flashing _block ] . bytes - bytes _flashed ) ;
var data _to _flash = self . hex . data [ flashing _block ] . data . slice ( bytes _flashed , bytes _flashed + bytes _to _write ) ;
address += bytes _to _write ;
bytes _flashed += bytes _to _write ;
bytes _flashed _total += bytes _to _write ;
self . controlTransfer ( 'out' , self . request . DNLOAD , wBlockNum ++ , 0 , 0 , data _to _flash , function ( ) {
self . controlTransfer ( 'in' , self . request . GETSTATUS , 0 , 0 , 6 , 0 , function ( data ) {
if ( data [ 4 ] == self . state . dfuDNBUSY ) {
var delay = data [ 1 ] | ( data [ 2 ] << 8 ) | ( data [ 3 ] << 16 ) ;
setTimeout ( function ( ) {
self . controlTransfer ( 'in' , self . request . GETSTATUS , 0 , 0 , 6 , 0 , function ( data ) {
if ( data [ 4 ] == self . state . dfuDNLOAD _IDLE ) {
// update progress bar
self . progress _bar _e . val ( bytes _flashed _total / ( self . hex . bytes _total * 2 ) * 100 ) ;
// flash another page
write ( ) ;
} else {
console . log ( 'Failed to write ' + bytes _to _write + 'bytes to 0x' + address . toString ( 16 ) ) ;
self . upload _procedure ( 99 ) ;
}
} ) ;
} , delay ) ;
} else {
console . log ( 'Failed to initiate write ' + bytes _to _write + 'bytes to 0x' + address . toString ( 16 ) ) ;
self . upload _procedure ( 99 ) ;
}
} ) ;
} )
} else {
if ( flashing _block < blocks ) {
// move to another block
flashing _block ++ ;
address = self . hex . data [ flashing _block ] . address ;
bytes _flashed = 0 ;
wBlockNum = 2 ;
self . loadAddress ( address , write ) ;
} else {
// all blocks flashed
console . log ( 'Writing: done' ) ;
// proceed to next step
self . upload _procedure ( 5 ) ;
}
}
}
// start
self . loadAddress ( address , write ) ;
break ;
case 5 :
// verify
console . log ( 'Verifying data ...' ) ;
$ ( 'span.progressLabel' ) . text ( 'Verifying ...' ) ;
var blocks = self . hex . data . length - 1 ;
var reading _block = 0 ;
var address = self . hex . data [ reading _block ] . address ;
var bytes _verified = 0 ;
var bytes _verified _total = 0 ; // used for progress bar
var wBlockNum = 2 ; // required by DFU
// initialize arrays
for ( var i = 0 ; i <= blocks ; i ++ ) {
self . verify _hex . push ( [ ] ) ;
}
// start
self . clearStatus ( function ( ) {
self . loadAddress ( address , function ( ) {
self . clearStatus ( read ) ;
} ) ;
} ) ;
var read = function ( ) {
if ( bytes _verified < self . hex . data [ reading _block ] . bytes ) {
var bytes _to _read = ( ( bytes _verified + 2048 ) <= self . hex . data [ reading _block ] . bytes ) ? 2048 : ( self . hex . data [ reading _block ] . bytes - bytes _verified ) ;
self . controlTransfer ( 'in' , self . request . UPLOAD , wBlockNum ++ , 0 , bytes _to _read , 0 , function ( data , code ) {
for ( var i = 0 ; i < data . length ; i ++ ) {
self . verify _hex [ reading _block ] . push ( data [ i ] ) ;
}
address += bytes _to _read ;
bytes _verified += bytes _to _read ;
bytes _verified _total += bytes _to _read ;
// update progress bar
self . progress _bar _e . val ( ( self . hex . bytes _total + bytes _verified _total ) / ( self . hex . bytes _total * 2 ) * 100 ) ;
// verify another page
read ( ) ;
} ) ;
} else {
if ( reading _block < blocks ) {
// move to another block
reading _block ++ ;
address = self . hex . data [ reading _block ] . address ;
bytes _verified = 0 ;
wBlockNum = 2 ;
self . clearStatus ( function ( ) {
self . loadAddress ( address , function ( ) {
self . clearStatus ( read ) ;
} ) ;
} ) ;
} else {
// all blocks read, verify
var verify = true ;
for ( var i = 0 ; i <= blocks ; i ++ ) {
verify = self . verify _flash ( self . hex . data [ i ] . data , self . verify _hex [ i ] ) ;
if ( ! verify ) break ;
}
if ( verify ) {
console . log ( 'Programming: SUCCESSFUL' ) ;
$ ( 'span.progressLabel' ) . text ( 'Programming: SUCCESSFUL' ) ;
googleAnalytics . sendEvent ( 'Flashing' , 'Programming' , 'success' ) ;
// update progress bar
self . progress _bar _e . addClass ( 'valid' ) ;
// proceed to next step
self . upload _procedure ( 6 ) ;
} else {
console . log ( 'Programming: FAILED' ) ;
$ ( 'span.progressLabel' ) . text ( 'Programming: FAILED' ) ;
googleAnalytics . sendEvent ( 'Flashing' , 'Programming' , 'fail' ) ;
// update progress bar
self . progress _bar _e . addClass ( 'invalid' ) ;
// disconnect
self . upload _procedure ( 99 ) ;
}
}
}
}
break ;
case 6 :
// jump to application code
var address = self . hex . data [ 0 ] . address ;
self . clearStatus ( function ( ) {
self . loadAddress ( address , leave ) ;
} ) ;
var leave = function ( ) {
self . controlTransfer ( 'out' , self . request . DNLOAD , 0 , 0 , 0 , 0 , function ( ) {
self . controlTransfer ( 'in' , self . request . GETSTATUS , 0 , 0 , 6 , 0 , function ( data ) {
self . upload _procedure ( 99 ) ;
} ) ;
} ) ;
}
break ;
case 99 :
// cleanup
self . releaseInterface ( 0 ) ;
GUI . connect _lock = false ;
var timeSpent = new Date ( ) . getTime ( ) - self . upload _time _start ;
console . log ( 'Script finished after: ' + ( timeSpent / 1000 ) + ' seconds' ) ;
if ( self . callback ) self . callback ( ) ;
break ;
}
} ;
// initialize object
var STM32DFU = new STM32DFU _protocol ( ) ;