@@ -83,7 +83,9 @@ func New(busName string) (*Transport, error) {
8383 transport := & Transport {
8484 dev : dev ,
8585 busName : busName ,
86- timeout : 50 * time .Millisecond ,
86+ // Match UART's unified timeout - originally 50ms but increased to 100ms
87+ // for better I2C bus compatibility across different hardware
88+ timeout : 100 * time .Millisecond ,
8789 }
8890
8991 return transport , nil
@@ -142,20 +144,41 @@ func (*Transport) Type() pn532.TransportType {
142144}
143145
144146// checkReady checks if the PN532 is ready by reading the ready status
147+ // Now includes retry logic with exponential backoff for better hardware compatibility
145148func (t * Transport ) checkReady () error {
146- // Use buffer pool for ready status check - small optimization
147- ready := frame .GetSmallBuffer (1 )
148- defer frame .PutBuffer (ready )
149+ const maxRetries = 5
150+ baseDelay := time .Millisecond
149151
150- if err := t .dev .Tx (nil , ready ); err != nil {
151- return fmt .Errorf ("I2C ready check failed: %w" , err )
152- }
152+ var lastErr error
153+ for attempt := 0 ; attempt < maxRetries ; attempt ++ {
154+ // Use buffer pool for ready status check - small optimization
155+ ready := frame .GetSmallBuffer (1 )
156+
157+ err := t .dev .Tx (nil , ready )
158+ if err != nil {
159+ frame .PutBuffer (ready )
160+ lastErr = fmt .Errorf ("I2C ready check failed: %w" , err )
161+ // Exponential backoff: 1ms, 2ms, 4ms, 8ms, 16ms
162+ if attempt < maxRetries - 1 {
163+ time .Sleep (baseDelay * time .Duration (1 << attempt ))
164+ continue
165+ }
166+ return lastErr
167+ }
168+
169+ if ready [0 ] == pn532Ready {
170+ frame .PutBuffer (ready )
171+ return nil
172+ }
153173
154- if ready [0 ] != pn532Ready {
155- return pn532 .NewTransportNotReadyError ("waitReady" , t .busName )
174+ frame .PutBuffer (ready )
175+ // Device not ready yet, wait with backoff
176+ if attempt < maxRetries - 1 {
177+ time .Sleep (baseDelay * time .Duration (1 << attempt ))
178+ }
156179 }
157180
158- return nil
181+ return pn532 . NewTransportNotReadyError ( "checkReady" , t . busName )
159182}
160183
161184// sendFrame sends a frame to the PN532 via I2C
@@ -295,18 +318,18 @@ func (t *Transport) receiveFrameAttempt() (data []byte, shouldRetry bool, err er
295318 return nil , true , nil
296319 }
297320
298- buf , err := t .readFrameData ()
321+ buf , actualLen , err := t .readFrameData ()
299322 if err != nil {
300323 return nil , false , err
301324 }
302325 defer frame .PutBuffer (buf ) // Ensure buffer is returned to pool
303326
304- off , err := t .findI2CFrameStart (buf )
327+ off , err := t .findI2CFrameStart (buf , actualLen )
305328 if err != nil {
306329 return nil , false , err
307330 }
308331
309- frameLen , shouldRetry , err := t .validateI2CFrameLength (buf , off )
332+ frameLen , shouldRetry , err := t .validateI2CFrameLength (buf , off , actualLen )
310333 if err != nil || shouldRetry {
311334 return nil , shouldRetry , err
312335 }
@@ -319,21 +342,99 @@ func (t *Transport) receiveFrameAttempt() (data []byte, shouldRetry bool, err er
319342 return t .extractI2CFrameData (buf , off , frameLen )
320343}
321344
322- // readFrameData reads frame data from I2C using buffer pool
323- func (t * Transport ) readFrameData () ([]byte , error ) {
324- // Use buffer pool for frame reading - major optimization
325- buf := frame .GetBuffer (255 + 7 ) // max data + frame overhead
326- if err := t .dev .Tx (nil , buf ); err != nil {
327- frame .PutBuffer (buf ) // Return to pool on error
328- return nil , fmt .Errorf ("I2C frame data read failed: %w" , err )
345+ // readFrameData reads frame data from I2C using incremental reads
346+ // This fixes the critical bug where we were reading a fixed-size buffer without
347+ // knowing how much data was actually received from the PN532
348+ func (t * Transport ) readFrameData () (buf []byte , actualLen int , err error ) {
349+ // PHASE 1: Read frame header to determine frame size
350+ // Frame header structure: [preamble] [0x00] [0xFF] [LEN] [LCS] = 5 bytes minimum
351+ // We read a bit more to get the TFI byte and start of data
352+ headerSize := 32 // Read enough to get header + some data
353+ headerBuf := frame .GetSmallBuffer (headerSize )
354+
355+ if err := t .dev .Tx (nil , headerBuf ); err != nil {
356+ frame .PutBuffer (headerBuf )
357+ return nil , 0 , fmt .Errorf ("I2C frame header read failed: %w" , err )
329358 }
330359
331- return buf , nil
360+ // Find frame start in header
361+ frameStart := - 1
362+ for i := 0 ; i < headerSize - 1 ; i ++ {
363+ if headerBuf [i ] == 0x00 && headerBuf [i + 1 ] == 0xFF {
364+ frameStart = i + 2 // Point to length byte
365+ break
366+ }
367+ }
368+
369+ if frameStart == - 1 || frameStart + 1 >= headerSize {
370+ frame .PutBuffer (headerBuf )
371+ return nil , 0 , & pn532.TransportError {
372+ Op : "readFrameData" ,
373+ Port : t .busName ,
374+ Err : pn532 .ErrFrameCorrupted ,
375+ Type : pn532 .ErrorTypeTransient ,
376+ Retryable : true ,
377+ }
378+ }
379+
380+ // Parse frame length
381+ frameLen := int (headerBuf [frameStart ])
382+ lengthChecksum := headerBuf [frameStart + 1 ]
383+
384+ // Validate length checksum
385+ if ((frameLen + int (lengthChecksum )) & 0xFF ) != 0 {
386+ frame .PutBuffer (headerBuf )
387+ return nil , 0 , & pn532.TransportError {
388+ Op : "readFrameData" ,
389+ Port : t .busName ,
390+ Err : pn532 .ErrFrameCorrupted ,
391+ Type : pn532 .ErrorTypeTransient ,
392+ Retryable : true ,
393+ }
394+ }
395+
396+ // PHASE 2: Calculate total frame size and check if we need more data
397+ // Total frame: [preamble] [0x00] [0xFF] [LEN] [LCS] [TFI] [data...] [DCS] [postamble]
398+ // = frameStart + 2 (LEN+LCS) + frameLen + 1 (DCS) + 1 (postamble)
399+ totalFrameSize := frameStart + 2 + frameLen + 2
400+
401+ // If header buffer has all the data, use it
402+ if totalFrameSize <= headerSize {
403+ return headerBuf , totalFrameSize , nil
404+ }
405+
406+ // PHASE 3: Need to read more data - allocate bigger buffer and copy header
407+ buf = frame .GetBuffer (totalFrameSize )
408+ copy (buf , headerBuf [:headerSize ])
409+ frame .PutBuffer (headerBuf ) // Return small buffer to pool
410+
411+ // Read remaining data
412+ remainingSize := totalFrameSize - headerSize
413+ remainingBuf := frame .GetSmallBuffer (remainingSize )
414+ defer frame .PutBuffer (remainingBuf )
415+
416+ if err := t .dev .Tx (nil , remainingBuf ); err != nil {
417+ frame .PutBuffer (buf )
418+ return nil , 0 , fmt .Errorf ("I2C remaining frame data read failed: %w" , err )
419+ }
420+
421+ // Copy remaining data to main buffer
422+ copy (buf [headerSize :], remainingBuf [:remainingSize ])
423+
424+ return buf , totalFrameSize , nil
332425}
333426
334427// findI2CFrameStart locates the frame start marker (0x00 0xFF)
335- func (t * Transport ) findI2CFrameStart (buf []byte ) (int , error ) {
336- for off := 0 ; off < len (buf )- 1 ; off ++ {
428+ // CRITICAL FIX: Now accepts actualLen to only search through actual received data
429+ // This prevents false positives from searching uninitialized buffer space
430+ func (t * Transport ) findI2CFrameStart (buf []byte , actualLen int ) (int , error ) {
431+ // Only search through actual data received, not entire buffer
432+ searchLen := actualLen
433+ if searchLen > len (buf ) {
434+ searchLen = len (buf )
435+ }
436+
437+ for off := 0 ; off < searchLen - 1 ; off ++ {
337438 if buf [off ] == 0x00 && buf [off + 1 ] == 0xFF {
338439 return off + 2 , nil // Skip to length byte
339440 }
@@ -348,8 +449,9 @@ func (t *Transport) findI2CFrameStart(buf []byte) (int, error) {
348449}
349450
350451// validateI2CFrameLength validates the frame length and its checksum
351- func (t * Transport ) validateI2CFrameLength (buf []byte , off int ) (frameLen int , shouldRetry bool , err error ) {
352- frameLen , shouldRetry , err = frame .ValidateFrameLength (buf , off - 1 , len (buf ), "receiveFrame" , t .busName )
452+ // CRITICAL FIX: Now uses actualLen instead of len(buf) to avoid reading beyond actual data
453+ func (t * Transport ) validateI2CFrameLength (buf []byte , off , actualLen int ) (frameLen int , shouldRetry bool , err error ) {
454+ frameLen , shouldRetry , err = frame .ValidateFrameLength (buf , off - 1 , actualLen , "receiveFrame" , t .busName )
353455 if err != nil {
354456 return frameLen , shouldRetry , fmt .Errorf ("I2C frame length validation failed: %w" , err )
355457 }
0 commit comments