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