From b9d5491a7d7da938d2846cb6e9e73d106dece2ea Mon Sep 17 00:00:00 2001 From: keeramis Date: Tue, 15 Aug 2023 21:47:07 +0530 Subject: [PATCH 1/4] Add support for a progress callback --- src/dfu-device.js | 7 ++++--- src/dfu.js | 27 ++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/dfu-device.js b/src/dfu-device.js index 3cad9949..e912d5d1 100644 --- a/src/dfu-device.js +++ b/src/dfu-device.js @@ -5,12 +5,13 @@ const DfuDevice = (base) => class extends base { * @param {Number} altSetting The interface alternate setting. * @param {Buffer} buffer The binary firmware data to be flashed. * @param {Number} addr The starting address where the firmware will be written. - * @param {Object} options Optional options for the flashing process. + * @param {Function} progress User's callback function to log progress of the flashing process. + * @param {Object} options Optional options for the flashing process (noErase, leave). * @returns {Promise} A Promise that resolves when the firmware is successfully flashed. */ - async writeOverDfu(altSetting, buffer, addr, options) { + async writeOverDfu({ altSetting, buffer, addr, options, progress }) { await this._dfu.setAltSetting(altSetting); - await this._dfu.doDownload(addr, buffer, options); + await this._dfu.doDownload(addr, buffer, options, progress); } }; diff --git a/src/dfu.js b/src/dfu.js index 1b4c39a2..e516c557 100644 --- a/src/dfu.js +++ b/src/dfu.js @@ -252,7 +252,7 @@ class Dfu { * @param {object} options - Options for the download process. * @return {Promise} */ - async doDownload(startAddr, data, options = { noErase: false, leave: false }) { + async doDownload(startAddr, data, options = { noErase: false, leave: false }, progress) { if (!this._memoryInfo || !this._memoryInfo.segments) { throw new Error('No memory map available'); } @@ -265,11 +265,14 @@ class Dfu { if (!options.noErase) { this._log.info('Erasing DFU device memory'); - await this._erase(startAddress, expectedSize); + await this._erase(startAddress, expectedSize, progress); } this._log.info('Copying binary data to DFU device startAddress=' + startAddress + ' total_expected_size=' + expectedSize); + if (progress) { + progress({ event: 'start-download', bytes: expectedSize }); + } let bytesSent = 0; let address = startAddress; while (bytesSent < expectedSize) { @@ -289,13 +292,22 @@ class Dfu { } if (dfuStatus.status !== DfuDeviceStatus.OK) { + if (progress) { + progress({ event: 'failed-download' }); + } throw new Error(`DFU DOWNLOAD failed state=${dfuStatus.state}, status=${dfuStatus.status}`); } this._log.trace('Wrote ' + chunkSize + ' bytes'); bytesSent += chunkSize; + if (progress) { + progress({ event: 'downloaded', bytes: chunkSize }); + } } this._log.info(`Wrote ${bytesSent} bytes total`); + if (progress) { + progress({ event: 'complete-download', bytes: bytesSent }); + } if (options.leave) { this._log.info('Manifesting new firmware'); @@ -581,7 +593,7 @@ class Dfu { * @param {number} length The length of the memory range to be erased in bytes. * @throws {Error} If the start address or the length is outside the memory map bounds, or if erasing fails. */ - async _erase(startAddr, length) { + async _erase(startAddr, length, progress) { let segment = this._getSegment(startAddr); let addr = this._getSectorStart(startAddr, segment); const endAddr = this._getSectorEnd(startAddr + length - 1); @@ -589,6 +601,9 @@ class Dfu { let bytesErased = 0; const bytesToErase = endAddr - addr; + if (progress) { + progress({ event: 'start-erase', bytes: bytesToErase }); + } while (addr < endAddr) { if (segment.end <= addr) { segment = this._getSegment(addr); @@ -605,6 +620,12 @@ class Dfu { await this._dfuseCommand(DfuseCommand.DFUSE_COMMAND_ERASE, sectorAddr); addr = sectorAddr + segment.sectorSize; bytesErased += segment.sectorSize; + if (progress) { + progress({ event: 'erased', bytes: segment.sectorSize }); + } + } + if (progress) { + progress({ event: 'complete-erase', bytes: bytesErased }); } } From a05d9e90f7e1e0a39c03c273d648692fa3494e73 Mon Sep 17 00:00:00 2001 From: keeramis Date: Thu, 17 Aug 2023 01:16:52 +0530 Subject: [PATCH 2/4] Add progress events to updateFirmware --- src/device.js | 15 ++++++++++++++- src/dfu.js | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/device.js b/src/device.js index 8ebe599a..74a357f6 100644 --- a/src/device.js +++ b/src/device.js @@ -366,19 +366,32 @@ class Device extends DeviceBase { * @param {Number} [options.timeout] Timeout (milliseconds). * @return {Promise} */ - async updateFirmware(data, { timeout = DEFAULT_FIRMWARE_UPDATE_TIMEOUT } = {}) { + async updateFirmware(data, { timeout = DEFAULT_FIRMWARE_UPDATE_TIMEOUT } = {}, progress) { if (!data.length) { throw new RangeError('Invalid firmware size'); } return this.timeout(timeout, async (s) => { + if (progress) { + progress({ event: 'start-erase', bytes: data.length }); + } const { chunkSize } = await s.sendRequest(Request.START_FIRMWARE_UPDATE, { size: data.length }); + if (progress) { + progress({ event: 'complete-erase', bytes: data.length }); + progress({ event: 'start-download', bytes: data.length }); + } let offs = 0; while (offs < data.length) { const n = Math.min(chunkSize, data.length - offs); await s.sendRequest(Request.FIRMWARE_UPDATE_DATA, { data: data.slice(offs, offs + n) }); + if (progress) { + progress({ event: 'downloaded', bytes: n }); + } offs += n; } await s.sendRequest(Request.FINISH_FIRMWARE_UPDATE, { validateOnly: false }); + if (progress) { + progress({ event: 'complete-download', bytes: data.length }); + } }); } diff --git a/src/dfu.js b/src/dfu.js index e516c557..ea623cb2 100644 --- a/src/dfu.js +++ b/src/dfu.js @@ -249,7 +249,8 @@ class Dfu { * * @param {number} startAddr - The starting address to write the data. * @param {Buffer} data - The binary data to write. - * @param {object} options - Options for the download process. + * @param {object} options - Options for the download process. (noErase, leave) + * @param {function} progress - Callback function used to log progress. * @return {Promise} */ async doDownload(startAddr, data, options = { noErase: false, leave: false }, progress) { From 4e112a21dec200c2983be695dd9301980096dc48 Mon Sep 17 00:00:00 2001 From: keeramis Date: Thu, 17 Aug 2023 18:42:05 +0530 Subject: [PATCH 3/4] Remove 'complete-erase' event --- src/device.js | 2 +- src/dfu.js | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/device.js b/src/device.js index 74a357f6..aab91233 100644 --- a/src/device.js +++ b/src/device.js @@ -376,7 +376,7 @@ class Device extends DeviceBase { } const { chunkSize } = await s.sendRequest(Request.START_FIRMWARE_UPDATE, { size: data.length }); if (progress) { - progress({ event: 'complete-erase', bytes: data.length }); + progress({ event: 'erased', bytes: data.length }); progress({ event: 'start-download', bytes: data.length }); } let offs = 0; diff --git a/src/dfu.js b/src/dfu.js index ea623cb2..9869aead 100644 --- a/src/dfu.js +++ b/src/dfu.js @@ -625,9 +625,6 @@ class Dfu { progress({ event: 'erased', bytes: segment.sectorSize }); } } - if (progress) { - progress({ event: 'complete-erase', bytes: bytesErased }); - } } async _getStringDescriptor(index) { From e5c3c508ce9be72b49bc3fbb1a6c9ec3e5b3f812 Mon Sep 17 00:00:00 2001 From: Julien Vanier Date: Mon, 21 Aug 2023 09:24:31 -0400 Subject: [PATCH 4/4] Make interface between DFU and control request flash consistent --- src/device.js | 3 ++- src/dfu-device.js | 16 +++++++++------- src/dfu.js | 20 +++++++++++++------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/device.js b/src/device.js index aab91233..0949f901 100644 --- a/src/device.js +++ b/src/device.js @@ -364,9 +364,10 @@ class Device extends DeviceBase { * @param {Buffer} data Firmware data. * @param {Object} [options] Options. * @param {Number} [options.timeout] Timeout (milliseconds). + * @param {Function} [options.progress] User's callback function to log progress of the flashing process. * @return {Promise} */ - async updateFirmware(data, { timeout = DEFAULT_FIRMWARE_UPDATE_TIMEOUT } = {}, progress) { + async updateFirmware(data, { timeout = DEFAULT_FIRMWARE_UPDATE_TIMEOUT, progress } = {}) { if (!data.length) { throw new RangeError('Invalid firmware size'); } diff --git a/src/dfu-device.js b/src/dfu-device.js index e912d5d1..0f46a8bb 100644 --- a/src/dfu-device.js +++ b/src/dfu-device.js @@ -2,16 +2,18 @@ const DfuDevice = (base) => class extends base { /** * Flashes the firmware over DFU interface. * - * @param {Number} altSetting The interface alternate setting. - * @param {Buffer} buffer The binary firmware data to be flashed. - * @param {Number} addr The starting address where the firmware will be written. - * @param {Function} progress User's callback function to log progress of the flashing process. - * @param {Object} options Optional options for the flashing process (noErase, leave). + * @param {Buffer} data The binary firmware data to be flashed. + * @param {Object} options Options. + * @param {Number} options.altSetting The interface alternate setting. + * @param {Number} options.startAddr The starting address where the firmware will be written. + * @param {boolean} [options.noErase] - Skip erasing the device memory. + * @param {boolean} [options.leave] - Leave DFU mode after download. + * @param {Function} [options.progress] User's callback function to log progress of the flashing process. * @returns {Promise} A Promise that resolves when the firmware is successfully flashed. */ - async writeOverDfu({ altSetting, buffer, addr, options, progress }) { + async writeOverDfu(data, { altSetting, startAddr, noErase, leave, progress }) { await this._dfu.setAltSetting(altSetting); - await this._dfu.doDownload(addr, buffer, options, progress); + await this._dfu.doDownload({ startAddr, data, noErase, leave, progress }); } }; diff --git a/src/dfu.js b/src/dfu.js index 9869aead..38f7af96 100644 --- a/src/dfu.js +++ b/src/dfu.js @@ -247,13 +247,15 @@ class Dfu { /** * Perform DFU download of binary data to the device. * - * @param {number} startAddr - The starting address to write the data. - * @param {Buffer} data - The binary data to write. - * @param {object} options - Options for the download process. (noErase, leave) - * @param {function} progress - Callback function used to log progress. + * @param {Object} options Options. + * @param {number} options.startAddr - The starting address to write the data. + * @param {Buffer} options.data - The binary data to write. + * @param {boolean} [options.noErase] - Skip erasing the device memory. + * @param {boolean} [options.leave] - Leave DFU mode after download. + * @param {function} [options.progress] - Callback function used to log progress. * @return {Promise} */ - async doDownload(startAddr, data, options = { noErase: false, leave: false }, progress) { + async doDownload({ startAddr, data, noErase, leave, progress }) { if (!this._memoryInfo || !this._memoryInfo.segments) { throw new Error('No memory map available'); } @@ -264,7 +266,7 @@ class Dfu { } const expectedSize = data.byteLength; - if (!options.noErase) { + if (!noErase) { this._log.info('Erasing DFU device memory'); await this._erase(startAddress, expectedSize, progress); } @@ -310,7 +312,7 @@ class Dfu { progress({ event: 'complete-download', bytes: bytesSent }); } - if (options.leave) { + if (leave) { this._log.info('Manifesting new firmware'); try { await this.leave(); @@ -612,6 +614,10 @@ class Dfu { if (!segment.erasable) { // Skip over the non-erasable section bytesErased = Math.min(bytesErased + segment.end - addr, bytesToErase); + if (progress) { + // include a progress event for the skipped section to ensure total matches + progress({ event: 'erased', bytes: segment.end - addr }); + } addr = segment.end; continue; }