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/src/esp_loader.ts
CHANGED
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
} from "./const";
|
|
63
63
|
import { getStubCode } from "./stubs";
|
|
64
64
|
import { hexFormatter, sleep, slipEncode, toHex } from "./util";
|
|
65
|
-
// @ts-
|
|
65
|
+
// @ts-expect-error pako ESM module doesn't have proper type definitions
|
|
66
66
|
import { deflate } from "pako/dist/pako.esm.mjs";
|
|
67
67
|
import { pack, unpack } from "./struct";
|
|
68
68
|
|
|
@@ -193,8 +193,8 @@ export class ESPLoader extends EventTarget {
|
|
|
193
193
|
await this.detectChip();
|
|
194
194
|
|
|
195
195
|
// Read the OTP data for this chip and store into this.efuses array
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
const FlAddr = getSpiFlashAddresses(this.getChipFamily());
|
|
197
|
+
const AddrMAC = FlAddr.macFuse;
|
|
198
198
|
for (let i = 0; i < 4; i++) {
|
|
199
199
|
this._efuses[i] = await this.readRegister(AddrMAC + 4 * i);
|
|
200
200
|
}
|
|
@@ -241,10 +241,10 @@ export class ESPLoader extends EventTarget {
|
|
|
241
241
|
this.logger.debug(
|
|
242
242
|
`Unknown IMAGE_CHIP_ID: ${chipId}, falling back to magic value detection`,
|
|
243
243
|
);
|
|
244
|
-
} catch (
|
|
244
|
+
} catch (error) {
|
|
245
245
|
// GET_SECURITY_INFO not supported, fall back to magic value detection
|
|
246
246
|
this.logger.debug(
|
|
247
|
-
`GET_SECURITY_INFO failed, using magic value detection: ${
|
|
247
|
+
`GET_SECURITY_INFO failed, using magic value detection: ${error}`,
|
|
248
248
|
);
|
|
249
249
|
|
|
250
250
|
// Clear input buffer and re-sync to recover from failed command
|
|
@@ -262,8 +262,8 @@ export class ESPLoader extends EventTarget {
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
// Fallback: Use magic value detection for ESP8266, ESP32, ESP32-S2, and ESP32-P4 RC versions
|
|
265
|
-
|
|
266
|
-
|
|
265
|
+
const chipMagicValue = await this.readRegister(CHIP_DETECT_MAGIC_REG_ADDR);
|
|
266
|
+
const chip = CHIP_DETECT_MAGIC_VALUES[chipMagicValue >>> 0];
|
|
267
267
|
if (chip === undefined) {
|
|
268
268
|
throw new Error(
|
|
269
269
|
`Unknown Chip: Hex: ${toHex(
|
|
@@ -325,7 +325,7 @@ export class ESPLoader extends EventTarget {
|
|
|
325
325
|
chipId: number;
|
|
326
326
|
apiVersion: number;
|
|
327
327
|
}> {
|
|
328
|
-
const [
|
|
328
|
+
const [, responseData] = await this.checkCommand(
|
|
329
329
|
ESP_GET_SECURITY_INFO,
|
|
330
330
|
[],
|
|
331
331
|
0,
|
|
@@ -377,10 +377,12 @@ export class ESPLoader extends EventTarget {
|
|
|
377
377
|
this._reader = this.port.readable!.getReader();
|
|
378
378
|
|
|
379
379
|
try {
|
|
380
|
-
|
|
380
|
+
let keepReading = true;
|
|
381
|
+
while (keepReading) {
|
|
381
382
|
const { value, done } = await this._reader.read();
|
|
382
383
|
if (done) {
|
|
383
384
|
this._reader.releaseLock();
|
|
385
|
+
keepReading = false;
|
|
384
386
|
break;
|
|
385
387
|
}
|
|
386
388
|
if (!value || value.length === 0) {
|
|
@@ -395,8 +397,8 @@ export class ESPLoader extends EventTarget {
|
|
|
395
397
|
// Track total bytes read from serial port
|
|
396
398
|
this._totalBytesRead += value.length;
|
|
397
399
|
}
|
|
398
|
-
} catch
|
|
399
|
-
|
|
400
|
+
} catch {
|
|
401
|
+
this.logger.error("Read loop got disconnected");
|
|
400
402
|
}
|
|
401
403
|
// Disconnected!
|
|
402
404
|
this.connected = false;
|
|
@@ -474,11 +476,11 @@ export class ESPLoader extends EventTarget {
|
|
|
474
476
|
* The MAC address burned into the OTP memory of the ESP chip
|
|
475
477
|
*/
|
|
476
478
|
macAddr() {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
479
|
+
const macAddr = new Array(6).fill(0);
|
|
480
|
+
const mac0 = this._efuses[0];
|
|
481
|
+
const mac1 = this._efuses[1];
|
|
482
|
+
const mac2 = this._efuses[2];
|
|
483
|
+
const mac3 = this._efuses[3];
|
|
482
484
|
let oui;
|
|
483
485
|
if (this.chipFamily == CHIP_FAMILY_ESP8266) {
|
|
484
486
|
if (mac3 != 0) {
|
|
@@ -534,9 +536,9 @@ export class ESPLoader extends EventTarget {
|
|
|
534
536
|
if (this.debug) {
|
|
535
537
|
this.logger.debug("Reading from Register " + toHex(reg, 8));
|
|
536
538
|
}
|
|
537
|
-
|
|
539
|
+
const packet = pack("<I", reg);
|
|
538
540
|
await this.sendCommand(ESP_READ_REG, packet);
|
|
539
|
-
|
|
541
|
+
const [val] = await this.getResponse(ESP_READ_REG);
|
|
540
542
|
return val;
|
|
541
543
|
}
|
|
542
544
|
|
|
@@ -554,12 +556,13 @@ export class ESPLoader extends EventTarget {
|
|
|
554
556
|
): Promise<[number, number[]]> {
|
|
555
557
|
timeout = Math.min(timeout, MAX_TIMEOUT);
|
|
556
558
|
await this.sendCommand(opcode, buffer, checksum);
|
|
557
|
-
|
|
559
|
+
const [value, responseData] = await this.getResponse(opcode, timeout);
|
|
558
560
|
|
|
559
|
-
if (
|
|
561
|
+
if (responseData === null) {
|
|
560
562
|
throw new Error("Didn't get enough status bytes");
|
|
561
563
|
}
|
|
562
564
|
|
|
565
|
+
let data = responseData;
|
|
563
566
|
let statusLen = 0;
|
|
564
567
|
|
|
565
568
|
if (this.IS_STUB || this.chipFamily == CHIP_FAMILY_ESP8266) {
|
|
@@ -595,7 +598,7 @@ export class ESPLoader extends EventTarget {
|
|
|
595
598
|
if (data.length < statusLen) {
|
|
596
599
|
throw new Error("Didn't get enough status bytes");
|
|
597
600
|
}
|
|
598
|
-
|
|
601
|
+
const status = data.slice(-statusLen, data.length);
|
|
599
602
|
data = data.slice(0, -statusLen);
|
|
600
603
|
if (this.debug) {
|
|
601
604
|
this.logger.debug("status", status);
|
|
@@ -619,7 +622,7 @@ export class ESPLoader extends EventTarget {
|
|
|
619
622
|
* does not check response
|
|
620
623
|
*/
|
|
621
624
|
async sendCommand(opcode: number, buffer: number[], checksum = 0) {
|
|
622
|
-
|
|
625
|
+
const packet = slipEncode([
|
|
623
626
|
...pack("<BBHI", 0x00, opcode, buffer.length, checksum),
|
|
624
627
|
...buffer,
|
|
625
628
|
]);
|
|
@@ -643,7 +646,7 @@ export class ESPLoader extends EventTarget {
|
|
|
643
646
|
let inEscape = false;
|
|
644
647
|
let readBytes: number[] = [];
|
|
645
648
|
while (true) {
|
|
646
|
-
|
|
649
|
+
const stamp = Date.now();
|
|
647
650
|
readBytes = [];
|
|
648
651
|
while (Date.now() - stamp < timeout) {
|
|
649
652
|
if (this._inputBuffer.length > 0) {
|
|
@@ -655,14 +658,14 @@ export class ESPLoader extends EventTarget {
|
|
|
655
658
|
}
|
|
656
659
|
}
|
|
657
660
|
if (readBytes.length == 0) {
|
|
658
|
-
|
|
661
|
+
const waitingFor = partialPacket === null ? "header" : "content";
|
|
659
662
|
throw new SlipReadError("Timed out waiting for packet " + waitingFor);
|
|
660
663
|
}
|
|
661
664
|
if (this.debug)
|
|
662
665
|
this.logger.debug(
|
|
663
666
|
"Read " + readBytes.length + " bytes: " + hexFormatter(readBytes),
|
|
664
667
|
);
|
|
665
|
-
for (
|
|
668
|
+
for (const b of readBytes) {
|
|
666
669
|
if (partialPacket === null) {
|
|
667
670
|
// waiting for packet header
|
|
668
671
|
if (b == 0xc0) {
|
|
@@ -738,7 +741,7 @@ export class ESPLoader extends EventTarget {
|
|
|
738
741
|
continue;
|
|
739
742
|
}
|
|
740
743
|
|
|
741
|
-
const [resp, opRet,
|
|
744
|
+
const [resp, opRet, , val] = unpack("<BBHI", packet.slice(0, 8));
|
|
742
745
|
if (resp != 1) {
|
|
743
746
|
continue;
|
|
744
747
|
}
|
|
@@ -759,7 +762,7 @@ export class ESPLoader extends EventTarget {
|
|
|
759
762
|
* Calculate checksum of a blob, as it is defined by the ROM
|
|
760
763
|
*/
|
|
761
764
|
checksum(data: number[], state = ESP_CHECKSUM_MAGIC) {
|
|
762
|
-
for (
|
|
765
|
+
for (const b of data) {
|
|
763
766
|
state ^= b;
|
|
764
767
|
}
|
|
765
768
|
return state;
|
|
@@ -772,10 +775,10 @@ export class ESPLoader extends EventTarget {
|
|
|
772
775
|
|
|
773
776
|
try {
|
|
774
777
|
// Send ESP_ROM_BAUD(115200) as the old one if running STUB otherwise 0
|
|
775
|
-
|
|
778
|
+
const buffer = pack("<II", baud, this.IS_STUB ? ESP_ROM_BAUD : 0);
|
|
776
779
|
await this.checkCommand(ESP_CHANGE_BAUDRATE, buffer);
|
|
777
780
|
} catch (e) {
|
|
778
|
-
|
|
781
|
+
this.logger.error(`Baudrate change error: ${e}`);
|
|
779
782
|
throw new Error(
|
|
780
783
|
`Unable to change the baud rate to ${baud}: No response from set baud rate command.`,
|
|
781
784
|
);
|
|
@@ -828,7 +831,7 @@ export class ESPLoader extends EventTarget {
|
|
|
828
831
|
// Restart Readloop
|
|
829
832
|
this.readLoop();
|
|
830
833
|
} catch (e) {
|
|
831
|
-
|
|
834
|
+
this.logger.error(`Reconfigure port error: ${e}`);
|
|
832
835
|
throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
|
|
833
836
|
}
|
|
834
837
|
}
|
|
@@ -841,7 +844,7 @@ export class ESPLoader extends EventTarget {
|
|
|
841
844
|
async sync() {
|
|
842
845
|
for (let i = 0; i < 5; i++) {
|
|
843
846
|
this._inputBuffer.length = 0;
|
|
844
|
-
|
|
847
|
+
const response = await this._sync();
|
|
845
848
|
if (response) {
|
|
846
849
|
await sleep(SYNC_TIMEOUT);
|
|
847
850
|
return true;
|
|
@@ -861,11 +864,11 @@ export class ESPLoader extends EventTarget {
|
|
|
861
864
|
await this.sendCommand(ESP_SYNC, SYNC_PACKET);
|
|
862
865
|
for (let i = 0; i < 8; i++) {
|
|
863
866
|
try {
|
|
864
|
-
|
|
867
|
+
const [, data] = await this.getResponse(ESP_SYNC, SYNC_TIMEOUT);
|
|
865
868
|
if (data.length > 1 && data[0] == 0 && data[1] == 0) {
|
|
866
869
|
return true;
|
|
867
870
|
}
|
|
868
|
-
} catch
|
|
871
|
+
} catch {
|
|
869
872
|
// If read packet fails.
|
|
870
873
|
}
|
|
871
874
|
}
|
|
@@ -898,10 +901,10 @@ export class ESPLoader extends EventTarget {
|
|
|
898
901
|
) {
|
|
899
902
|
if (binaryData.byteLength >= 8) {
|
|
900
903
|
// unpack the (potential) image header
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
904
|
+
const header = Array.from(new Uint8Array(binaryData, 0, 4));
|
|
905
|
+
const headerMagic = header[0];
|
|
906
|
+
const headerFlashMode = header[2];
|
|
907
|
+
const headerFlashSizeFreq = header[3];
|
|
905
908
|
|
|
906
909
|
this.logger.log(
|
|
907
910
|
`Image header, Magic=${toHex(headerMagic)}, FlashMode=${toHex(
|
|
@@ -910,7 +913,7 @@ export class ESPLoader extends EventTarget {
|
|
|
910
913
|
);
|
|
911
914
|
}
|
|
912
915
|
|
|
913
|
-
|
|
916
|
+
const uncompressedFilesize = binaryData.byteLength;
|
|
914
917
|
let compressedFilesize = 0;
|
|
915
918
|
|
|
916
919
|
let dataToFlash;
|
|
@@ -939,10 +942,10 @@ export class ESPLoader extends EventTarget {
|
|
|
939
942
|
let seq = 0;
|
|
940
943
|
let written = 0;
|
|
941
944
|
let position = 0;
|
|
942
|
-
|
|
943
|
-
|
|
945
|
+
const stamp = Date.now();
|
|
946
|
+
const flashWriteSize = this.getFlashWriteSize();
|
|
944
947
|
|
|
945
|
-
|
|
948
|
+
const filesize = compress ? compressedFilesize : uncompressedFilesize;
|
|
946
949
|
|
|
947
950
|
while (filesize - position > 0) {
|
|
948
951
|
if (this.debug) {
|
|
@@ -1028,8 +1031,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1028
1031
|
await this.flushSerialBuffers();
|
|
1029
1032
|
|
|
1030
1033
|
let eraseSize;
|
|
1031
|
-
|
|
1032
|
-
let flashWriteSize = this.getFlashWriteSize();
|
|
1034
|
+
const flashWriteSize = this.getFlashWriteSize();
|
|
1033
1035
|
if (
|
|
1034
1036
|
!this.IS_STUB &&
|
|
1035
1037
|
[
|
|
@@ -1050,22 +1052,19 @@ export class ESPLoader extends EventTarget {
|
|
|
1050
1052
|
) {
|
|
1051
1053
|
await this.checkCommand(ESP_SPI_ATTACH, new Array(8).fill(0));
|
|
1052
1054
|
}
|
|
1053
|
-
|
|
1055
|
+
const numBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
|
|
1054
1056
|
if (this.chipFamily == CHIP_FAMILY_ESP8266) {
|
|
1055
1057
|
eraseSize = this.getEraseSize(offset, size);
|
|
1056
1058
|
} else {
|
|
1057
1059
|
eraseSize = size;
|
|
1058
1060
|
}
|
|
1059
1061
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
} else {
|
|
1064
|
-
timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
|
|
1065
|
-
}
|
|
1062
|
+
const timeout = this.IS_STUB
|
|
1063
|
+
? DEFAULT_TIMEOUT
|
|
1064
|
+
: timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
|
|
1066
1065
|
|
|
1067
|
-
|
|
1068
|
-
buffer = pack("<IIII", eraseSize, numBlocks, flashWriteSize, offset);
|
|
1066
|
+
const stamp = Date.now();
|
|
1067
|
+
let buffer = pack("<IIII", eraseSize, numBlocks, flashWriteSize, offset);
|
|
1069
1068
|
if (
|
|
1070
1069
|
this.chipFamily == CHIP_FAMILY_ESP32 ||
|
|
1071
1070
|
this.chipFamily == CHIP_FAMILY_ESP32S2 ||
|
|
@@ -1109,22 +1108,18 @@ export class ESPLoader extends EventTarget {
|
|
|
1109
1108
|
*
|
|
1110
1109
|
*/
|
|
1111
1110
|
|
|
1112
|
-
async flashDeflBegin(
|
|
1113
|
-
size = 0,
|
|
1114
|
-
compressedSize = 0,
|
|
1115
|
-
offset = 0,
|
|
1116
|
-
encrypted = false,
|
|
1117
|
-
) {
|
|
1111
|
+
async flashDeflBegin(size = 0, compressedSize = 0, offset = 0) {
|
|
1118
1112
|
// Start downloading compressed data to Flash (performs an erase)
|
|
1119
1113
|
// Returns number of blocks to write.
|
|
1120
|
-
|
|
1121
|
-
|
|
1114
|
+
const flashWriteSize = this.getFlashWriteSize();
|
|
1115
|
+
const numBlocks = Math.floor(
|
|
1122
1116
|
(compressedSize + flashWriteSize - 1) / flashWriteSize,
|
|
1123
1117
|
);
|
|
1124
|
-
|
|
1118
|
+
const eraseBlocks = Math.floor(
|
|
1119
|
+
(size + flashWriteSize - 1) / flashWriteSize,
|
|
1120
|
+
);
|
|
1125
1121
|
let writeSize = 0;
|
|
1126
1122
|
let timeout = 0;
|
|
1127
|
-
let buffer;
|
|
1128
1123
|
|
|
1129
1124
|
if (this.IS_STUB) {
|
|
1130
1125
|
writeSize = size; // stub expects number of bytes here, manages erasing internally
|
|
@@ -1133,7 +1128,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1133
1128
|
writeSize = eraseBlocks * flashWriteSize; // ROM expects rounded up to erase block size
|
|
1134
1129
|
timeout = DEFAULT_TIMEOUT;
|
|
1135
1130
|
}
|
|
1136
|
-
buffer = pack("<IIII", writeSize, numBlocks, flashWriteSize, offset);
|
|
1131
|
+
const buffer = pack("<IIII", writeSize, numBlocks, flashWriteSize, offset);
|
|
1137
1132
|
|
|
1138
1133
|
await this.checkCommand(ESP_FLASH_DEFL_BEGIN, buffer, 0, timeout);
|
|
1139
1134
|
|
|
@@ -1141,24 +1136,24 @@ export class ESPLoader extends EventTarget {
|
|
|
1141
1136
|
}
|
|
1142
1137
|
|
|
1143
1138
|
async flashFinish() {
|
|
1144
|
-
|
|
1139
|
+
const buffer = pack("<I", 1);
|
|
1145
1140
|
await this.checkCommand(ESP_FLASH_END, buffer);
|
|
1146
1141
|
}
|
|
1147
1142
|
|
|
1148
1143
|
async flashDeflFinish() {
|
|
1149
|
-
|
|
1144
|
+
const buffer = pack("<I", 1);
|
|
1150
1145
|
await this.checkCommand(ESP_FLASH_DEFL_END, buffer);
|
|
1151
1146
|
}
|
|
1152
1147
|
|
|
1153
1148
|
getBootloaderOffset() {
|
|
1154
|
-
|
|
1155
|
-
|
|
1149
|
+
const bootFlashOffs = getSpiFlashAddresses(this.getChipFamily());
|
|
1150
|
+
const BootldrFlashOffs = bootFlashOffs.flashOffs;
|
|
1156
1151
|
return BootldrFlashOffs;
|
|
1157
1152
|
}
|
|
1158
1153
|
|
|
1159
1154
|
async flashId() {
|
|
1160
|
-
|
|
1161
|
-
|
|
1155
|
+
const SPIFLASH_RDID = 0x9f;
|
|
1156
|
+
const result = await this.runSpiFlashCommand(SPIFLASH_RDID, [], 24);
|
|
1162
1157
|
return result;
|
|
1163
1158
|
}
|
|
1164
1159
|
|
|
@@ -1176,7 +1171,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1176
1171
|
let buffer = pack("<IIII", address, value, mask, delayUs);
|
|
1177
1172
|
if (delayAfterUs > 0) {
|
|
1178
1173
|
// add a dummy write to a date register as an excuse to have a delay
|
|
1179
|
-
buffer.concat(
|
|
1174
|
+
buffer = buffer.concat(
|
|
1180
1175
|
pack(
|
|
1181
1176
|
"<IIII",
|
|
1182
1177
|
getSpiFlashAddresses(this.getChipFamily()).uartDateReg,
|
|
@@ -1196,8 +1191,10 @@ export class ESPLoader extends EventTarget {
|
|
|
1196
1191
|
) {
|
|
1197
1192
|
if (spiAddresses.mosiDlenOffs != -1) {
|
|
1198
1193
|
// ESP32/32S2/32S3/32C3 has a more sophisticated way to set up "user" commands
|
|
1199
|
-
|
|
1200
|
-
|
|
1194
|
+
const SPI_MOSI_DLEN_REG =
|
|
1195
|
+
spiAddresses.regBase + spiAddresses.mosiDlenOffs;
|
|
1196
|
+
const SPI_MISO_DLEN_REG =
|
|
1197
|
+
spiAddresses.regBase + spiAddresses.misoDlenOffs;
|
|
1201
1198
|
if (mosiBits > 0) {
|
|
1202
1199
|
await this.writeRegister(SPI_MOSI_DLEN_REG, mosiBits - 1);
|
|
1203
1200
|
}
|
|
@@ -1205,19 +1202,19 @@ export class ESPLoader extends EventTarget {
|
|
|
1205
1202
|
await this.writeRegister(SPI_MISO_DLEN_REG, misoBits - 1);
|
|
1206
1203
|
}
|
|
1207
1204
|
} else {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1205
|
+
const SPI_DATA_LEN_REG = spiAddresses.regBase + spiAddresses.usr1Offs;
|
|
1206
|
+
const SPI_MOSI_BITLEN_S = 17;
|
|
1207
|
+
const SPI_MISO_BITLEN_S = 8;
|
|
1208
|
+
const mosiMask = mosiBits == 0 ? 0 : mosiBits - 1;
|
|
1209
|
+
const misoMask = misoBits == 0 ? 0 : misoBits - 1;
|
|
1210
|
+
const value =
|
|
1214
1211
|
(misoMask << SPI_MISO_BITLEN_S) | (mosiMask << SPI_MOSI_BITLEN_S);
|
|
1215
1212
|
await this.writeRegister(SPI_DATA_LEN_REG, value);
|
|
1216
1213
|
}
|
|
1217
1214
|
}
|
|
1218
1215
|
async waitDone(spiCmdReg: number, spiCmdUsr: number) {
|
|
1219
1216
|
for (let i = 0; i < 10; i++) {
|
|
1220
|
-
|
|
1217
|
+
const cmdValue = await this.readRegister(spiCmdReg);
|
|
1221
1218
|
if ((cmdValue & spiCmdUsr) == 0) {
|
|
1222
1219
|
return;
|
|
1223
1220
|
}
|
|
@@ -1241,23 +1238,23 @@ export class ESPLoader extends EventTarget {
|
|
|
1241
1238
|
// reads back 'read_bits' of reply on MISO. Result is a number.
|
|
1242
1239
|
|
|
1243
1240
|
// SPI_USR register flags
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1241
|
+
const SPI_USR_COMMAND = 1 << 31;
|
|
1242
|
+
const SPI_USR_MISO = 1 << 28;
|
|
1243
|
+
const SPI_USR_MOSI = 1 << 27;
|
|
1247
1244
|
|
|
1248
1245
|
// SPI registers, base address differs ESP32* vs 8266
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1246
|
+
const spiAddresses = getSpiFlashAddresses(this.getChipFamily());
|
|
1247
|
+
const base = spiAddresses.regBase;
|
|
1248
|
+
const SPI_CMD_REG = base;
|
|
1249
|
+
const SPI_USR_REG = base + spiAddresses.usrOffs;
|
|
1250
|
+
const SPI_USR2_REG = base + spiAddresses.usr2Offs;
|
|
1251
|
+
const SPI_W0_REG = base + spiAddresses.w0Offs;
|
|
1255
1252
|
|
|
1256
1253
|
// SPI peripheral "command" bitmasks for SPI_CMD_REG
|
|
1257
|
-
|
|
1254
|
+
const SPI_CMD_USR = 1 << 18;
|
|
1258
1255
|
|
|
1259
1256
|
// shift values
|
|
1260
|
-
|
|
1257
|
+
const SPI_USR2_COMMAND_LEN_SHIFT = 28;
|
|
1261
1258
|
|
|
1262
1259
|
if (readBits > 32) {
|
|
1263
1260
|
throw new Error(
|
|
@@ -1270,9 +1267,9 @@ export class ESPLoader extends EventTarget {
|
|
|
1270
1267
|
);
|
|
1271
1268
|
}
|
|
1272
1269
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1270
|
+
const dataBits = data.length * 8;
|
|
1271
|
+
const oldSpiUsr = await this.readRegister(SPI_USR_REG);
|
|
1272
|
+
const oldSpiUsr2 = await this.readRegister(SPI_USR2_REG);
|
|
1276
1273
|
|
|
1277
1274
|
let flags = SPI_USR_COMMAND;
|
|
1278
1275
|
|
|
@@ -1293,9 +1290,10 @@ export class ESPLoader extends EventTarget {
|
|
|
1293
1290
|
if (dataBits == 0) {
|
|
1294
1291
|
await this.writeRegister(SPI_W0_REG, 0); // clear data register before we read it
|
|
1295
1292
|
} else {
|
|
1296
|
-
|
|
1293
|
+
const padLen = (4 - (data.length % 4)) % 4;
|
|
1294
|
+
data = data.concat(new Array(padLen).fill(0x00)); // pad to 32-bit multiple
|
|
1297
1295
|
|
|
1298
|
-
|
|
1296
|
+
const words = unpack("I".repeat(Math.floor(data.length / 4)), data);
|
|
1299
1297
|
let nextReg = SPI_W0_REG;
|
|
1300
1298
|
|
|
1301
1299
|
this.logger.debug(`Words Length: ${words.length}`);
|
|
@@ -1311,7 +1309,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1311
1309
|
await this.writeRegister(SPI_CMD_REG, SPI_CMD_USR);
|
|
1312
1310
|
await this.waitDone(SPI_CMD_REG, SPI_CMD_USR);
|
|
1313
1311
|
|
|
1314
|
-
|
|
1312
|
+
const status = await this.readRegister(SPI_W0_REG);
|
|
1315
1313
|
// restore some SPI controller registers
|
|
1316
1314
|
await this.writeRegister(SPI_USR_REG, oldSpiUsr);
|
|
1317
1315
|
await this.writeRegister(SPI_USR2_REG, oldSpiUsr2);
|
|
@@ -1320,9 +1318,9 @@ export class ESPLoader extends EventTarget {
|
|
|
1320
1318
|
async detectFlashSize() {
|
|
1321
1319
|
this.logger.log("Detecting Flash Size");
|
|
1322
1320
|
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1321
|
+
const flashId = await this.flashId();
|
|
1322
|
+
const manufacturer = flashId & 0xff;
|
|
1323
|
+
const flashIdLowbyte = (flashId >> 16) & 0xff;
|
|
1326
1324
|
|
|
1327
1325
|
this.logger.log(`FlashId: ${toHex(flashId)}`);
|
|
1328
1326
|
this.logger.log(`Flash Manufacturer: ${manufacturer.toString(16)}`);
|
|
@@ -1342,10 +1340,10 @@ export class ESPLoader extends EventTarget {
|
|
|
1342
1340
|
* Provides a workaround for the bootloader erase bug on ESP8266.
|
|
1343
1341
|
*/
|
|
1344
1342
|
getEraseSize(offset: number, size: number) {
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1343
|
+
const sectorsPerBlock = 16;
|
|
1344
|
+
const sectorSize = FLASH_SECTOR_SIZE;
|
|
1345
|
+
const numSectors = Math.floor((size + sectorSize - 1) / sectorSize);
|
|
1346
|
+
const startSector = Math.floor(offset / sectorSize);
|
|
1349
1347
|
|
|
1350
1348
|
let headSectors = sectorsPerBlock - (startSector % sectorsPerBlock);
|
|
1351
1349
|
if (numSectors < headSectors) {
|
|
@@ -1397,16 +1395,13 @@ export class ESPLoader extends EventTarget {
|
|
|
1397
1395
|
* ignore errors.
|
|
1398
1396
|
*/
|
|
1399
1397
|
async memFinish(entrypoint = 0) {
|
|
1400
|
-
|
|
1401
|
-
|
|
1398
|
+
const timeout = this.IS_STUB ? DEFAULT_TIMEOUT : MEM_END_ROM_TIMEOUT;
|
|
1399
|
+
const data = pack("<II", entrypoint == 0 ? 1 : 0, entrypoint);
|
|
1402
1400
|
return await this.checkCommand(ESP_MEM_END, data, 0, timeout);
|
|
1403
1401
|
}
|
|
1404
1402
|
|
|
1405
1403
|
async runStub(skipFlashDetection = false): Promise<EspStubLoader> {
|
|
1406
|
-
const stub
|
|
1407
|
-
this.chipFamily,
|
|
1408
|
-
this.chipRevision,
|
|
1409
|
-
);
|
|
1404
|
+
const stub = await getStubCode(this.chipFamily, this.chipRevision);
|
|
1410
1405
|
|
|
1411
1406
|
// No stub available for this chip, return ROM loader
|
|
1412
1407
|
if (stub === null) {
|
|
@@ -1417,32 +1412,29 @@ export class ESPLoader extends EventTarget {
|
|
|
1417
1412
|
}
|
|
1418
1413
|
|
|
1419
1414
|
// We're transferring over USB, right?
|
|
1420
|
-
|
|
1415
|
+
const ramBlock = USB_RAM_BLOCK;
|
|
1421
1416
|
|
|
1422
1417
|
// Upload
|
|
1423
1418
|
this.logger.log("Uploading stub...");
|
|
1424
|
-
for (
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
}
|
|
1436
|
-
await this.memBlock(stub[field].slice(fromOffs, toOffs), seq);
|
|
1419
|
+
for (const field of ["text", "data"] as const) {
|
|
1420
|
+
const fieldData = stub[field];
|
|
1421
|
+
const offset = stub[`${field}_start` as "text_start" | "data_start"];
|
|
1422
|
+
const length = fieldData.length;
|
|
1423
|
+
const blocks = Math.floor((length + ramBlock - 1) / ramBlock);
|
|
1424
|
+
await this.memBegin(length, blocks, ramBlock, offset);
|
|
1425
|
+
for (const seq of Array(blocks).keys()) {
|
|
1426
|
+
const fromOffs = seq * ramBlock;
|
|
1427
|
+
let toOffs = fromOffs + ramBlock;
|
|
1428
|
+
if (toOffs > length) {
|
|
1429
|
+
toOffs = length;
|
|
1437
1430
|
}
|
|
1431
|
+
await this.memBlock(fieldData.slice(fromOffs, toOffs), seq);
|
|
1438
1432
|
}
|
|
1439
1433
|
}
|
|
1440
|
-
await this.memFinish(stub
|
|
1441
|
-
|
|
1442
|
-
let pChar: string;
|
|
1434
|
+
await this.memFinish(stub.entry);
|
|
1443
1435
|
|
|
1444
1436
|
const p = await this.readPacket(500);
|
|
1445
|
-
pChar = String.fromCharCode(...p);
|
|
1437
|
+
const pChar = String.fromCharCode(...p);
|
|
1446
1438
|
|
|
1447
1439
|
if (pChar != "OHAI") {
|
|
1448
1440
|
throw new Error("Failed to start stub. Unexpected response: " + pChar);
|
|
@@ -1464,7 +1456,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1464
1456
|
try {
|
|
1465
1457
|
writer.releaseLock();
|
|
1466
1458
|
} catch (err) {
|
|
1467
|
-
|
|
1459
|
+
this.logger.error(`Ignoring release lock error: ${err}`);
|
|
1468
1460
|
}
|
|
1469
1461
|
}
|
|
1470
1462
|
|
|
@@ -1708,8 +1700,8 @@ export class ESPLoader extends EventTarget {
|
|
|
1708
1700
|
);
|
|
1709
1701
|
|
|
1710
1702
|
// Send read flash command for this chunk
|
|
1711
|
-
|
|
1712
|
-
const [res
|
|
1703
|
+
const pkt = pack("<IIII", currentAddr, chunkSize, 0x1000, 1024);
|
|
1704
|
+
const [res] = await this.checkCommand(ESP_READ_FLASH, pkt);
|
|
1713
1705
|
|
|
1714
1706
|
if (res != 0) {
|
|
1715
1707
|
throw new Error("Failed to read memory: " + res);
|
|
@@ -1798,7 +1790,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1798
1790
|
remainingSize -= chunkSize;
|
|
1799
1791
|
|
|
1800
1792
|
this.logger.debug(
|
|
1801
|
-
`Total progress: 0x${allData.length.toString(16)}
|
|
1793
|
+
`Total progress: 0x${allData.length.toString(16)} from 0x${size.toString(16)} bytes`,
|
|
1802
1794
|
);
|
|
1803
1795
|
}
|
|
1804
1796
|
|
|
@@ -1820,27 +1812,26 @@ class EspStubLoader extends ESPLoader {
|
|
|
1820
1812
|
*/
|
|
1821
1813
|
async memBegin(
|
|
1822
1814
|
size: number,
|
|
1823
|
-
|
|
1824
|
-
|
|
1815
|
+
_blocks: number,
|
|
1816
|
+
_blocksize: number,
|
|
1825
1817
|
offset: number,
|
|
1826
|
-
): Promise<
|
|
1827
|
-
|
|
1818
|
+
): Promise<[number, number[]]> {
|
|
1819
|
+
const stub = await getStubCode(this.chipFamily, this.chipRevision);
|
|
1828
1820
|
|
|
1829
1821
|
// Stub may be null for chips without stub support
|
|
1830
1822
|
if (stub === null) {
|
|
1831
|
-
return;
|
|
1823
|
+
return [0, []];
|
|
1832
1824
|
}
|
|
1833
1825
|
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
stub.text_start,
|
|
1841
|
-
stub.text.length,
|
|
1826
|
+
const load_start = offset;
|
|
1827
|
+
const load_end = offset + size;
|
|
1828
|
+
this.logger.debug(
|
|
1829
|
+
`Load range: ${toHex(load_start, 8)}-${toHex(load_end, 8)}`,
|
|
1830
|
+
);
|
|
1831
|
+
this.logger.debug(
|
|
1832
|
+
`Stub data: ${toHex(stub.data_start, 8)}, len: ${stub.data.length}, text: ${toHex(stub.text_start, 8)}, len: ${stub.text.length}`,
|
|
1842
1833
|
);
|
|
1843
|
-
for (
|
|
1834
|
+
for (const [start, end] of [
|
|
1844
1835
|
[stub.data_start, stub.data_start + stub.data.length],
|
|
1845
1836
|
[stub.text_start, stub.text_start + stub.text.length],
|
|
1846
1837
|
]) {
|
|
@@ -1860,6 +1851,7 @@ class EspStubLoader extends ESPLoader {
|
|
|
1860
1851
|
);
|
|
1861
1852
|
}
|
|
1862
1853
|
}
|
|
1854
|
+
return [0, []];
|
|
1863
1855
|
}
|
|
1864
1856
|
|
|
1865
1857
|
/**
|