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