HiPi

 view release on metacpan or  search on metacpan

lib/HiPi/Interface/MFRC522.pm  view on Meta::CPAN

	}
	
	if ($i == 0) {        
		return ( MFRC522_STATUS_TIMEOUT, [] );
	}
	
	# Stop now if any errors except collisions were detected.
	my $errorRegValue = $self->read_register(MFRC522_REG_ErrorReg); # // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr
	if ($errorRegValue & 0x13) {	 #// BufferOvfl ParityErr ProtocolErr
		return ( MFRC522_STATUS_ERROR, [] );
	}
  
	my $check_validBits = 0;
	
    my @rdata = ();
    
    # warn qq(something's out there .....);
    
	# If the caller wants data back, get it from the MFRC522.
	if ($getlen) {
        
        # warn qq(let's read what it says .....);
		my $haslen = $self->read_register(MFRC522_REG_FIFOLevelReg);	#// Number of bytes in the FIFO
		
        @rdata = $self->read_fifo($haslen);
        
        $getlen = $haslen;
        
        if ($rxalign) {		#// Only update bit positions rxAlign..7 in values[0]
            #// Create bit mask for bit positions rxAlign..7
            my $mask = (0xFF << $rxalign) & 0xFF;
            
            # // Apply mask to both current value of values[0] and the new data in value.
            $rdata[0] = ( $rdata[0] & ~$mask) | ( $rdata[0] & $mask );
        }
        
        $check_validBits = $self->read_register(MFRC522_REG_ControlReg) & 0x07;		#// RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid.
        $validbits = $check_validBits if $validbits;
	}
    
	# // Tell about collisions
	if ($errorRegValue & 0x08) {		#  CollErr
		return ( MFRC522_STATUS_COLLISION, \@rdata, $validbits );
	}
	
	# // Perform CRC_A validation if requested.
	if ( @rdata && $checkcrc) {
        
		# // In this case a MIFARE Classic NAK is not OK.
		if ( $getlen == 1 && $check_validBits == 4) {
			return ( MFRC522_STATUS_MIFARE_NACK , \@rdata, $validbits);
		}
		#// We need at least the CRC_A value and all 8 bits of the last byte must be received.
		
        if ( $getlen < 2 || $check_validBits != 0) {
			return ( MFRC522_STATUS_CRC_WRONG, \@rdata, $validbits );
		}
		
        #// Verify CRC_A - do our own calculation and store the control in controlBuffer.
		         
        my @crcdata = @rdata;
        pop @crcdata; pop @crcdata;
        
        my ( $crcstatus, $cbuffer1, $cbuffer2 ) = $self->pcd_calculate_crc( \@crcdata );
        
		if ($crcstatus != MFRC522_STATUS_OK) {
			return ($crcstatus, \@rdata, $check_validBits) ;
		}
		if (($rdata[-2] != $cbuffer1 ) || ($rdata[-1] != $cbuffer2)) {
			return (MFRC522_STATUS_CRC_WRONG, \@rdata, $check_validBits) ;
		}
	}
	
	return ( MFRC522_STATUS_OK, \@rdata, $check_validBits );
}

sub pcd_calculate_crc {
    my($self, $dataref) = @_;
    
    $self->write_register(MFRC522_REG_CommandReg, MFRC522_IDLE);	#// Stop any active command.
	$self->write_register(MFRC522_REG_DivIrqReg, 0x04);             #// Clear the CRCIRq interrupt request bit
	$self->write_register(MFRC522_REG_FIFOLevelReg, 0x80);			#// FlushBuffer = 1, FIFO initialization
	$self->write_register(MFRC522_REG_FIFODataReg, @$dataref);	    #// Write data to the FIFO
	$self->write_register(MFRC522_REG_CommandReg, MFRC522_CALCCRC);	#// Start the calculation
	
	#// Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73?s.
	#// TODO check/modify for other architectures than Arduino Uno 16bit

	#// Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73us.
	for (my $i = 5000; $i > 0; $i--) {
		#// DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved
		my $checkirq = $self->read_register(MFRC522_REG_DivIrqReg);
		if ($checkirq & 0x04) {									            # // CRCIRq bit set - calculation done
			$self->write_register(MFRC522_REG_CommandReg, MFRC522_IDLE);	# // Stop calculating CRC for new content in the FIFO.
			# // Transfer the result from the registers to the result buffer
			my $r1 = $self->read_register(MFRC522_REG_CRCResultRegL);
			my $r2 = $self->read_register(MFRC522_REG_CRCResultRegH);
			return ( MFRC522_STATUS_OK, $r1, $r2 );
		}
        $self->sleep_microseconds( $self->scanwait );
	}
	
	return ( MFRC522_STATUS_TIMEOUT, undef, undef );
}

sub picc_is_new_tag_present  {
    my $self = shift;
	
	#// Reset baud rates
	$self->write_register(MFRC522_REG_TxModeReg, 0x00);
	$self->write_register(MFRC522_REG_RxModeReg, 0x00);
	#// Reset ModWidthReg
	$self->write_register(MFRC522_REG_ModWidthReg, 0x26);
	    
    my( $status, $data, $validbits ) = $self->picc_request_active();
    
    my $result = ( $status == MFRC522_STATUS_OK || $status == MFRC522_STATUS_COLLISION );
    return $result;
}

sub picc_request_active {
    my($self) = @_;
    my( $status, $data, $validbits ) = $self->picc_request_idl_or_wup( MIFARE_REQIDL, 2 );
    return ( $status, $data, $validbits );

lib/HiPi/Interface/MFRC522.pm  view on Meta::CPAN

    my($self, $command, $getlen) = @_;
    
    # command is MIFARE_REQIDL or MIFARE_REQALL
    
	$self->clear_bit_mask(MFRC522_REG_CollReg, 0x80);	#// ValuesAfterColl=1 => Bits received after collision are cleared.
	
    my $validbits = 7;									#// For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0]
    my($status, $data);
    ( $status, $data, $validbits ) = $self->pcd_transceive_data( $command , $validbits, $getlen );
	
    if ($status != MFRC522_STATUS_OK) {
		return ( $status, undef, undef );
	}
    
	if (scalar @$data != 2 || $validbits != 0) {		#// ATQA must be exactly 16 bits.
		return ( MFRC522_STATUS_ERROR, undef, undef );
	}
    
	return ( $status, $data, $validbits );
}

sub pcd_transceive_data {
    my($self, $senddata, $validbitsin, $getlen, $rxalign, $checkcrc ) = @_;
    
    my $sendref = ( ref($senddata) eq 'ARRAY') ? $senddata : [ $senddata ];
    
    my $waitirq = 0x30;
    
    # // we sometimes pass in null bytes
    for (my $i = 0; $i < @$sendref; $i ++ ) {
        $sendref->[$i] //= 0;
    }
    
    my ($status, $data, $validbits) = $self->pcd_communicate_with_picc(
        MFRC522_TRANSCEIVE, $waitirq, $sendref, $getlen, $validbitsin, $rxalign, $checkcrc 
    );
    
    return ($status, $data, $validbits);
}

sub pcd_mifare_transceive {
    my($self, $data, $accepttimeout) = @_;
    
    $accepttimeout = ( $accepttimeout ) ? 1 : 0;
    
	#// Copy sendData[] to cmdBuffer[] and add CRC_A
	    
    my ( $crcstatus, $cbuffer1, $cbuffer2 ) = $self->pcd_calculate_crc( $data );
    
	if ($crcstatus != MFRC522_STATUS_OK) { 
		return ( $crcstatus, undef );
	}
	    
    my @sendbuffer = @$data;
    push( @sendbuffer, $cbuffer1, $cbuffer2 );
	
	#// Transceive the data, store the reply in cmdBuffer[]
	my $waitIRq = 0x30;		#// RxIRq and IdleIRq
	my $validBits = 0;
    my $getlen = 16;
    my ($status, $piccdata, $validbitsout) = $self->pcd_communicate_with_picc(
        MFRC522_TRANSCEIVE, $waitIRq, \@sendbuffer, $getlen, $validBits 
    );
    
	if ($accepttimeout && $status == MFRC522_STATUS_TIMEOUT) {
		return (MFRC522_STATUS_OK, [] );
	}
	if ($status != MFRC522_STATUS_OK) {
		return ( $status, undef );
	}
	#// The PICC must reply with a 4 bit ACK
    
    my $returnbuffersize = scalar @$piccdata;
    
	if ($returnbuffersize != 1 || $validbitsout != 4) {
        
		return ( MFRC522_STATUS_ERROR, undef );
	}
	if ($piccdata->[0] != MIFARE_MF_ACK) {
		return ( MFRC522_STATUS_MIFARE_NACK, undef );
	}
    
	return ($status, $piccdata, $validbitsout);
    
}

sub picc_read_tag_serial {
    my $self = shift;
    my( $status, $uid ) = $self->picc_select;
    
    my $serialstring = '';
    if( $status == MFRC522_STATUS_OK ) {
        for (my $i = 0; $i < $uid->{'size'}; $i ++ ) {
            $serialstring .= '-' if $serialstring;
            $serialstring .= sprintf('%02X', $uid->{'data'}->[$i]);
        }
    }
    
    return ( $status, $uid, $serialstring );
}

sub picc_select {
    my ($self, $uid, $validbits ) = @_;
    
    $uid //= {
        size => 0,
        data => [],
        sak  => 0,
    };
    
    $validbits ||= 0;
    
#   bool uidComplete;
#	bool selectDone;
#	bool useCascadeTag;
#	byte cascadeLevel = 1;
#	MFRC522::StatusCode result;
#	byte count;
#	byte checkBit;
#	byte index;
#	byte uidIndex;					// The first index in uid->uidByte[] that is used in the current Cascade Level.
#	int8_t currentLevelKnownBits;		// The number of known UID bits in the current Cascade Level.
#	byte buffer[9];					// The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A
#	byte bufferUsed;				// The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO.
#	byte rxAlign;					// Used in BitFramingReg. Defines the bit position for the first bit received.
#	byte txLastBits;				// Used in BitFramingReg. The number of valid bits in the last transmitted byte. 
#	byte *responseBuffer;
#	byte responseLength;
#	
#	// Description of buffer structure:
#	//		Byte 0: SEL 				Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
#	//		Byte 1: NVB					Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. 
#	//		Byte 2: UID-data or CT		See explanation below. CT means Cascade Tag.
#	//		Byte 3: UID-data
#	//		Byte 4: UID-data
#	//		Byte 5: UID-data
#	//		Byte 6: BCC					Block Check Character - XOR of bytes 2-5
#	//		Byte 7: CRC_A
#	//		Byte 8: CRC_A
#	// The BCC and CRC_A are only transmitted if we know all the UID bits of the current Cascade Level.
#	//
#	// Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels)
#	//		UID size	Cascade level	Byte2	Byte3	Byte4	Byte5

lib/HiPi/Interface/MFRC522.pm  view on Meta::CPAN

            $uidIndex = 3,
            $useCascadeTag = ( $validbits && $uid->{'size'} > 7 ) ? 1 : 0;
		
        } elsif( $cascadeLevel == 3 ) {
			            
            $buffer[0] = MIFARE_SELECT_CL3;
            $uidIndex = 6,
            $useCascadeTag = 0
			
		} else {
            # should not get here
            # warn qq( cascade level $cascadeLevel);
            return ( MFRC522_STATUS_INTERNAL_ERROR );
        }
		
		# // How many UID bits are known in this Cascade Level?
		$currentLevelKnownBits = $validbits - (8 * $uidIndex);
        
		if ($currentLevelKnownBits < 0) {
			$currentLevelKnownBits = 0;
		}
                
		# // Copy the known bits from uid->uidByte[] to buffer[]
		$index = 2; #// destination index in buffer[]
		if ($useCascadeTag) {
			$buffer[$index++] = MIFARE_CASCADE;
		}
        
		my $bytesToCopy = int($currentLevelKnownBits / 8) + ($currentLevelKnownBits % 8 ? 1 : 0); # // The number of bytes needed to represent the known bits for this level.
		
        if ($bytesToCopy) {
			my $maxBytes = $useCascadeTag ? 3 : 4; #// Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag
			if ($bytesToCopy > $maxBytes) {
				$bytesToCopy = $maxBytes;
			}
			for (my $count = 0; $count < $bytesToCopy; $count++) {
				$buffer[$index++] = $uid->{'data'}->[$uidIndex + $count] || 0;
			}
		}
		# // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits
		if ($useCascadeTag) {
			$currentLevelKnownBits += 8;
		}
		
		# // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations.
		
		while (!$selectDone) {
			# // Find out how many bits and bytes to send and receive.
			if ($currentLevelKnownBits >= 32) { # // All UID bits in this Cascade Level are known. This is a SELECT.
				
				$buffer[1] = 0x70; #// NVB - Number of Valid Bits: Seven whole bytes
				#// Calculate BCC - Block Check Character
                
                for( 2,3,4,5) {
                    $buffer[$_] //= 0;
                }
                
				$buffer[6] = $buffer[2] ^ $buffer[3] ^ $buffer[4] ^ $buffer[5];
				# // Calculate CRC_A
                
                my @crcdata = @buffer[0..6];
                
                ( $crcstatus, $cbuffer1, $cbuffer2 ) = $self->pcd_calculate_crc( \@crcdata );
                
				if ($crcstatus != MFRC522_STATUS_OK) {
					return ( $crcstatus, undef, undef );
				}
                
                # set the crc result
                $buffer[7] = $cbuffer1;
                $buffer[8] = $cbuffer2;
                
				$txLastBits		= 0; #// 0 => All 8 bits are valid.
				$bufferUsed		= 9; 
				$responseLength	= 3;
                $responseIndex  = 6;
                
			} else { #// This is an ANTICOLLISION.
				
				$txLastBits		= $currentLevelKnownBits % 8;
				my $count		= int($currentLevelKnownBits / 8);	#// Number of whole bytes in the UID part.
				$index			= 2 + $count;					#// Number of whole bytes: SEL + NVB + UIDs
				$buffer[1]		= ($index << 4) + $txLastBits;	#// NVB - Number of Valid Bits
				$bufferUsed		= $index + ($txLastBits ? 1 : 0);
				$responseLength = 9 - $index;
                $responseIndex = $index;
			}
			
			#// Set bit adjustments
			$rxAlign = $txLastBits || 0;											#// Having a separate variable is overkill. But it makes the next line easier to read.
			$self->write_register(MFRC522_REG_BitFramingReg, ($rxAlign << 4) + $txLastBits);	#// RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0]
			
			#// Transmit the buffer and receive the response.
            
            my $getlen = $responseLength;
            
            my $sendlen = $bufferUsed -1;
           
            my @sendbuffer = @buffer[0..$sendlen];
                        
            ($respstatus, $respdata, $respvalidbits) = $self->pcd_transceive_data(\@sendbuffer, $txLastBits, $getlen, $rxAlign );
            
			$txLastBits = $respvalidbits;
            
            if($respdata && ref($respdata)) {
                
                for (my $i = 0; $i < @$respdata; $i ++) {
                    last if $i > $responseLength;
                    $buffer[ $i + $responseIndex ] = $respdata->[$i];
                }
            }
            
            if ($respstatus == MFRC522_STATUS_COLLISION) { #// More than one PICC in the field => collision.
				my $valueOfCollReg = $self->read_register(MFRC522_REG_CollReg); #// CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0]
				if ($valueOfCollReg & 0x20) { #// CollPosNotValid
					return ( MFRC522_STATUS_COLLISION, undef, undef ); # // Without a valid collision position we cannot continue
				}
				my $collisionPos = $valueOfCollReg & 0x1F; # // Values 0-31, 0 means bit 32.
				if ($collisionPos == 0) {
					$collisionPos = 32;
				}
				if ($collisionPos <= $currentLevelKnownBits) { #// No progress - should not happen
					return ( MFRC522_STATUS_INTERNAL_ERROR, undef, undef );
				}
				#// Choose the PICC with the bit set.
				$currentLevelKnownBits	= $collisionPos;
				my $count			= $currentLevelKnownBits % 8; #// The bit to modify
				my $checkBit		= ($currentLevelKnownBits - 1) % 8;
				$index			= 1 + int($currentLevelKnownBits / 8) + ($count ? 1 : 0); # // First byte is index 0.
				$buffer[$index]	|= (1 << $checkBit);
			} elsif ($respstatus != MFRC522_STATUS_OK) {
				return ( $respstatus, undef, undef );
			} else { # // MFRC522_STATUS_OK
                
				if ($currentLevelKnownBits >= 32) { #// This was a SELECT.
					$selectDone = 1; #// No more anticollision 
					#// We continue below outside the while.
				} else { #// This was an ANTICOLLISION.
					#/ We now have all 32 bits of the UID in this Cascade Level
					$currentLevelKnownBits = 32;
					#// Run loop again to do the SELECT.
				}
			}
		} 
		
		#// We do not check the CBB - it was constructed by us above.
		
		#// Copy the found UID bytes from buffer[] to uid->uidByte[]
		$index			= ($buffer[2] == MIFARE_CASCADE) ? 3 : 2; #// source index in buffer[]
		$bytesToCopy	= ($buffer[2] == MIFARE_CASCADE) ? 3 : 4;
		for (my $count = 0; $count < $bytesToCopy; $count++) {
            $uid->{'data'}->[$uidIndex + $count] = $buffer[$index++];
		}
		
		#// Check response SAK (Select Acknowledge)
        
        my $resplen = scalar @$respdata;
        
		if ($resplen != 3 || $respvalidbits != 0) { # // SAK must be exactly 24 bits (1 byte + CRC_A).
			return ( MFRC522_STATUS_ERROR, undef, undef );
		}
		#// Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore.
         
        my @crcdata = @$respdata;
        pop @crcdata; pop @crcdata;
        
        ( $crcstatus, $cbuffer1, $cbuffer2 ) = $self->pcd_calculate_crc( \@crcdata );
        
		if ($crcstatus != MFRC522_STATUS_OK) {
			return ( $crcstatus, undef, undef );
		}
		if (($cbuffer1 != $respdata->[-2]) || ($cbuffer2 != $respdata->[-1])) {
			return ( MFRC522_STATUS_CRC_WRONG, undef, undef );
		}
        
		if ($respdata->[0] & 0x04) { #// Cascade bit set - UID not complete yes
			$cascadeLevel++;
		} else {
			$uidComplete = 1;
			$uid->{'sak'} = $respdata->[0];
		}
	} 
	
	#// Set correct uid->size
	$uid->{'size'} = 3 * $cascadeLevel + 1;

	return ( MFRC522_STATUS_OK, $uid, $respvalidbits );
}

sub get_status_code_name {
    my($self, $code) = @_;
    $code //= 0xEE;
    if($code == MFRC522_STATUS_OK ) {
        return 'Success.';
    } elsif($code == MFRC522_STATUS_ERROR ) {
        return 'Error in communication.';
    } elsif($code == MFRC522_STATUS_COLLISION ) {
        return 'Collision detected.';
    } elsif($code == MFRC522_STATUS_TIMEOUT ) {
        return 'Timeout in communication.';
    } elsif($code == MFRC522_STATUS_NO_ROOM ) {
        return 'A buffer is not big enough.';
    } elsif($code == MFRC522_STATUS_INTERNAL_ERROR ) {
        return 'Internal error in the code. Should not happen.';
    } elsif($code == MFRC522_STATUS_INVALID ) {
        return 'Invalid argument.';
    } elsif($code == MFRC522_STATUS_CRC_WRONG ) {
        return 'The CRC_A does not match.';
    } elsif($code == MFRC522_STATUS_MIFARE_NACK ) {
        return 'A MIFARE PICC responded with NAK.';
    } elsif($code == MFRC522_STATUS_UNSUPPORTED_TYPE ) {
        return 'Unsupported command for this PICC type.';
    } elsif($code == MFRC522_STATUS_BLOCK_NOT_ALLOWED ) {
        return 'Command not allowed for this block.';
    } elsif($code == MFRC522_STATUS_BAD_PARAM ) {
        return 'Bad parameter.';
    } else {
        return 'Unknown Error.';
    }
}

sub get_default_key {
    my $self = shift;
    my @key = (0xFF) x 6;
    return \@key;
}

lib/HiPi/Interface/MFRC522.pm  view on Meta::CPAN

    }
    
    return $output . qq(\n);
}

sub picc_dump_ultralight_memory {
    my($self, $uid, $picctype, $key) = @_;
    my $piccname = $self->picc_get_type_name( $picctype );
    my $output = qq(Dumping memory contents not implemented for $piccname\n);
    return $output;
}

sub picc_dump_classic_memory {
    my($self, $uid, $picctype, $key) = @_;
    
    my $output = '';
    my $no_of_sectors = 0;
    if( $picctype == MFRC522_PICC_TYPE_MIFARE_MINI ) {
        $no_of_sectors = 5;
    } elsif( $picctype == MFRC522_PICC_TYPE_MIFARE_1K ) {
        $no_of_sectors = 16;
    } elsif( $picctype == MFRC522_PICC_TYPE_MIFARE_4K ) {
        $no_of_sectors = 40;
    }
    
	#// Dump sectors, highest address first.
	if ($no_of_sectors) {
        $output .= qq(Sector Block   0  1  2  3   4  5  6  7   8  9 10 11  12 13 14 15  AccessBits\n);
        for (my $i = $no_of_sectors -1; $i >= 0; $i-- ) {
            $output .= $self->picc_dump_classic_sector( $uid, $key, $i );
		}
	}
    
    my $haltstatus = $self->picc_halt_active;
    unless($haltstatus == MFRC522_STATUS_OK) {
        $output .= $self->get_status_code_name( $haltstatus ) . qq(\n);
    }
    $self->pcd_stop_crypto1;
    return $output;
}

sub pcd_authenticate {
    my($self, $command, $blockAddr, $key, $uid ) = @_;
    
    my $waitIRq = 0x10;
	    
    my @sendData = ( $command, $blockAddr );
    
	for (my $i = 0; $i < 6; $i++) {	# // 6 key bytes
		$sendData[2 + $i] = $key->[$i];
	}
	#// Use the last uid bytes as specified in http://cache.nxp.com/documents/application_note/AN10927.pdf
	#// section 3.2.5 "MIFARE Classic Authentication".
	#// The only missed case is the MF1Sxxxx shortcut activation,
	#// but it requires cascade tag (CT) byte, that is not part of uid.
    
	for (my $i = 0; $i < 4; $i++) {				#// The last 4 bytes of the UID
		$sendData[8 + $i] = $uid->{'data'}->[$i + $uid->{'size'} -4];
	}
	
    my ($piccstatus, $piccdata, $piccvalidbits) = $self->pcd_communicate_with_picc( MFRC522_AUTHENT, $waitIRq, \@sendData  );
	return $piccstatus;
}

sub picc_dump_classic_sector {
    my($self, $uid, $key, $sector) = @_;
    
    my $output = '';
    
    my ( $status, $firstBlock, $no_of_blocks, $isSectorTrailer );
	#byte firstBlock;		// Address of lowest address to dump actually last block dumped)
	#byte no_of_blocks;		// Number of blocks in sector
	#bool isSectorTrailer;	// Set to true while handling the "last" (ie highest address) in the sector.
	
	#// The access bits are stored in a peculiar fashion.
	#// There are four groups:
	#//		g[3]	Access bits for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39)
	#//		g[2]	Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39)
	#//		g[1]	Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39)
	#//		g[0]	Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39)
	#// Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is LSB.
	#// The four CX bits are stored together in a nible cx and an inverted nible cx_.
	#byte c1, c2, c3;		// Nibbles
	#byte c1_, c2_, c3_;		// Inverted nibbles
    
    my ( $c1, $c2, $c3, $c1x, $c2x, $c3x );
    
	#bool invertedError;		// True if one of the inverted nibbles did not match
	#byte g[4];				// Access bits for each of the four groups.
	#byte group;				// 0-3 - active group for access bits
	#bool firstInGroup;		// True for the first block dumped in the group
    
    my( $invertedError, $group, $firstInGroup );
    my @g = (0,0,0,0);
	
	#// Determine position and size of sector.
	if ($sector < 32) { #// Sectors 0..31 has 4 blocks each
		$no_of_blocks = 4;
		$firstBlock = $sector * $no_of_blocks;
	} elsif ($sector < 40) { #// Sectors 32-39 has 16 blocks each
		$no_of_blocks = 16;
		$firstBlock = 128 + ($sector - 32) * $no_of_blocks;
	}  else { #// Illegal input, no MIFARE Classic PICC has more than 40 sectors.
		return qq(Illegal input, no MIFARE Classic PICC has more than 40 sectors\n);
	}
		
	#// Dump blocks, highest address first.
	my $byteCount;
	my @buffer = ( 0 ) x 18;
	my $blockAddr;
	$isSectorTrailer = 1;
	$invertedError = 0;	# // Avoid "unused variable" warning.
	for (my $blockOffset = $no_of_blocks - 1; $blockOffset >= 0; $blockOffset-- ) {
		$blockAddr = $firstBlock + $blockOffset;
		#// Sector number - only on first line
		if ($isSectorTrailer) {
			if($sector < 10) {
				$output .= '   '; #// Pad with spaces
            } else {
				$output .= '  '; #// Pad with spaces
            }



( run in 0.572 second using v1.01-cache-2.11-cpan-97f6503c9c8 )