@@ -673,7 +673,46 @@ bool EspClass::flashEraseSector(uint32_t sector) {
673
673
return rc == 0 ;
674
674
}
675
675
676
+ // Adapted from the old version of `flash_hal_write()` (before 3.0.0), which was used for SPIFFS to allow
677
+ // writing from both unaligned u8 buffers and to an unaligned offset on flash.
678
+ // Updated version re-uses some of the code from RTOS, replacing individual methods for block & page
679
+ // writes with just a single one
680
+ // https://github.com/espressif/ESP8266_RTOS_SDK/blob/master/components/spi_flash/src/spi_flash.c
681
+ // (if necessary, we could follow the esp-idf code and introduce flash chip drivers controling more than just writing methods?)
682
+
683
+ // This is a generic writer that does not cross page boundaries.
684
+ // Offset, data address and size *must* be 4byte aligned.
685
+ static SpiFlashOpResult spi_flash_write_page_break (uint32_t offset, uint32_t *data, size_t size) {
686
+ static constexpr uint32_t PageSize { FLASH_PAGE_SIZE };
687
+ size_t size_page_aligned = PageSize - (offset % PageSize);
688
+
689
+ // most common case, we don't cross a page and simply write the data
690
+ if (size < size_page_aligned) {
691
+ return spi_flash_write (offset, data, size);
692
+ }
693
+
694
+ // otherwise, write the initial part and continue writing breaking each page interval
695
+ SpiFlashOpResult result = SPI_FLASH_RESULT_ERR;
696
+ if ((result = spi_flash_write (offset, data, size_page_aligned)) != SPI_FLASH_RESULT_OK) {
697
+ return result;
698
+ }
699
+
700
+ const auto last_page = (size - size_page_aligned) / PageSize;
701
+ for (uint32_t page = 0 ; page < last_page; ++page) {
702
+ if ((result = spi_flash_write (offset + size_page_aligned, data + (size_page_aligned >> 2 ), PageSize)) != SPI_FLASH_RESULT_OK) {
703
+ return result;
704
+ }
705
+
706
+ size_page_aligned += PageSize;
707
+ }
708
+
709
+ // finally, the remaining data
710
+ return spi_flash_write (offset + size_page_aligned, data + (size_page_aligned >> 2 ), size - size_page_aligned);
711
+ }
712
+
676
713
#if PUYA_SUPPORT
714
+ // Special wrapper for spi_flash_write *only for PUYA flash chips*
715
+ // Already handles paging, could be used as a `spi_flash_write_page_break` replacement
677
716
static SpiFlashOpResult spi_flash_write_puya (uint32_t offset, uint32_t *data, size_t size) {
678
717
if (data == nullptr ) {
679
718
return SPI_FLASH_RESULT_ERR;
@@ -720,230 +759,165 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
720
759
}
721
760
#endif
722
761
723
- bool EspClass::flashReplaceBlock (uint32_t address, const uint8_t *value, uint32_t byteCount) {
724
- uint32_t alignedAddress = (address & ~3 );
725
- uint32_t alignmentOffset = address - alignedAddress;
762
+ static constexpr size_t Alignment { 4 };
726
763
727
- if (alignedAddress != ((address + byteCount - 1 ) & ~3 )) {
728
- // Only one 4 byte block is supported
729
- return false ;
730
- }
731
- #if PUYA_SUPPORT
732
- if (getFlashChipVendorId () == SPI_FLASH_VENDOR_PUYA) {
733
- uint8_t tempData[4 ] __attribute__ ((aligned (4 )));
734
- if (spi_flash_read (alignedAddress, (uint32_t *)tempData, 4 ) != SPI_FLASH_RESULT_OK) {
735
- return false ;
736
- }
737
- for (size_t i = 0 ; i < byteCount; i++) {
738
- tempData[i + alignmentOffset] &= value[i];
739
- }
740
- if (spi_flash_write (alignedAddress, (uint32_t *)tempData, 4 ) != SPI_FLASH_RESULT_OK) {
741
- return false ;
742
- }
743
- }
744
- else
745
- #endif // PUYA_SUPPORT
746
- {
747
- uint32_t tempData;
748
- if (spi_flash_read (alignedAddress, &tempData, 4 ) != SPI_FLASH_RESULT_OK) {
749
- return false ;
750
- }
751
- memcpy ((uint8_t *)&tempData + alignmentOffset, value, byteCount);
752
- if (spi_flash_write (alignedAddress, &tempData, 4 ) != SPI_FLASH_RESULT_OK) {
753
- return false ;
754
- }
755
- }
756
- return true ;
764
+ template <typename T>
765
+ static T aligned (T value) {
766
+ static constexpr auto Mask = Alignment - 1 ;
767
+ return (value + Mask) & ~Mask;
768
+ }
769
+
770
+ template <typename T>
771
+ static T alignBefore (T value) {
772
+ return aligned (value) - Alignment;
773
+ }
774
+
775
+ static bool isAlignedAddress (uint32_t address) {
776
+ return (address & (Alignment - 1 )) == 0 ;
777
+ }
778
+
779
+ static bool isAlignedSize (size_t size) {
780
+ return (size & (Alignment - 1 )) == 0 ;
781
+ }
782
+
783
+ static bool isAlignedPointer (const uint8_t *ptr) {
784
+ return isAlignedAddress (reinterpret_cast <uint32_t >(ptr));
757
785
}
758
786
787
+
759
788
size_t EspClass::flashWriteUnalignedMemory (uint32_t address, const uint8_t *data, size_t size) {
760
- size_t sizeLeft = (size & ~3 );
761
- size_t currentOffset = 0 ;
762
- // Memory is unaligned, so we need to copy it to an aligned buffer
763
- uint32_t alignedData[FLASH_PAGE_SIZE / sizeof (uint32_t )] __attribute__ ((aligned (4 )));
764
- // Handle page boundary
765
- bool pageBreak = ((address % 4 ) != 0 ) && ((address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1 ) / FLASH_PAGE_SIZE));
789
+ auto flash_write = [](uint32_t address, uint8_t *data, size_t size) {
790
+ return spi_flash_write (address, reinterpret_cast <uint32_t *>(data), size) == SPI_FLASH_RESULT_OK;
791
+ };
792
+
793
+ auto flash_read = [](uint32_t address, uint8_t *data, size_t size) {
794
+ return spi_flash_read (address, reinterpret_cast <uint32_t *>(data), size) == SPI_FLASH_RESULT_OK;
795
+ };
766
796
767
- if (pageBreak) {
768
- size_t byteCount = 4 - (address % 4 ) ;
797
+ constexpr size_t BufferSize { FLASH_PAGE_SIZE };
798
+ alignas ( alignof ( uint32_t )) uint8_t buf[BufferSize] ;
769
799
770
- if (!flashReplaceBlock (address, data, byteCount)) {
800
+ size_t written = 0 ;
801
+
802
+ if (!isAlignedAddress (address)) {
803
+ auto before_address = alignBefore (address);
804
+ auto offset = address - before_address;
805
+ auto wlen = std::min (Alignment - offset, size);
806
+
807
+ if (!flash_read (before_address, &buf[0 ], Alignment)) {
771
808
return 0 ;
772
809
}
773
- // We will now have aligned address, so we can cross page boundaries
774
- currentOffset += byteCount;
775
- // Realign size to 4
776
- sizeLeft = (size - byteCount) & ~3 ;
777
- }
778
810
779
- while (sizeLeft) {
780
- size_t willCopy = std::min (sizeLeft, sizeof (alignedData));
781
- memcpy (alignedData, data + currentOffset, willCopy);
782
- // We now have address, data and size aligned to 4 bytes, so we can use aligned write
783
- if (!flashWrite (address + currentOffset, alignedData, willCopy))
784
- {
811
+ #if PUYA_SUPPORT
812
+ if (getFlashChipVendorId () == SPI_FLASH_VENDOR_PUYA) {
813
+ for (size_t i = 0 ; i < wlen ; ++i) {
814
+ buf[offset + i] &= data[i];
815
+ }
816
+ } else {
817
+ #endif
818
+ memcpy (&buf[offset], data, wlen);
819
+ #if PUYA_SUPPORT
820
+ }
821
+ #endif
822
+
823
+ if (!flash_write (before_address, &buf[0 ], Alignment)) {
785
824
return 0 ;
786
825
}
787
- sizeLeft -= willCopy;
788
- currentOffset += willCopy;
826
+
827
+ address += wlen;
828
+ data += wlen;
829
+ written += wlen;
830
+ size -= wlen;
789
831
}
790
832
791
- return currentOffset;
792
- }
833
+ while (size > 0 ) {
834
+ auto len = std::min (size, BufferSize);
835
+ auto wlen = aligned (len);
793
836
794
- bool EspClass::flashWritePageBreak (uint32_t address, const uint8_t *data, size_t size) {
795
- if (size > 4 ) {
796
- return false ;
797
- }
798
- size_t pageLeft = FLASH_PAGE_SIZE - (address % FLASH_PAGE_SIZE);
799
- size_t offset = 0 ;
800
- size_t sizeLeft = size;
801
- if (pageLeft > 3 ) {
802
- return false ;
803
- }
837
+ if (wlen != len) {
838
+ auto partial = wlen - Alignment;
839
+ if (!flash_read (address + partial, &buf[partial], Alignment)) {
840
+ return written;
841
+ }
842
+ }
804
843
805
- if (!flashReplaceBlock (address, data, pageLeft)) {
806
- return false ;
807
- }
808
- offset += pageLeft;
809
- sizeLeft -= pageLeft;
810
- // We replaced last 4-byte block of the page, now we write the remainder in next page
811
- if (!flashReplaceBlock (address + offset, data + offset, sizeLeft)) {
812
- return false ;
844
+ memcpy (&buf[0 ], data, len);
845
+ if (!flashWrite (address, reinterpret_cast <const uint32_t *>(&buf[0 ]), wlen)) {
846
+ return written;
847
+ }
848
+
849
+ address += len;
850
+ data += len;
851
+ written += len;
852
+ size -= len;
813
853
}
814
- return true ;
854
+
855
+ return written;
815
856
}
816
857
817
858
bool EspClass::flashWrite (uint32_t address, const uint32_t *data, size_t size) {
818
- SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
819
- bool pageBreak = ((address % 4 ) != 0 && (address / FLASH_PAGE_SIZE) != ((address + size - 1 ) / FLASH_PAGE_SIZE));
820
-
821
- if ((uintptr_t )data % 4 != 0 || size % 4 != 0 || pageBreak) {
822
- return false ;
823
- }
859
+ SpiFlashOpResult result;
824
860
#if PUYA_SUPPORT
825
861
if (getFlashChipVendorId () == SPI_FLASH_VENDOR_PUYA) {
826
- rc = spi_flash_write_puya (address, const_cast <uint32_t *>(data), size);
862
+ result = spi_flash_write_puya (address, const_cast <uint32_t *>(data), size);
827
863
}
828
864
else
829
- #endif // PUYA_SUPPORT
865
+ #endif
830
866
{
831
- rc = spi_flash_write (address, const_cast <uint32_t *>(data), size);
867
+ result = spi_flash_write_page_break (address, const_cast <uint32_t *>(data), size);
832
868
}
833
- return rc == SPI_FLASH_RESULT_OK;
869
+ return result == SPI_FLASH_RESULT_OK;
834
870
}
835
871
836
872
bool EspClass::flashWrite (uint32_t address, const uint8_t *data, size_t size) {
837
- if (size == 0 ) {
838
- return true ;
839
- }
840
-
841
- size_t sizeLeft = size & ~3 ;
842
- size_t currentOffset = 0 ;
843
-
844
- if (sizeLeft) {
845
- if ((uintptr_t )data % 4 != 0 ) {
846
- size_t written = flashWriteUnalignedMemory (address, data, size);
847
- if (!written) {
848
- return false ;
849
- }
850
- currentOffset += written;
851
- sizeLeft -= written;
852
- } else {
853
- bool pageBreak = ((address % 4 ) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1 ) / FLASH_PAGE_SIZE));
854
-
855
- if (pageBreak) {
856
- while (sizeLeft) {
857
- // We cannot cross page boundary, but the write must be 4 byte aligned,
858
- // so this is the maximum amount we can write
859
- size_t pageBoundary = (FLASH_PAGE_SIZE - ((address + currentOffset) % FLASH_PAGE_SIZE)) & ~3 ;
860
-
861
- if (sizeLeft > pageBoundary) {
862
- // Aligned write up to page boundary
863
- if (!flashWrite (address + currentOffset, (uint32_t *)(data + currentOffset), pageBoundary)) {
864
- return false ;
865
- }
866
- currentOffset += pageBoundary;
867
- sizeLeft -= pageBoundary;
868
- // Cross the page boundary
869
- if (!flashWritePageBreak (address + currentOffset, data + currentOffset, 4 )) {
870
- return false ;
871
- }
872
- currentOffset += 4 ;
873
- sizeLeft -= 4 ;
874
- } else {
875
- // We do not cross page boundary
876
- if (!flashWrite (address + currentOffset, (uint32_t *)(data + currentOffset), sizeLeft)) {
877
- return false ;
878
- }
879
- currentOffset += sizeLeft;
880
- sizeLeft = 0 ;
881
- }
882
- }
883
- } else {
884
- // Pointer is properly aligned and write does not cross page boundary,
885
- // so use aligned write
886
- if (!flashWrite (address, (uint32_t *)data, sizeLeft)) {
887
- return false ;
888
- }
889
- currentOffset = sizeLeft;
890
- sizeLeft = 0 ;
891
- }
892
- }
893
- }
894
- sizeLeft = size - currentOffset;
895
- if (sizeLeft > 0 ) {
896
- // Size was not aligned, so we have some bytes left to write, we also need to recheck for
897
- // page boundary crossing
898
- bool pageBreak = ((address % 4 ) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1 ) / FLASH_PAGE_SIZE));
899
-
900
- if (pageBreak) {
901
- // Cross the page boundary
902
- if (!flashWritePageBreak (address + currentOffset, data + currentOffset, sizeLeft)) {
903
- return false ;
904
- }
905
- } else {
906
- // Just write partial block
907
- flashReplaceBlock (address + currentOffset, data + currentOffset, sizeLeft);
873
+ if (data && size) {
874
+ if (!isAlignedAddress (address)
875
+ || !isAlignedPointer (data)
876
+ || !isAlignedSize (size))
877
+ {
878
+ return flashWriteUnalignedMemory (address, data, size) == size;
908
879
}
880
+
881
+ return flashWrite (address, reinterpret_cast <const uint32_t *>(data), size);
909
882
}
910
883
911
- return true ;
884
+ return false ;
912
885
}
913
886
914
887
bool EspClass::flashRead (uint32_t address, uint8_t *data, size_t size) {
915
888
size_t sizeAligned = size & ~3 ;
916
889
size_t currentOffset = 0 ;
917
890
918
891
if ((uintptr_t )data % 4 != 0 ) {
919
- uint32_t alignedData[FLASH_PAGE_SIZE / sizeof (uint32_t )] __attribute__ ((aligned (4 )));
892
+ constexpr size_t BufferSize { FLASH_PAGE_SIZE / sizeof (uint32_t ) };
893
+ alignas (alignof (uint32_t )) uint32_t buf[BufferSize];
920
894
size_t sizeLeft = sizeAligned;
921
895
922
896
while (sizeLeft) {
923
- size_t willCopy = std::min (sizeLeft, sizeof (alignedData) );
897
+ size_t willCopy = std::min (sizeLeft, BufferSize );
924
898
// We read to our aligned buffer and then copy to data
925
- if (!flashRead (address + currentOffset, alignedData , willCopy))
899
+ if (!flashRead (address + currentOffset, &buf[ 0 ] , willCopy))
926
900
{
927
901
return false ;
928
902
}
929
- memcpy (data + currentOffset, alignedData , willCopy);
903
+ memcpy (data + currentOffset, &buf[ 0 ] , willCopy);
930
904
sizeLeft -= willCopy;
931
905
currentOffset += willCopy;
932
906
}
933
907
} else {
934
908
// Pointer is properly aligned, so use aligned read
935
- if (!flashRead (address, ( uint32_t *) data, sizeAligned)) {
909
+ if (!flashRead (address, reinterpret_cast < uint32_t *>( data) , sizeAligned)) {
936
910
return false ;
937
911
}
938
912
currentOffset = sizeAligned;
939
913
}
940
914
941
915
if (currentOffset < size) {
942
916
uint32_t tempData;
943
- if (spi_flash_read (address + currentOffset, &tempData, 4 ) != SPI_FLASH_RESULT_OK) {
917
+ if (spi_flash_read (address + currentOffset, &tempData, sizeof (tempData) ) != SPI_FLASH_RESULT_OK) {
944
918
return false ;
945
919
}
946
- memcpy (( uint8_t *) data + currentOffset, &tempData, size - currentOffset);
920
+ memcpy (data + currentOffset, &tempData, size - currentOffset);
947
921
}
948
922
949
923
return true ;
@@ -973,7 +947,7 @@ String EspClass::getSketchMD5()
973
947
md5.begin ();
974
948
while ( lengthLeft > 0 ) {
975
949
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
976
- if (!flashRead (offset, reinterpret_cast <uint32_t *>(buf.get ()), (readBytes + 3 ) & ~3 )) {
950
+ if (!flashRead (offset, reinterpret_cast <uint32_t *>(buf.get ()), (readBytes + 3 ) & ~3 )) {
977
951
return emptyString;
978
952
}
979
953
md5.add (buf.get (), readBytes);
0 commit comments