@@ -625,25 +625,48 @@ bool NFCLayerV::reset_to_ready(const PICC* picc)
625625 return rx[0 ] == 0x00 ;
626626}
627627
628- void NFCLayerV::probe_memory_layout (m5::nfc::v::PICC & picc)
628+ // Read one block while addressing the PICC by UID, which needs nothing the inventory did not give
629+ // us. Blocks past the first 256 need the extended command, which not every PICC carries.
630+ bool NFCLayerV::probe_read_block (const m5::nfc::v::PICC & picc, const uint16_t block, uint8_t * rx, uint16_t & rx_len)
629631{
630- // Read block zero while addressing the PICC by UID, which needs nothing we do not have yet
632+ if (block > 0xFF ) {
633+ uint8_t tmp[32 ]{};
634+ if (!read_block_ext (tmp, picc, block)) {
635+ return false ;
636+ }
637+ rx_len = std::min<uint16_t >(rx_len, sizeof (tmp));
638+ memcpy (rx, tmp, rx_len);
639+ return true ;
640+ }
641+
631642 uint8_t frame[2 + 8 + 1 ]{};
632643 make_frame (frame, address_flag | data_rate_flag, m5::stl::to_underlying (Command::ReadSingleBlock), &picc);
633- frame[10 ] = 0x00 ;
644+ frame[10 ] = static_cast < uint8_t >(block) ;
634645
635646 uint8_t rbuf[32 + 1 ]{};
636- uint16_t rx_len = sizeof (rbuf);
637- const bool read =
638- _impl->transceive (rbuf, rx_len, frame, sizeof (frame), TIMEOUT_READ_SINGLE_BLOCK , modulationMode ()) &&
639- rx_len > 1 && rbuf[0 ] == 0x00 ;
647+ uint16_t len = sizeof (rbuf);
648+ if (!_impl->transceive (rbuf, len, frame, sizeof (frame), TIMEOUT_READ_SINGLE_BLOCK , modulationMode ()) || len < 2 ||
649+ rbuf[0 ] != 0x00 ) {
650+ return false ;
651+ }
652+ // The answer carries the flags byte and then the block itself
653+ rx_len = std::min<uint16_t >(rx_len, len - 1 );
654+ memcpy (rx, rbuf + 1 , rx_len);
655+ return true ;
656+ }
640657
641- // The answer carries the flags byte and then one block, so its length gives the block size away
658+ void NFCLayerV::probe_memory_layout (m5::nfc::v::PICC & picc)
659+ {
660+ uint8_t cc[8 ]{};
661+ uint16_t got = sizeof (cc);
662+ const bool read = probe_read_block (picc, 0 , cc, got);
663+
664+ // One block came back, so its length gives the block size away
642665 if (!picc.block_size ) {
643666 if (!read) {
644667 M5_LIB_LOGW (" Could not read block 0; assuming %u byte blocks" , DEFAULT_BLOCK_SIZE );
645668 }
646- picc.block_size = read ? static_cast <uint8_t >(rx_len - 1 ) : DEFAULT_BLOCK_SIZE ;
669+ picc.block_size = read ? static_cast <uint8_t >(got ) : DEFAULT_BLOCK_SIZE ;
647670 }
648671
649672 if (picc.blocks ) {
@@ -654,29 +677,30 @@ void NFCLayerV::probe_memory_layout(m5::nfc::v::PICC& picc)
654677 // eight when MLEN needs more room than one byte, which it signals by leaving the short MLEN
655678 // zero. The second half then lives in the next block unless the blocks are big enough to hold
656679 // it all. MLEN measures the memory in units of 8 bytes (NFC Forum T5T 4.3.1.17).
657- uint8_t cc[8 ]{};
658- uint32_t cc_len{};
659- if (read) {
660- cc_len = std::min<uint32_t >(rx_len - 1 , sizeof (cc));
661- memcpy (cc, rbuf + 1 , cc_len);
662- }
680+ uint16_t cc_len = read ? got : 0 ;
663681 if (cc_len >= 4 && (cc[0 ] == 0xE1 || cc[0 ] == 0xE2 ) && !cc[2 ] && cc_len < sizeof (cc)) {
664- frame[10 ] = 0x01 ;
665- uint16_t len = sizeof (rbuf);
666- const bool more =
667- _impl->transceive (rbuf, len, frame, sizeof (frame), TIMEOUT_READ_SINGLE_BLOCK , modulationMode ()) &&
668- len > 1 && rbuf[0 ] == 0x00 ;
669- if (more) {
670- const uint32_t take = std::min<uint32_t >(len - 1 , sizeof (cc) - cc_len);
671- memcpy (cc + cc_len, rbuf + 1 , take);
672- cc_len += take;
682+ uint16_t more = sizeof (cc) - cc_len;
683+ if (probe_read_block (picc, 1 , cc + cc_len, more)) {
684+ cc_len += more;
673685 }
674686 }
675687
676688 if (cc_len >= 4 && (cc[0 ] == 0xE1 || cc[0 ] == 0xE2 )) {
677689 const uint32_t mlen = cc[2 ] ? cc[2 ] : (cc_len >= 8 ? (((uint32_t )cc[6 ] << 8 ) | cc[7 ]) : 0U );
678690 picc.blocks = static_cast <uint16_t >(mlen * 8U / picc.block_size );
679691 }
692+
693+ // A container can be stale, or written by something that got the units wrong, so make sure the
694+ // last block it claims really answers before taking its word for it
695+ if (picc.blocks ) {
696+ uint8_t tmp[32 ]{};
697+ uint16_t tmp_len = sizeof (tmp);
698+ if (!probe_read_block (picc, picc.blocks - 1 , tmp, tmp_len)) {
699+ M5_LIB_LOGW (" The capability container claims %u blocks but the last one does not answer" , picc.blocks );
700+ picc.blocks = 0 ;
701+ }
702+ }
703+
680704 if (!picc.blocks ) {
681705 // Neither the PICC nor a capability container said how big it is, so the caller gets a guess
682706 M5_LIB_LOGW (" Assuming %u blocks of %u bytes; reads past the end of %s will fail" , DEFAULT_BLOCKS ,
0 commit comments