@@ -59,6 +59,9 @@ const (
5959 // flashSectorSize is the minimum flash erase unit.
6060 flashSectorSize uint32 = 0x1000 // 4KB
6161
62+ // readFlashBlockSize is the block size for read flash operations.
63+ readFlashBlockSize uint32 = 0x1000 // 4KB
64+
6265 // espImageMagic is the first byte of a valid ESP firmware image.
6366 espImageMagic byte = 0xE9
6467
@@ -79,7 +82,8 @@ const (
7982type conn struct {
8083 port serial.Port
8184 reader * slipReader
82- stub bool
85+ stub bool
86+ usesUSB bool // set for USB-OTG and USB-JTAG/Serial connections
8387 // supportsEncryptedFlash indicates the ROM supports the 5th parameter
8488 // (encrypted flag) in flash_begin/flash_defl_begin commands.
8589 // Set based on chip type after detection.
@@ -91,6 +95,11 @@ func (c *conn) isStub() bool {
9195 return c .stub
9296}
9397
98+ // setUSB sets whether the connection uses USB-OTG or USB-JTAG endpoints.
99+ func (c * conn ) setUSB (v bool ) {
100+ c .usesUSB = v
101+ }
102+
94103// setSupportsEncryptedFlash sets whether the ROM supports encrypted flash commands.
95104func (c * conn ) setSupportsEncryptedFlash (v bool ) {
96105 c .supportsEncryptedFlash = v
@@ -125,8 +134,20 @@ func (c *conn) sendCommand(opcode byte, data []byte, chk uint32) error {
125134 copy (pkt [8 :], data )
126135
127136 frame := slipEncode (pkt )
128- _ , err := c .port .Write (frame )
129- return err
137+ // USB CDC endpoints have limited buffer sizes. Writing large SLIP frames
138+ // in one shot can overflow the endpoint buffer and cause data loss.
139+ // Chunk writes to 64 bytes (standard USB Full Speed bulk endpoint size).
140+ const maxChunk = 64
141+ for off := 0 ; off < len (frame ); off += maxChunk {
142+ end := off + maxChunk
143+ if end > len (frame ) {
144+ end = len (frame )
145+ }
146+ if _ , err := c .port .Write (frame [off :end ]); err != nil {
147+ return err
148+ }
149+ }
150+ return nil
130151}
131152
132153// commandResponse represents a parsed response from the ESP device.
@@ -542,6 +563,51 @@ func (c *conn) eraseRegion(offset, size uint32) error {
542563 return err
543564}
544565
566+ // readFlash reads data from flash memory (stub-only).
567+ func (c * conn ) readFlash (offset , size uint32 ) ([]byte , error ) {
568+ data := make ([]byte , 16 )
569+ binary .LittleEndian .PutUint32 (data [0 :4 ], offset )
570+ binary .LittleEndian .PutUint32 (data [4 :8 ], size )
571+ binary .LittleEndian .PutUint32 (data [8 :12 ], readFlashBlockSize )
572+ binary .LittleEndian .PutUint32 (data [12 :16 ], 64 ) // max_inflight (stub clamps to 1)
573+
574+ if _ , err := c .checkCommand ("read flash" , cmdReadFlash , data , 0 , defaultTimeout , 0 ); err != nil {
575+ return nil , err
576+ }
577+
578+ blockTimeout := defaultTimeout + time .Duration (readFlashBlockSize / 256 )* 100 * time .Millisecond
579+ numBlocks := (size + readFlashBlockSize - 1 ) / readFlashBlockSize
580+ result := make ([]byte , 0 , size )
581+
582+ for i := uint32 (0 ); i < numBlocks ; i ++ {
583+ // Read SLIP-framed data block
584+ block , err := c .reader .ReadFrame (blockTimeout )
585+ if err != nil {
586+ return nil , fmt .Errorf ("read flash block %d/%d: %w" , i + 1 , numBlocks , err )
587+ }
588+ result = append (result , block ... )
589+
590+ // Send ACK: cumulative bytes received (SLIP-framed)
591+ ack := make ([]byte , 4 )
592+ binary .LittleEndian .PutUint32 (ack , uint32 (len (result )))
593+ ackFrame := slipEncode (ack )
594+ if _ , err := c .port .Write (ackFrame ); err != nil {
595+ return nil , fmt .Errorf ("read flash ACK %d/%d: %w" , i + 1 , numBlocks , err )
596+ }
597+ }
598+
599+ // Read final 16-byte MD5 digest (SLIP-framed)
600+ _ , err := c .reader .ReadFrame (defaultTimeout )
601+ if err != nil {
602+ return nil , fmt .Errorf ("read flash MD5: %w" , err )
603+ }
604+
605+ if uint32 (len (result )) > size {
606+ result = result [:size ]
607+ }
608+ return result , nil
609+ }
610+
545611// flashWriteSize returns the appropriate block size based on loader type.
546612func (c * conn ) flashWriteSize () uint32 {
547613 if c .stub {
@@ -602,17 +668,24 @@ func (c *conn) loadStub(s *stub) error {
602668
603669// uploadToRAM writes a binary segment to the device's RAM via mem_begin/mem_data.
604670func (c * conn ) uploadToRAM (data []byte , addr uint32 ) error {
671+ // USB CDC endpoints have limited buffer sizes. Use 1KB blocks for
672+ // USB connections instead of the default 6KB to avoid timeout.
673+ blockSize := espRAMBlock
674+ if c .usesUSB {
675+ blockSize = 0x400 // 1KB
676+ }
677+
605678 dataLen := uint32 (len (data ))
606- numBlocks := (dataLen + espRAMBlock - 1 ) / espRAMBlock
679+ numBlocks := (dataLen + blockSize - 1 ) / blockSize
607680
608- if err := c .memBegin (dataLen , numBlocks , espRAMBlock , addr ); err != nil {
681+ if err := c .memBegin (dataLen , numBlocks , blockSize , addr ); err != nil {
609682 return err
610683 }
611684
612685 seq := uint32 (0 )
613686 offset := uint32 (0 )
614687 for offset < dataLen {
615- end := offset + espRAMBlock
688+ end := offset + blockSize
616689 if end > dataLen {
617690 end = dataLen
618691 }
0 commit comments