tasmota-webserial-esptool 7.0.1 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/const.js +1 -1
- package/dist/esp_loader.d.ts +2 -2
- package/dist/esp_loader.js +127 -130
- package/dist/struct.js +9 -23
- package/dist/util.js +4 -4
- package/dist/web/index.js +1 -1
- package/eslint.config.js +22 -0
- package/js/modules/esptool.js +1 -1
- package/js/script.js +1 -1
- package/package.json +12 -3
- package/src/const.ts +3 -1
- package/src/esp_loader.ts +137 -145
- package/src/struct.ts +18 -25
- package/src/util.ts +4 -4
- package/.devcontainer/devcontainer.json +0 -22
- package/.github/dependabot.yml +0 -10
- package/.github/workflows/build_upload.yml +0 -75
- package/.github/workflows/ci.yml +0 -30
- package/CHANGELOG_CHIP_VARIANT.md +0 -169
- package/CHIP_VARIANT_SUPPORT.md +0 -184
- package/READ_FLASH_FEATURE.md +0 -130
package/dist/const.js
CHANGED
|
@@ -307,7 +307,7 @@ export const FLASH_READ_TIMEOUT = 100; // timeout for reading flash in ms
|
|
|
307
307
|
* Scales timeouts which are size-specific
|
|
308
308
|
*/
|
|
309
309
|
export const timeoutPerMb = (secondsPerMb, sizeBytes) => {
|
|
310
|
-
|
|
310
|
+
const result = Math.floor(secondsPerMb * (sizeBytes / 0x1e6));
|
|
311
311
|
if (result < DEFAULT_TIMEOUT) {
|
|
312
312
|
return DEFAULT_TIMEOUT;
|
|
313
313
|
}
|
package/dist/esp_loader.d.ts
CHANGED
|
@@ -132,7 +132,7 @@ export declare class ESPLoader extends EventTarget {
|
|
|
132
132
|
* @name flashDeflBegin
|
|
133
133
|
*
|
|
134
134
|
*/
|
|
135
|
-
flashDeflBegin(size?: number, compressedSize?: number, offset?: number
|
|
135
|
+
flashDeflBegin(size?: number, compressedSize?: number, offset?: number): Promise<number>;
|
|
136
136
|
flashFinish(): Promise<void>;
|
|
137
137
|
flashDeflFinish(): Promise<void>;
|
|
138
138
|
getBootloaderOffset(): number;
|
|
@@ -199,7 +199,7 @@ declare class EspStubLoader extends ESPLoader {
|
|
|
199
199
|
* @name memBegin (592)
|
|
200
200
|
* Start downloading an application image to RAM
|
|
201
201
|
*/
|
|
202
|
-
memBegin(size: number,
|
|
202
|
+
memBegin(size: number, _blocks: number, _blocksize: number, offset: number): Promise<[number, number[]]>;
|
|
203
203
|
/**
|
|
204
204
|
* @name getEraseSize
|
|
205
205
|
* depending on flash chip model the erase may take this long (maybe longer!)
|
package/dist/esp_loader.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, CHIP_FAMILY_ESP32C2, CHIP_FAMILY_ESP32C3, CHIP_FAMILY_ESP32C5, CHIP_FAMILY_ESP32C6, CHIP_FAMILY_ESP32C61, CHIP_FAMILY_ESP32H2, CHIP_FAMILY_ESP32H4, CHIP_FAMILY_ESP32H21, CHIP_FAMILY_ESP32P4, CHIP_FAMILY_ESP32S31, CHIP_FAMILY_ESP8266, MAX_TIMEOUT, DEFAULT_TIMEOUT, ERASE_REGION_TIMEOUT_PER_MB, ESP_CHANGE_BAUDRATE, ESP_CHECKSUM_MAGIC, ESP_FLASH_BEGIN, ESP_FLASH_DATA, ESP_FLASH_END, ESP_MEM_BEGIN, ESP_MEM_DATA, ESP_MEM_END, ESP_READ_REG, ESP_WRITE_REG, ESP_SPI_ATTACH, ESP_SYNC, ESP_GET_SECURITY_INFO, FLASH_SECTOR_SIZE, FLASH_WRITE_SIZE, STUB_FLASH_WRITE_SIZE, MEM_END_ROM_TIMEOUT, ROM_INVALID_RECV_MSG, SYNC_PACKET, SYNC_TIMEOUT, USB_RAM_BLOCK, ESP_ERASE_FLASH, ESP_READ_FLASH, CHIP_ERASE_TIMEOUT, FLASH_READ_TIMEOUT, timeoutPerMb, ESP_ROM_BAUD, USB_JTAG_SERIAL_PID, ESP_FLASH_DEFL_BEGIN, ESP_FLASH_DEFL_DATA, ESP_FLASH_DEFL_END, getSpiFlashAddresses, DETECTED_FLASH_SIZES, CHIP_DETECT_MAGIC_REG_ADDR, CHIP_DETECT_MAGIC_VALUES, CHIP_ID_TO_INFO, ESP32P4_EFUSE_BLOCK1_ADDR, SlipReadError, } from "./const";
|
|
3
3
|
import { getStubCode } from "./stubs";
|
|
4
4
|
import { hexFormatter, sleep, slipEncode, toHex } from "./util";
|
|
5
|
-
// @ts-
|
|
5
|
+
// @ts-expect-error pako ESM module doesn't have proper type definitions
|
|
6
6
|
import { deflate } from "pako/dist/pako.esm.mjs";
|
|
7
7
|
import { pack, unpack } from "./struct";
|
|
8
8
|
export class ESPLoader extends EventTarget {
|
|
@@ -102,8 +102,8 @@ export class ESPLoader extends EventTarget {
|
|
|
102
102
|
// Detect chip type
|
|
103
103
|
await this.detectChip();
|
|
104
104
|
// Read the OTP data for this chip and store into this.efuses array
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
const FlAddr = getSpiFlashAddresses(this.getChipFamily());
|
|
106
|
+
const AddrMAC = FlAddr.macFuse;
|
|
107
107
|
for (let i = 0; i < 4; i++) {
|
|
108
108
|
this._efuses[i] = await this.readRegister(AddrMAC + 4 * i);
|
|
109
109
|
}
|
|
@@ -140,9 +140,9 @@ export class ESPLoader extends EventTarget {
|
|
|
140
140
|
}
|
|
141
141
|
this.logger.debug(`Unknown IMAGE_CHIP_ID: ${chipId}, falling back to magic value detection`);
|
|
142
142
|
}
|
|
143
|
-
catch (
|
|
143
|
+
catch (error) {
|
|
144
144
|
// GET_SECURITY_INFO not supported, fall back to magic value detection
|
|
145
|
-
this.logger.debug(`GET_SECURITY_INFO failed, using magic value detection: ${
|
|
145
|
+
this.logger.debug(`GET_SECURITY_INFO failed, using magic value detection: ${error}`);
|
|
146
146
|
// Clear input buffer and re-sync to recover from failed command
|
|
147
147
|
this._inputBuffer.length = 0;
|
|
148
148
|
await sleep(SYNC_TIMEOUT);
|
|
@@ -155,8 +155,8 @@ export class ESPLoader extends EventTarget {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
// Fallback: Use magic value detection for ESP8266, ESP32, ESP32-S2, and ESP32-P4 RC versions
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
const chipMagicValue = await this.readRegister(CHIP_DETECT_MAGIC_REG_ADDR);
|
|
159
|
+
const chip = CHIP_DETECT_MAGIC_VALUES[chipMagicValue >>> 0];
|
|
160
160
|
if (chip === undefined) {
|
|
161
161
|
throw new Error(`Unknown Chip: Hex: ${toHex(chipMagicValue >>> 0, 8).toLowerCase()} Number: ${chipMagicValue}`);
|
|
162
162
|
}
|
|
@@ -197,7 +197,7 @@ export class ESPLoader extends EventTarget {
|
|
|
197
197
|
* Get security info including chip ID (ESP32-C3 and later)
|
|
198
198
|
*/
|
|
199
199
|
async getSecurityInfo() {
|
|
200
|
-
const [
|
|
200
|
+
const [, responseData] = await this.checkCommand(ESP_GET_SECURITY_INFO, [], 0);
|
|
201
201
|
// Some chips/ROM versions return empty response or don't support this command
|
|
202
202
|
if (responseData.length === 0) {
|
|
203
203
|
throw new Error(`GET_SECURITY_INFO not supported or returned empty response`);
|
|
@@ -232,10 +232,12 @@ export class ESPLoader extends EventTarget {
|
|
|
232
232
|
}
|
|
233
233
|
this._reader = this.port.readable.getReader();
|
|
234
234
|
try {
|
|
235
|
-
|
|
235
|
+
let keepReading = true;
|
|
236
|
+
while (keepReading) {
|
|
236
237
|
const { value, done } = await this._reader.read();
|
|
237
238
|
if (done) {
|
|
238
239
|
this._reader.releaseLock();
|
|
240
|
+
keepReading = false;
|
|
239
241
|
break;
|
|
240
242
|
}
|
|
241
243
|
if (!value || value.length === 0) {
|
|
@@ -249,8 +251,8 @@ export class ESPLoader extends EventTarget {
|
|
|
249
251
|
this._totalBytesRead += value.length;
|
|
250
252
|
}
|
|
251
253
|
}
|
|
252
|
-
catch
|
|
253
|
-
|
|
254
|
+
catch {
|
|
255
|
+
this.logger.error("Read loop got disconnected");
|
|
254
256
|
}
|
|
255
257
|
// Disconnected!
|
|
256
258
|
this.connected = false;
|
|
@@ -321,11 +323,11 @@ export class ESPLoader extends EventTarget {
|
|
|
321
323
|
* The MAC address burned into the OTP memory of the ESP chip
|
|
322
324
|
*/
|
|
323
325
|
macAddr() {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
326
|
+
const macAddr = new Array(6).fill(0);
|
|
327
|
+
const mac0 = this._efuses[0];
|
|
328
|
+
const mac1 = this._efuses[1];
|
|
329
|
+
const mac2 = this._efuses[2];
|
|
330
|
+
const mac3 = this._efuses[3];
|
|
329
331
|
let oui;
|
|
330
332
|
if (this.chipFamily == CHIP_FAMILY_ESP8266) {
|
|
331
333
|
if (mac3 != 0) {
|
|
@@ -383,9 +385,9 @@ export class ESPLoader extends EventTarget {
|
|
|
383
385
|
if (this.debug) {
|
|
384
386
|
this.logger.debug("Reading from Register " + toHex(reg, 8));
|
|
385
387
|
}
|
|
386
|
-
|
|
388
|
+
const packet = pack("<I", reg);
|
|
387
389
|
await this.sendCommand(ESP_READ_REG, packet);
|
|
388
|
-
|
|
390
|
+
const [val] = await this.getResponse(ESP_READ_REG);
|
|
389
391
|
return val;
|
|
390
392
|
}
|
|
391
393
|
/**
|
|
@@ -397,10 +399,11 @@ export class ESPLoader extends EventTarget {
|
|
|
397
399
|
async checkCommand(opcode, buffer, checksum = 0, timeout = DEFAULT_TIMEOUT) {
|
|
398
400
|
timeout = Math.min(timeout, MAX_TIMEOUT);
|
|
399
401
|
await this.sendCommand(opcode, buffer, checksum);
|
|
400
|
-
|
|
401
|
-
if (
|
|
402
|
+
const [value, responseData] = await this.getResponse(opcode, timeout);
|
|
403
|
+
if (responseData === null) {
|
|
402
404
|
throw new Error("Didn't get enough status bytes");
|
|
403
405
|
}
|
|
406
|
+
let data = responseData;
|
|
404
407
|
let statusLen = 0;
|
|
405
408
|
if (this.IS_STUB || this.chipFamily == CHIP_FAMILY_ESP8266) {
|
|
406
409
|
statusLen = 2;
|
|
@@ -435,7 +438,7 @@ export class ESPLoader extends EventTarget {
|
|
|
435
438
|
if (data.length < statusLen) {
|
|
436
439
|
throw new Error("Didn't get enough status bytes");
|
|
437
440
|
}
|
|
438
|
-
|
|
441
|
+
const status = data.slice(-statusLen, data.length);
|
|
439
442
|
data = data.slice(0, -statusLen);
|
|
440
443
|
if (this.debug) {
|
|
441
444
|
this.logger.debug("status", status);
|
|
@@ -458,7 +461,7 @@ export class ESPLoader extends EventTarget {
|
|
|
458
461
|
* does not check response
|
|
459
462
|
*/
|
|
460
463
|
async sendCommand(opcode, buffer, checksum = 0) {
|
|
461
|
-
|
|
464
|
+
const packet = slipEncode([
|
|
462
465
|
...pack("<BBHI", 0x00, opcode, buffer.length, checksum),
|
|
463
466
|
...buffer,
|
|
464
467
|
]);
|
|
@@ -477,7 +480,7 @@ export class ESPLoader extends EventTarget {
|
|
|
477
480
|
let inEscape = false;
|
|
478
481
|
let readBytes = [];
|
|
479
482
|
while (true) {
|
|
480
|
-
|
|
483
|
+
const stamp = Date.now();
|
|
481
484
|
readBytes = [];
|
|
482
485
|
while (Date.now() - stamp < timeout) {
|
|
483
486
|
if (this._inputBuffer.length > 0) {
|
|
@@ -490,12 +493,12 @@ export class ESPLoader extends EventTarget {
|
|
|
490
493
|
}
|
|
491
494
|
}
|
|
492
495
|
if (readBytes.length == 0) {
|
|
493
|
-
|
|
496
|
+
const waitingFor = partialPacket === null ? "header" : "content";
|
|
494
497
|
throw new SlipReadError("Timed out waiting for packet " + waitingFor);
|
|
495
498
|
}
|
|
496
499
|
if (this.debug)
|
|
497
500
|
this.logger.debug("Read " + readBytes.length + " bytes: " + hexFormatter(readBytes));
|
|
498
|
-
for (
|
|
501
|
+
for (const b of readBytes) {
|
|
499
502
|
if (partialPacket === null) {
|
|
500
503
|
// waiting for packet header
|
|
501
504
|
if (b == 0xc0) {
|
|
@@ -558,7 +561,7 @@ export class ESPLoader extends EventTarget {
|
|
|
558
561
|
if (packet.length < 8) {
|
|
559
562
|
continue;
|
|
560
563
|
}
|
|
561
|
-
const [resp, opRet,
|
|
564
|
+
const [resp, opRet, , val] = unpack("<BBHI", packet.slice(0, 8));
|
|
562
565
|
if (resp != 1) {
|
|
563
566
|
continue;
|
|
564
567
|
}
|
|
@@ -578,7 +581,7 @@ export class ESPLoader extends EventTarget {
|
|
|
578
581
|
* Calculate checksum of a blob, as it is defined by the ROM
|
|
579
582
|
*/
|
|
580
583
|
checksum(data, state = ESP_CHECKSUM_MAGIC) {
|
|
581
|
-
for (
|
|
584
|
+
for (const b of data) {
|
|
582
585
|
state ^= b;
|
|
583
586
|
}
|
|
584
587
|
return state;
|
|
@@ -589,11 +592,11 @@ export class ESPLoader extends EventTarget {
|
|
|
589
592
|
}
|
|
590
593
|
try {
|
|
591
594
|
// Send ESP_ROM_BAUD(115200) as the old one if running STUB otherwise 0
|
|
592
|
-
|
|
595
|
+
const buffer = pack("<II", baud, this.IS_STUB ? ESP_ROM_BAUD : 0);
|
|
593
596
|
await this.checkCommand(ESP_CHANGE_BAUDRATE, buffer);
|
|
594
597
|
}
|
|
595
598
|
catch (e) {
|
|
596
|
-
|
|
599
|
+
this.logger.error(`Baudrate change error: ${e}`);
|
|
597
600
|
throw new Error(`Unable to change the baud rate to ${baud}: No response from set baud rate command.`);
|
|
598
601
|
}
|
|
599
602
|
if (this._parent) {
|
|
@@ -636,7 +639,7 @@ export class ESPLoader extends EventTarget {
|
|
|
636
639
|
this.readLoop();
|
|
637
640
|
}
|
|
638
641
|
catch (e) {
|
|
639
|
-
|
|
642
|
+
this.logger.error(`Reconfigure port error: ${e}`);
|
|
640
643
|
throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
|
|
641
644
|
}
|
|
642
645
|
}
|
|
@@ -648,7 +651,7 @@ export class ESPLoader extends EventTarget {
|
|
|
648
651
|
async sync() {
|
|
649
652
|
for (let i = 0; i < 5; i++) {
|
|
650
653
|
this._inputBuffer.length = 0;
|
|
651
|
-
|
|
654
|
+
const response = await this._sync();
|
|
652
655
|
if (response) {
|
|
653
656
|
await sleep(SYNC_TIMEOUT);
|
|
654
657
|
return true;
|
|
@@ -666,12 +669,12 @@ export class ESPLoader extends EventTarget {
|
|
|
666
669
|
await this.sendCommand(ESP_SYNC, SYNC_PACKET);
|
|
667
670
|
for (let i = 0; i < 8; i++) {
|
|
668
671
|
try {
|
|
669
|
-
|
|
672
|
+
const [, data] = await this.getResponse(ESP_SYNC, SYNC_TIMEOUT);
|
|
670
673
|
if (data.length > 1 && data[0] == 0 && data[1] == 0) {
|
|
671
674
|
return true;
|
|
672
675
|
}
|
|
673
676
|
}
|
|
674
|
-
catch
|
|
677
|
+
catch {
|
|
675
678
|
// If read packet fails.
|
|
676
679
|
}
|
|
677
680
|
}
|
|
@@ -697,13 +700,13 @@ export class ESPLoader extends EventTarget {
|
|
|
697
700
|
async flashData(binaryData, updateProgress, offset = 0, compress = false) {
|
|
698
701
|
if (binaryData.byteLength >= 8) {
|
|
699
702
|
// unpack the (potential) image header
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
703
|
+
const header = Array.from(new Uint8Array(binaryData, 0, 4));
|
|
704
|
+
const headerMagic = header[0];
|
|
705
|
+
const headerFlashMode = header[2];
|
|
706
|
+
const headerFlashSizeFreq = header[3];
|
|
704
707
|
this.logger.log(`Image header, Magic=${toHex(headerMagic)}, FlashMode=${toHex(headerFlashMode)}, FlashSizeFreq=${toHex(headerFlashSizeFreq)}`);
|
|
705
708
|
}
|
|
706
|
-
|
|
709
|
+
const uncompressedFilesize = binaryData.byteLength;
|
|
707
710
|
let compressedFilesize = 0;
|
|
708
711
|
let dataToFlash;
|
|
709
712
|
let timeout = DEFAULT_TIMEOUT;
|
|
@@ -724,9 +727,9 @@ export class ESPLoader extends EventTarget {
|
|
|
724
727
|
let seq = 0;
|
|
725
728
|
let written = 0;
|
|
726
729
|
let position = 0;
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
+
const stamp = Date.now();
|
|
731
|
+
const flashWriteSize = this.getFlashWriteSize();
|
|
732
|
+
const filesize = compress ? compressedFilesize : uncompressedFilesize;
|
|
730
733
|
while (filesize - position > 0) {
|
|
731
734
|
if (this.debug) {
|
|
732
735
|
this.logger.log(`Writing at ${toHex(offset + seq * flashWriteSize, 8)} `);
|
|
@@ -787,8 +790,7 @@ export class ESPLoader extends EventTarget {
|
|
|
787
790
|
// Flush serial buffers before flash write operation
|
|
788
791
|
await this.flushSerialBuffers();
|
|
789
792
|
let eraseSize;
|
|
790
|
-
|
|
791
|
-
let flashWriteSize = this.getFlashWriteSize();
|
|
793
|
+
const flashWriteSize = this.getFlashWriteSize();
|
|
792
794
|
if (!this.IS_STUB &&
|
|
793
795
|
[
|
|
794
796
|
CHIP_FAMILY_ESP32,
|
|
@@ -807,22 +809,18 @@ export class ESPLoader extends EventTarget {
|
|
|
807
809
|
].includes(this.chipFamily)) {
|
|
808
810
|
await this.checkCommand(ESP_SPI_ATTACH, new Array(8).fill(0));
|
|
809
811
|
}
|
|
810
|
-
|
|
812
|
+
const numBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
|
|
811
813
|
if (this.chipFamily == CHIP_FAMILY_ESP8266) {
|
|
812
814
|
eraseSize = this.getEraseSize(offset, size);
|
|
813
815
|
}
|
|
814
816
|
else {
|
|
815
817
|
eraseSize = size;
|
|
816
818
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
|
|
823
|
-
}
|
|
824
|
-
let stamp = Date.now();
|
|
825
|
-
buffer = pack("<IIII", eraseSize, numBlocks, flashWriteSize, offset);
|
|
819
|
+
const timeout = this.IS_STUB
|
|
820
|
+
? DEFAULT_TIMEOUT
|
|
821
|
+
: timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
|
|
822
|
+
const stamp = Date.now();
|
|
823
|
+
let buffer = pack("<IIII", eraseSize, numBlocks, flashWriteSize, offset);
|
|
826
824
|
if (this.chipFamily == CHIP_FAMILY_ESP32 ||
|
|
827
825
|
this.chipFamily == CHIP_FAMILY_ESP32S2 ||
|
|
828
826
|
this.chipFamily == CHIP_FAMILY_ESP32S3 ||
|
|
@@ -858,15 +856,14 @@ export class ESPLoader extends EventTarget {
|
|
|
858
856
|
* @name flashDeflBegin
|
|
859
857
|
*
|
|
860
858
|
*/
|
|
861
|
-
async flashDeflBegin(size = 0, compressedSize = 0, offset = 0
|
|
859
|
+
async flashDeflBegin(size = 0, compressedSize = 0, offset = 0) {
|
|
862
860
|
// Start downloading compressed data to Flash (performs an erase)
|
|
863
861
|
// Returns number of blocks to write.
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
862
|
+
const flashWriteSize = this.getFlashWriteSize();
|
|
863
|
+
const numBlocks = Math.floor((compressedSize + flashWriteSize - 1) / flashWriteSize);
|
|
864
|
+
const eraseBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
|
|
867
865
|
let writeSize = 0;
|
|
868
866
|
let timeout = 0;
|
|
869
|
-
let buffer;
|
|
870
867
|
if (this.IS_STUB) {
|
|
871
868
|
writeSize = size; // stub expects number of bytes here, manages erasing internally
|
|
872
869
|
timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, writeSize); // ROM performs the erase up front
|
|
@@ -875,26 +872,26 @@ export class ESPLoader extends EventTarget {
|
|
|
875
872
|
writeSize = eraseBlocks * flashWriteSize; // ROM expects rounded up to erase block size
|
|
876
873
|
timeout = DEFAULT_TIMEOUT;
|
|
877
874
|
}
|
|
878
|
-
buffer = pack("<IIII", writeSize, numBlocks, flashWriteSize, offset);
|
|
875
|
+
const buffer = pack("<IIII", writeSize, numBlocks, flashWriteSize, offset);
|
|
879
876
|
await this.checkCommand(ESP_FLASH_DEFL_BEGIN, buffer, 0, timeout);
|
|
880
877
|
return timeout;
|
|
881
878
|
}
|
|
882
879
|
async flashFinish() {
|
|
883
|
-
|
|
880
|
+
const buffer = pack("<I", 1);
|
|
884
881
|
await this.checkCommand(ESP_FLASH_END, buffer);
|
|
885
882
|
}
|
|
886
883
|
async flashDeflFinish() {
|
|
887
|
-
|
|
884
|
+
const buffer = pack("<I", 1);
|
|
888
885
|
await this.checkCommand(ESP_FLASH_DEFL_END, buffer);
|
|
889
886
|
}
|
|
890
887
|
getBootloaderOffset() {
|
|
891
|
-
|
|
892
|
-
|
|
888
|
+
const bootFlashOffs = getSpiFlashAddresses(this.getChipFamily());
|
|
889
|
+
const BootldrFlashOffs = bootFlashOffs.flashOffs;
|
|
893
890
|
return BootldrFlashOffs;
|
|
894
891
|
}
|
|
895
892
|
async flashId() {
|
|
896
|
-
|
|
897
|
-
|
|
893
|
+
const SPIFLASH_RDID = 0x9f;
|
|
894
|
+
const result = await this.runSpiFlashCommand(SPIFLASH_RDID, [], 24);
|
|
898
895
|
return result;
|
|
899
896
|
}
|
|
900
897
|
getChipFamily() {
|
|
@@ -904,15 +901,15 @@ export class ESPLoader extends EventTarget {
|
|
|
904
901
|
let buffer = pack("<IIII", address, value, mask, delayUs);
|
|
905
902
|
if (delayAfterUs > 0) {
|
|
906
903
|
// add a dummy write to a date register as an excuse to have a delay
|
|
907
|
-
buffer.concat(pack("<IIII", getSpiFlashAddresses(this.getChipFamily()).uartDateReg, 0, 0, delayAfterUs));
|
|
904
|
+
buffer = buffer.concat(pack("<IIII", getSpiFlashAddresses(this.getChipFamily()).uartDateReg, 0, 0, delayAfterUs));
|
|
908
905
|
}
|
|
909
906
|
await this.checkCommand(ESP_WRITE_REG, buffer);
|
|
910
907
|
}
|
|
911
908
|
async setDataLengths(spiAddresses, mosiBits, misoBits) {
|
|
912
909
|
if (spiAddresses.mosiDlenOffs != -1) {
|
|
913
910
|
// ESP32/32S2/32S3/32C3 has a more sophisticated way to set up "user" commands
|
|
914
|
-
|
|
915
|
-
|
|
911
|
+
const SPI_MOSI_DLEN_REG = spiAddresses.regBase + spiAddresses.mosiDlenOffs;
|
|
912
|
+
const SPI_MISO_DLEN_REG = spiAddresses.regBase + spiAddresses.misoDlenOffs;
|
|
916
913
|
if (mosiBits > 0) {
|
|
917
914
|
await this.writeRegister(SPI_MOSI_DLEN_REG, mosiBits - 1);
|
|
918
915
|
}
|
|
@@ -921,18 +918,18 @@ export class ESPLoader extends EventTarget {
|
|
|
921
918
|
}
|
|
922
919
|
}
|
|
923
920
|
else {
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
921
|
+
const SPI_DATA_LEN_REG = spiAddresses.regBase + spiAddresses.usr1Offs;
|
|
922
|
+
const SPI_MOSI_BITLEN_S = 17;
|
|
923
|
+
const SPI_MISO_BITLEN_S = 8;
|
|
924
|
+
const mosiMask = mosiBits == 0 ? 0 : mosiBits - 1;
|
|
925
|
+
const misoMask = misoBits == 0 ? 0 : misoBits - 1;
|
|
926
|
+
const value = (misoMask << SPI_MISO_BITLEN_S) | (mosiMask << SPI_MOSI_BITLEN_S);
|
|
930
927
|
await this.writeRegister(SPI_DATA_LEN_REG, value);
|
|
931
928
|
}
|
|
932
929
|
}
|
|
933
930
|
async waitDone(spiCmdReg, spiCmdUsr) {
|
|
934
931
|
for (let i = 0; i < 10; i++) {
|
|
935
|
-
|
|
932
|
+
const cmdValue = await this.readRegister(spiCmdReg);
|
|
936
933
|
if ((cmdValue & spiCmdUsr) == 0) {
|
|
937
934
|
return;
|
|
938
935
|
}
|
|
@@ -948,29 +945,29 @@ export class ESPLoader extends EventTarget {
|
|
|
948
945
|
// After writing command byte, writes 'data' to MOSI and then
|
|
949
946
|
// reads back 'read_bits' of reply on MISO. Result is a number.
|
|
950
947
|
// SPI_USR register flags
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
948
|
+
const SPI_USR_COMMAND = 1 << 31;
|
|
949
|
+
const SPI_USR_MISO = 1 << 28;
|
|
950
|
+
const SPI_USR_MOSI = 1 << 27;
|
|
954
951
|
// SPI registers, base address differs ESP32* vs 8266
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
952
|
+
const spiAddresses = getSpiFlashAddresses(this.getChipFamily());
|
|
953
|
+
const base = spiAddresses.regBase;
|
|
954
|
+
const SPI_CMD_REG = base;
|
|
955
|
+
const SPI_USR_REG = base + spiAddresses.usrOffs;
|
|
956
|
+
const SPI_USR2_REG = base + spiAddresses.usr2Offs;
|
|
957
|
+
const SPI_W0_REG = base + spiAddresses.w0Offs;
|
|
961
958
|
// SPI peripheral "command" bitmasks for SPI_CMD_REG
|
|
962
|
-
|
|
959
|
+
const SPI_CMD_USR = 1 << 18;
|
|
963
960
|
// shift values
|
|
964
|
-
|
|
961
|
+
const SPI_USR2_COMMAND_LEN_SHIFT = 28;
|
|
965
962
|
if (readBits > 32) {
|
|
966
963
|
throw new Error("Reading more than 32 bits back from a SPI flash operation is unsupported");
|
|
967
964
|
}
|
|
968
965
|
if (data.length > 64) {
|
|
969
966
|
throw new Error("Writing more than 64 bytes of data with one SPI command is unsupported");
|
|
970
967
|
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
968
|
+
const dataBits = data.length * 8;
|
|
969
|
+
const oldSpiUsr = await this.readRegister(SPI_USR_REG);
|
|
970
|
+
const oldSpiUsr2 = await this.readRegister(SPI_USR2_REG);
|
|
974
971
|
let flags = SPI_USR_COMMAND;
|
|
975
972
|
if (readBits > 0) {
|
|
976
973
|
flags |= SPI_USR_MISO;
|
|
@@ -985,8 +982,9 @@ export class ESPLoader extends EventTarget {
|
|
|
985
982
|
await this.writeRegister(SPI_W0_REG, 0); // clear data register before we read it
|
|
986
983
|
}
|
|
987
984
|
else {
|
|
988
|
-
|
|
989
|
-
|
|
985
|
+
const padLen = (4 - (data.length % 4)) % 4;
|
|
986
|
+
data = data.concat(new Array(padLen).fill(0x00)); // pad to 32-bit multiple
|
|
987
|
+
const words = unpack("I".repeat(Math.floor(data.length / 4)), data);
|
|
990
988
|
let nextReg = SPI_W0_REG;
|
|
991
989
|
this.logger.debug(`Words Length: ${words.length}`);
|
|
992
990
|
for (const word of words) {
|
|
@@ -997,7 +995,7 @@ export class ESPLoader extends EventTarget {
|
|
|
997
995
|
}
|
|
998
996
|
await this.writeRegister(SPI_CMD_REG, SPI_CMD_USR);
|
|
999
997
|
await this.waitDone(SPI_CMD_REG, SPI_CMD_USR);
|
|
1000
|
-
|
|
998
|
+
const status = await this.readRegister(SPI_W0_REG);
|
|
1001
999
|
// restore some SPI controller registers
|
|
1002
1000
|
await this.writeRegister(SPI_USR_REG, oldSpiUsr);
|
|
1003
1001
|
await this.writeRegister(SPI_USR2_REG, oldSpiUsr2);
|
|
@@ -1005,9 +1003,9 @@ export class ESPLoader extends EventTarget {
|
|
|
1005
1003
|
}
|
|
1006
1004
|
async detectFlashSize() {
|
|
1007
1005
|
this.logger.log("Detecting Flash Size");
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1006
|
+
const flashId = await this.flashId();
|
|
1007
|
+
const manufacturer = flashId & 0xff;
|
|
1008
|
+
const flashIdLowbyte = (flashId >> 16) & 0xff;
|
|
1011
1009
|
this.logger.log(`FlashId: ${toHex(flashId)}`);
|
|
1012
1010
|
this.logger.log(`Flash Manufacturer: ${manufacturer.toString(16)}`);
|
|
1013
1011
|
this.logger.log(`Flash Device: ${((flashId >> 8) & 0xff).toString(16)}${flashIdLowbyte.toString(16)}`);
|
|
@@ -1020,10 +1018,10 @@ export class ESPLoader extends EventTarget {
|
|
|
1020
1018
|
* Provides a workaround for the bootloader erase bug on ESP8266.
|
|
1021
1019
|
*/
|
|
1022
1020
|
getEraseSize(offset, size) {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1021
|
+
const sectorsPerBlock = 16;
|
|
1022
|
+
const sectorSize = FLASH_SECTOR_SIZE;
|
|
1023
|
+
const numSectors = Math.floor((size + sectorSize - 1) / sectorSize);
|
|
1024
|
+
const startSector = Math.floor(offset / sectorSize);
|
|
1027
1025
|
let headSectors = sectorsPerBlock - (startSector % sectorsPerBlock);
|
|
1028
1026
|
if (numSectors < headSectors) {
|
|
1029
1027
|
headSectors = numSectors;
|
|
@@ -1057,8 +1055,8 @@ export class ESPLoader extends EventTarget {
|
|
|
1057
1055
|
* ignore errors.
|
|
1058
1056
|
*/
|
|
1059
1057
|
async memFinish(entrypoint = 0) {
|
|
1060
|
-
|
|
1061
|
-
|
|
1058
|
+
const timeout = this.IS_STUB ? DEFAULT_TIMEOUT : MEM_END_ROM_TIMEOUT;
|
|
1059
|
+
const data = pack("<II", entrypoint == 0 ? 1 : 0, entrypoint);
|
|
1062
1060
|
return await this.checkCommand(ESP_MEM_END, data, 0, timeout);
|
|
1063
1061
|
}
|
|
1064
1062
|
async runStub(skipFlashDetection = false) {
|
|
@@ -1069,29 +1067,27 @@ export class ESPLoader extends EventTarget {
|
|
|
1069
1067
|
return this;
|
|
1070
1068
|
}
|
|
1071
1069
|
// We're transferring over USB, right?
|
|
1072
|
-
|
|
1070
|
+
const ramBlock = USB_RAM_BLOCK;
|
|
1073
1071
|
// Upload
|
|
1074
1072
|
this.logger.log("Uploading stub...");
|
|
1075
|
-
for (
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
}
|
|
1087
|
-
await this.memBlock(stub[field].slice(fromOffs, toOffs), seq);
|
|
1073
|
+
for (const field of ["text", "data"]) {
|
|
1074
|
+
const fieldData = stub[field];
|
|
1075
|
+
const offset = stub[`${field}_start`];
|
|
1076
|
+
const length = fieldData.length;
|
|
1077
|
+
const blocks = Math.floor((length + ramBlock - 1) / ramBlock);
|
|
1078
|
+
await this.memBegin(length, blocks, ramBlock, offset);
|
|
1079
|
+
for (const seq of Array(blocks).keys()) {
|
|
1080
|
+
const fromOffs = seq * ramBlock;
|
|
1081
|
+
let toOffs = fromOffs + ramBlock;
|
|
1082
|
+
if (toOffs > length) {
|
|
1083
|
+
toOffs = length;
|
|
1088
1084
|
}
|
|
1085
|
+
await this.memBlock(fieldData.slice(fromOffs, toOffs), seq);
|
|
1089
1086
|
}
|
|
1090
1087
|
}
|
|
1091
|
-
await this.memFinish(stub
|
|
1092
|
-
let pChar;
|
|
1088
|
+
await this.memFinish(stub.entry);
|
|
1093
1089
|
const p = await this.readPacket(500);
|
|
1094
|
-
pChar = String.fromCharCode(...p);
|
|
1090
|
+
const pChar = String.fromCharCode(...p);
|
|
1095
1091
|
if (pChar != "OHAI") {
|
|
1096
1092
|
throw new Error("Failed to start stub. Unexpected response: " + pChar);
|
|
1097
1093
|
}
|
|
@@ -1110,7 +1106,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1110
1106
|
writer.releaseLock();
|
|
1111
1107
|
}
|
|
1112
1108
|
catch (err) {
|
|
1113
|
-
|
|
1109
|
+
this.logger.error(`Ignoring release lock error: ${err}`);
|
|
1114
1110
|
}
|
|
1115
1111
|
}
|
|
1116
1112
|
async disconnect() {
|
|
@@ -1298,8 +1294,8 @@ export class ESPLoader extends EventTarget {
|
|
|
1298
1294
|
try {
|
|
1299
1295
|
this.logger.debug(`Reading chunk at 0x${currentAddr.toString(16)}, size: 0x${chunkSize.toString(16)}`);
|
|
1300
1296
|
// Send read flash command for this chunk
|
|
1301
|
-
|
|
1302
|
-
const [res
|
|
1297
|
+
const pkt = pack("<IIII", currentAddr, chunkSize, 0x1000, 1024);
|
|
1298
|
+
const [res] = await this.checkCommand(ESP_READ_FLASH, pkt);
|
|
1303
1299
|
if (res != 0) {
|
|
1304
1300
|
throw new Error("Failed to read memory: " + res);
|
|
1305
1301
|
}
|
|
@@ -1371,7 +1367,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1371
1367
|
}
|
|
1372
1368
|
currentAddr += chunkSize;
|
|
1373
1369
|
remainingSize -= chunkSize;
|
|
1374
|
-
this.logger.debug(`Total progress: 0x${allData.length.toString(16)}
|
|
1370
|
+
this.logger.debug(`Total progress: 0x${allData.length.toString(16)} from 0x${size.toString(16)} bytes`);
|
|
1375
1371
|
}
|
|
1376
1372
|
this.logger.debug(`Successfully read ${allData.length} bytes from flash`);
|
|
1377
1373
|
return allData;
|
|
@@ -1390,17 +1386,17 @@ class EspStubLoader extends ESPLoader {
|
|
|
1390
1386
|
* @name memBegin (592)
|
|
1391
1387
|
* Start downloading an application image to RAM
|
|
1392
1388
|
*/
|
|
1393
|
-
async memBegin(size,
|
|
1394
|
-
|
|
1389
|
+
async memBegin(size, _blocks, _blocksize, offset) {
|
|
1390
|
+
const stub = await getStubCode(this.chipFamily, this.chipRevision);
|
|
1395
1391
|
// Stub may be null for chips without stub support
|
|
1396
1392
|
if (stub === null) {
|
|
1397
|
-
return;
|
|
1393
|
+
return [0, []];
|
|
1398
1394
|
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
for (
|
|
1395
|
+
const load_start = offset;
|
|
1396
|
+
const load_end = offset + size;
|
|
1397
|
+
this.logger.debug(`Load range: ${toHex(load_start, 8)}-${toHex(load_end, 8)}`);
|
|
1398
|
+
this.logger.debug(`Stub data: ${toHex(stub.data_start, 8)}, len: ${stub.data.length}, text: ${toHex(stub.text_start, 8)}, len: ${stub.text.length}`);
|
|
1399
|
+
for (const [start, end] of [
|
|
1404
1400
|
[stub.data_start, stub.data_start + stub.data.length],
|
|
1405
1401
|
[stub.text_start, stub.text_start + stub.text.length],
|
|
1406
1402
|
]) {
|
|
@@ -1418,6 +1414,7 @@ class EspStubLoader extends ESPLoader {
|
|
|
1418
1414
|
"Try changing the binary loading address.");
|
|
1419
1415
|
}
|
|
1420
1416
|
}
|
|
1417
|
+
return [0, []];
|
|
1421
1418
|
}
|
|
1422
1419
|
/**
|
|
1423
1420
|
* @name getEraseSize
|