@@ -351,7 +351,20 @@ function* loadFirmware(
351351 'Expected metadata to be v2.x' ,
352352 ) ;
353353
354- const firmware = new Uint8Array ( firmwareBase . length + 4 ) ;
354+ const [ checksumFunc , checksumSize ] = ( function ( ) {
355+ switch ( metadata [ 'checksum-type' ] ) {
356+ case 'sum' :
357+ return [ sumComplement32 , 4 ] ;
358+ case 'crc32' :
359+ return [ crc32 , 4 ] ;
360+ case 'none' :
361+ return [ null , 0 ] ;
362+ default :
363+ return [ undefined , 0 ] ;
364+ }
365+ } ) ( ) ;
366+
367+ const firmware = new Uint8Array ( firmwareBase . length + checksumSize ) ;
355368 const firmwareView = new DataView ( firmware . buffer ) ;
356369
357370 firmware . set ( firmwareBase ) ;
@@ -361,22 +374,7 @@ function* loadFirmware(
361374 firmware . set ( encodeHubName ( hubName , metadata ) , metadata [ 'hub-name-offset' ] ) ;
362375 }
363376
364- const checksum = ( function ( ) {
365- switch ( metadata [ 'checksum-type' ] ) {
366- case 'sum' :
367- return sumComplement32 (
368- firmwareIterator ( firmwareView , metadata [ 'checksum-size' ] ) ,
369- ) ;
370- case 'crc32' :
371- return crc32 ( firmwareIterator ( firmwareView , metadata [ 'checksum-size' ] ) ) ;
372- case 'none' :
373- return null ;
374- default :
375- return undefined ;
376- }
377- } ) ( ) ;
378-
379- if ( checksum === undefined ) {
377+ if ( checksumFunc === undefined ) {
380378 // FIXME: we should return error/throw instead
381379 yield * put (
382380 didFailToFinish (
@@ -391,8 +389,12 @@ function* loadFirmware(
391389 throw new Error ( 'unreachable' ) ;
392390 }
393391
394- if ( checksum !== null ) {
395- firmwareView . setUint32 ( firmwareBase . length , checksum , true ) ;
392+ if ( checksumFunc !== null ) {
393+ firmwareView . setUint32 (
394+ firmwareBase . length ,
395+ checksumFunc ( firmwareIterator ( firmwareView , metadata [ 'checksum-size' ] ) ) ,
396+ true ,
397+ ) ;
396398 }
397399
398400 return { firmware, deviceId : metadata [ 'device-id' ] } ;
@@ -1219,73 +1221,114 @@ function* handleFlashEV3(action: ReturnType<typeof firmwareFlashEV3>): Generator
12191221
12201222 defined ( version ) ;
12211223
1222- console . debug (
1223- `EV3 bootloader version: ${ version . getUint32 (
1224- 0 ,
1225- true ,
1226- ) } , HW version: ${ version . getUint32 ( 4 , true ) } `,
1227- ) ;
1224+ // For reasons that we do not currently understand, some EV3s do not return
1225+ // anything to our GetVersion command. We don't actually use the version
1226+ // for anything so we will just ignore this error.
1227+ try {
1228+ console . debug (
1229+ `EV3 bootloader version: ${ version . getUint32 (
1230+ 0 ,
1231+ true ,
1232+ ) } , HW version: ${ version . getUint32 ( 4 , true ) } `,
1233+ ) ;
1234+ } catch ( err ) {
1235+ console . error ( `Failed to parse ev3 version response: ${ ensureError ( err ) } ` ) ;
1236+ }
12281237
12291238 // FIXME: should be called much earlier.
12301239 yield * put ( didStart ( ) ) ;
12311240
12321241 const sectorSize = 64 * 1024 ; // flash memory sector size
12331242 const maxPayloadSize = 1018 ; // maximum payload size for EV3 commands
12341243
1235- for ( let i = 0 ; i < action . firmware . byteLength ; i += sectorSize ) {
1236- const sectorData = action . firmware . slice ( i , i + sectorSize ) ;
1237- assert ( sectorData . byteLength <= sectorSize , 'sector data too large' ) ;
1244+ console . info ( `Firmware size: ${ action . firmware . byteLength } bytes` ) ;
1245+
1246+ // Apparently, erasing a span of the flash creates some sort of record in
1247+ // the EV3, and we can only write within a given erase span. Writes that
1248+ // cross the boundary will hang. To avoid this, we erase the whole firmware
1249+ // range at once.
1250+ const numSectors = Math . floor ( action . firmware . byteLength / sectorSize ) ;
1251+ assert (
1252+ action . firmware . byteLength === numSectors * sectorSize ,
1253+ 'Firmware size is required to be a round multiple of sector size.' ,
1254+ ) ;
1255+
1256+ const erasePayload = new DataView ( new ArrayBuffer ( 8 ) ) ;
1257+ erasePayload . setUint32 ( 0 , 0 , true ) ; // start address
1258+ erasePayload . setUint32 ( 4 , numSectors * sectorSize , true ) ; // size
1259+ console . debug (
1260+ `Erasing bytes [0x${ ( 0 ) . toString ( 16 ) } , 0x${ ( numSectors * sectorSize ) . toString (
1261+ 16 ,
1262+ ) } )`,
1263+ ) ;
1264+
1265+ yield * put (
1266+ alertsShowAlert (
1267+ 'firmware' ,
1268+ 'flashProgress' ,
1269+ {
1270+ action : 'erase' ,
1271+ progress : undefined ,
1272+ } ,
1273+ firmwareBleProgressToastId ,
1274+ true ,
1275+ ) ,
1276+ ) ;
1277+
1278+ const [ , eraseError ] = yield * sendCommand (
1279+ 0xf0 ,
1280+ new Uint8Array ( erasePayload . buffer ) ,
1281+ ) ;
1282+
1283+ if ( eraseError ) {
1284+ yield * put (
1285+ alertsShowAlert ( 'alerts' , 'unexpectedError' , {
1286+ error : eraseError ,
1287+ } ) ,
1288+ ) ;
1289+ // FIXME: should have a better error reason
1290+ yield * put ( didFailToFinish ( FailToFinishReasonType . Unknown , eraseError ) ) ;
1291+ yield * put ( firmwareDidFailToFlashEV3 ( ) ) ;
1292+ yield * cleanup ( ) ;
1293+ return ;
1294+ }
12381295
1239- const erasePayload = new DataView ( new ArrayBuffer ( 8 ) ) ;
1240- erasePayload . setUint32 ( 0 , i , true ) ;
1241- erasePayload . setUint32 ( 4 , sectorData . byteLength , true ) ;
1242- const [ , eraseError ] = yield * sendCommand (
1243- 0xf0 ,
1244- new Uint8Array ( erasePayload . buffer ) ,
1296+ // If we don't write an exact multiple of the sector size, the flash process
1297+ // will hang on the last write we send.
1298+ const firmware = action . firmware ;
1299+ for ( let i = 0 ; i < firmware . byteLength ; i += maxPayloadSize ) {
1300+ const writeLength = Math . min ( maxPayloadSize , firmware . byteLength - i ) ;
1301+ const payload = firmware . slice ( i , i + writeLength ) ;
1302+ console . debug (
1303+ `Programming bytes [0x${ i . toString ( 16 ) } , 0x${ ( i + writeLength ) . toString (
1304+ 16 ,
1305+ ) } )`,
12451306 ) ;
12461307
1247- if ( eraseError ) {
1308+ const [ , sendError ] = yield * sendCommand ( 0xf2 , new Uint8Array ( payload ) ) ;
1309+ if ( sendError ) {
12481310 yield * put (
12491311 alertsShowAlert ( 'alerts' , 'unexpectedError' , {
1250- error : eraseError ,
1312+ error : sendError ,
12511313 } ) ,
12521314 ) ;
12531315 // FIXME: should have a better error reason
1254- yield * put ( didFailToFinish ( FailToFinishReasonType . Unknown , eraseError ) ) ;
1316+ yield * put ( didFailToFinish ( FailToFinishReasonType . Unknown , sendError ) ) ;
12551317 yield * put ( firmwareDidFailToFlashEV3 ( ) ) ;
12561318 yield * cleanup ( ) ;
12571319 return ;
12581320 }
12591321
1260- for ( let j = 0 ; j < sectorData . byteLength ; j += maxPayloadSize ) {
1261- const payload = sectorData . slice ( j , j + maxPayloadSize ) ;
1262-
1263- const [ , sendError ] = yield * sendCommand ( 0xf2 , new Uint8Array ( payload ) ) ;
1264- if ( sendError ) {
1265- yield * put (
1266- alertsShowAlert ( 'alerts' , 'unexpectedError' , {
1267- error : sendError ,
1268- } ) ,
1269- ) ;
1270- // FIXME: should have a better error reason
1271- yield * put ( didFailToFinish ( FailToFinishReasonType . Unknown , sendError ) ) ;
1272- yield * put ( firmwareDidFailToFlashEV3 ( ) ) ;
1273- yield * cleanup ( ) ;
1274- return ;
1275- }
1276- }
1277-
1278- yield * put (
1279- didProgress ( ( i + sectorData . byteLength ) / action . firmware . byteLength ) ,
1280- ) ;
1322+ const progress = ( i + writeLength ) / firmware . byteLength ;
1323+ yield * put ( didProgress ( progress ) ) ;
12811324
12821325 yield * put (
12831326 alertsShowAlert (
12841327 'firmware' ,
12851328 'flashProgress' ,
12861329 {
12871330 action : 'flash' ,
1288- progress : ( i + sectorData . byteLength ) / action . firmware . byteLength ,
1331+ progress : progress ,
12891332 } ,
12901333 firmwareBleProgressToastId ,
12911334 true ,
0 commit comments