#############################################################
# This file was automatically generated on 2019-11-25.      #
#                                                           #
# Perl Bindings Version 2.1.25                              #
#                                                           #
# If you have a bugfix for this file and want to commit it, #
# please fix the bug in the generator. You can find a link  #
# to the generators git repository on tinkerforge.com       #
#############################################################

=pod

=encoding utf8

=head1 NAME

Tinkerforge::BrickletNFC - NFC tag read/write, NFC P2P and Card Emulation

=cut

package Tinkerforge::BrickletNFC;

use strict;
use warnings;
use Carp;
use threads;
use threads::shared;
use parent 'Tinkerforge::Device';
use Tinkerforge::IPConnection;
use Tinkerforge::Error;

=head1 CONSTANTS

=over

=item DEVICE_IDENTIFIER

This constant is used to identify a NFC Bricklet.

The get_identity() subroutine and the CALLBACK_ENUMERATE callback of the
IP Connection have a device_identifier parameter to specify the Brick's or
Bricklet's type.

=cut

use constant DEVICE_IDENTIFIER => 286;

=item DEVICE_DISPLAY_NAME

This constant represents the display name of a NFC Bricklet.

=cut

use constant DEVICE_DISPLAY_NAME => 'NFC Bricklet';

=item CALLBACK_READER_STATE_CHANGED

This constant is used with the register_callback() subroutine to specify
the CALLBACK_READER_STATE_CHANGED callback.

=cut

use constant CALLBACK_READER_STATE_CHANGED => 13;

=item CALLBACK_CARDEMU_STATE_CHANGED

This constant is used with the register_callback() subroutine to specify
the CALLBACK_CARDEMU_STATE_CHANGED callback.

=cut

use constant CALLBACK_CARDEMU_STATE_CHANGED => 18;

=item CALLBACK_P2P_STATE_CHANGED

This constant is used with the register_callback() subroutine to specify
the CALLBACK_P2P_STATE_CHANGED callback.

=cut

use constant CALLBACK_P2P_STATE_CHANGED => 24;

=item FUNCTION_SET_MODE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_SET_MODE => 1;

=item FUNCTION_GET_MODE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_GET_MODE => 2;

=item FUNCTION_READER_REQUEST_TAG_ID

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READER_REQUEST_TAG_ID => 3;

=item FUNCTION_READER_GET_TAG_ID_LOW_LEVEL

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READER_GET_TAG_ID_LOW_LEVEL => 4;

=item FUNCTION_READER_GET_STATE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READER_GET_STATE => 5;

=item FUNCTION_READER_WRITE_NDEF_LOW_LEVEL

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READER_WRITE_NDEF_LOW_LEVEL => 6;

=item FUNCTION_READER_REQUEST_NDEF

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READER_REQUEST_NDEF => 7;

=item FUNCTION_READER_READ_NDEF_LOW_LEVEL

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READER_READ_NDEF_LOW_LEVEL => 8;

=item FUNCTION_READER_AUTHENTICATE_MIFARE_CLASSIC_PAGE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READER_AUTHENTICATE_MIFARE_CLASSIC_PAGE => 9;

=item FUNCTION_READER_WRITE_PAGE_LOW_LEVEL

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READER_WRITE_PAGE_LOW_LEVEL => 10;

=item FUNCTION_READER_REQUEST_PAGE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READER_REQUEST_PAGE => 11;

=item FUNCTION_READER_READ_PAGE_LOW_LEVEL

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READER_READ_PAGE_LOW_LEVEL => 12;

=item FUNCTION_CARDEMU_GET_STATE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_CARDEMU_GET_STATE => 14;

=item FUNCTION_CARDEMU_START_DISCOVERY

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_CARDEMU_START_DISCOVERY => 15;

=item FUNCTION_CARDEMU_WRITE_NDEF_LOW_LEVEL

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_CARDEMU_WRITE_NDEF_LOW_LEVEL => 16;

=item FUNCTION_CARDEMU_START_TRANSFER

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_CARDEMU_START_TRANSFER => 17;

=item FUNCTION_P2P_GET_STATE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_P2P_GET_STATE => 19;

=item FUNCTION_P2P_START_DISCOVERY

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_P2P_START_DISCOVERY => 20;

=item FUNCTION_P2P_WRITE_NDEF_LOW_LEVEL

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_P2P_WRITE_NDEF_LOW_LEVEL => 21;

=item FUNCTION_P2P_START_TRANSFER

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_P2P_START_TRANSFER => 22;

=item FUNCTION_P2P_READ_NDEF_LOW_LEVEL

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_P2P_READ_NDEF_LOW_LEVEL => 23;

=item FUNCTION_SET_DETECTION_LED_CONFIG

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_SET_DETECTION_LED_CONFIG => 25;

=item FUNCTION_GET_DETECTION_LED_CONFIG

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_GET_DETECTION_LED_CONFIG => 26;

=item FUNCTION_SET_MAXIMUM_TIMEOUT

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_SET_MAXIMUM_TIMEOUT => 27;

=item FUNCTION_GET_MAXIMUM_TIMEOUT

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_GET_MAXIMUM_TIMEOUT => 28;

=item FUNCTION_GET_SPITFP_ERROR_COUNT

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_GET_SPITFP_ERROR_COUNT => 234;

=item FUNCTION_SET_BOOTLOADER_MODE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_SET_BOOTLOADER_MODE => 235;

=item FUNCTION_GET_BOOTLOADER_MODE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_GET_BOOTLOADER_MODE => 236;

=item FUNCTION_SET_WRITE_FIRMWARE_POINTER

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_SET_WRITE_FIRMWARE_POINTER => 237;

=item FUNCTION_WRITE_FIRMWARE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_WRITE_FIRMWARE => 238;

=item FUNCTION_SET_STATUS_LED_CONFIG

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_SET_STATUS_LED_CONFIG => 239;

=item FUNCTION_GET_STATUS_LED_CONFIG

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_GET_STATUS_LED_CONFIG => 240;

=item FUNCTION_GET_CHIP_TEMPERATURE

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_GET_CHIP_TEMPERATURE => 242;

=item FUNCTION_RESET

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_RESET => 243;

=item FUNCTION_WRITE_UID

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_WRITE_UID => 248;

=item FUNCTION_READ_UID

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_READ_UID => 249;

=item FUNCTION_GET_IDENTITY

This constant is used with the get_response_expected(), set_response_expected()
and set_response_expected_all() subroutines.

=cut

use constant FUNCTION_GET_IDENTITY => 255;
use constant MODE_OFF => 0;
use constant MODE_CARDEMU => 1;
use constant MODE_P2P => 2;
use constant MODE_READER => 3;
use constant TAG_TYPE_MIFARE_CLASSIC => 0;
use constant TAG_TYPE_TYPE1 => 1;
use constant TAG_TYPE_TYPE2 => 2;
use constant TAG_TYPE_TYPE3 => 3;
use constant TAG_TYPE_TYPE4 => 4;
use constant READER_STATE_INITIALIZATION => 0;
use constant READER_STATE_IDLE => 128;
use constant READER_STATE_ERROR => 192;
use constant READER_STATE_REQUEST_TAG_ID => 2;
use constant READER_STATE_REQUEST_TAG_ID_READY => 130;
use constant READER_STATE_REQUEST_TAG_ID_ERROR => 194;
use constant READER_STATE_AUTHENTICATE_MIFARE_CLASSIC_PAGE => 3;
use constant READER_STATE_AUTHENTICATE_MIFARE_CLASSIC_PAGE_READY => 131;
use constant READER_STATE_AUTHENTICATE_MIFARE_CLASSIC_PAGE_ERROR => 195;
use constant READER_STATE_WRITE_PAGE => 4;
use constant READER_STATE_WRITE_PAGE_READY => 132;
use constant READER_STATE_WRITE_PAGE_ERROR => 196;
use constant READER_STATE_REQUEST_PAGE => 5;
use constant READER_STATE_REQUEST_PAGE_READY => 133;
use constant READER_STATE_REQUEST_PAGE_ERROR => 197;
use constant READER_STATE_WRITE_NDEF => 6;
use constant READER_STATE_WRITE_NDEF_READY => 134;
use constant READER_STATE_WRITE_NDEF_ERROR => 198;
use constant READER_STATE_REQUEST_NDEF => 7;
use constant READER_STATE_REQUEST_NDEF_READY => 135;
use constant READER_STATE_REQUEST_NDEF_ERROR => 199;
use constant KEY_A => 0;
use constant KEY_B => 1;
use constant READER_WRITE_TYPE4_CAPABILITY_CONTAINER => 3;
use constant READER_WRITE_TYPE4_NDEF => 4;
use constant READER_REQUEST_TYPE4_CAPABILITY_CONTAINER => 3;
use constant READER_REQUEST_TYPE4_NDEF => 4;
use constant CARDEMU_STATE_INITIALIZATION => 0;
use constant CARDEMU_STATE_IDLE => 128;
use constant CARDEMU_STATE_ERROR => 192;
use constant CARDEMU_STATE_DISCOVER => 2;
use constant CARDEMU_STATE_DISCOVER_READY => 130;
use constant CARDEMU_STATE_DISCOVER_ERROR => 194;
use constant CARDEMU_STATE_TRANSFER_NDEF => 3;
use constant CARDEMU_STATE_TRANSFER_NDEF_READY => 131;
use constant CARDEMU_STATE_TRANSFER_NDEF_ERROR => 195;
use constant CARDEMU_TRANSFER_ABORT => 0;
use constant CARDEMU_TRANSFER_WRITE => 1;
use constant P2P_STATE_INITIALIZATION => 0;
use constant P2P_STATE_IDLE => 128;
use constant P2P_STATE_ERROR => 192;
use constant P2P_STATE_DISCOVER => 2;
use constant P2P_STATE_DISCOVER_READY => 130;
use constant P2P_STATE_DISCOVER_ERROR => 194;
use constant P2P_STATE_TRANSFER_NDEF => 3;
use constant P2P_STATE_TRANSFER_NDEF_READY => 131;
use constant P2P_STATE_TRANSFER_NDEF_ERROR => 195;
use constant P2P_TRANSFER_ABORT => 0;
use constant P2P_TRANSFER_WRITE => 1;
use constant P2P_TRANSFER_READ => 2;
use constant DETECTION_LED_CONFIG_OFF => 0;
use constant DETECTION_LED_CONFIG_ON => 1;
use constant DETECTION_LED_CONFIG_SHOW_HEARTBEAT => 2;
use constant DETECTION_LED_CONFIG_SHOW_DETECTION => 3;
use constant BOOTLOADER_MODE_BOOTLOADER => 0;
use constant BOOTLOADER_MODE_FIRMWARE => 1;
use constant BOOTLOADER_MODE_BOOTLOADER_WAIT_FOR_REBOOT => 2;
use constant BOOTLOADER_MODE_FIRMWARE_WAIT_FOR_REBOOT => 3;
use constant BOOTLOADER_MODE_FIRMWARE_WAIT_FOR_ERASE_AND_REBOOT => 4;
use constant BOOTLOADER_STATUS_OK => 0;
use constant BOOTLOADER_STATUS_INVALID_MODE => 1;
use constant BOOTLOADER_STATUS_NO_CHANGE => 2;
use constant BOOTLOADER_STATUS_ENTRY_FUNCTION_NOT_PRESENT => 3;
use constant BOOTLOADER_STATUS_DEVICE_IDENTIFIER_INCORRECT => 4;
use constant BOOTLOADER_STATUS_CRC_MISMATCH => 5;
use constant STATUS_LED_CONFIG_OFF => 0;
use constant STATUS_LED_CONFIG_ON => 1;
use constant STATUS_LED_CONFIG_SHOW_HEARTBEAT => 2;
use constant STATUS_LED_CONFIG_SHOW_STATUS => 3;


=back

=head1 FUNCTIONS

=over

=item new()

Creates an object with the unique device ID *uid* and adds it to
the IP Connection *ipcon*.

=cut

sub new
{
	my ($class, $uid, $ipcon) = @_;

	my $self = Tinkerforge::Device->_new($uid, $ipcon, [2, 0, 1]);

	$self->{response_expected}->{&FUNCTION_SET_MODE} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_GET_MODE} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_READER_REQUEST_TAG_ID} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_READER_GET_TAG_ID_LOW_LEVEL} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_READER_GET_STATE} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_READER_WRITE_NDEF_LOW_LEVEL} = Tinkerforge::Device->_RESPONSE_EXPECTED_TRUE;
	$self->{response_expected}->{&FUNCTION_READER_REQUEST_NDEF} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_READER_READ_NDEF_LOW_LEVEL} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_READER_AUTHENTICATE_MIFARE_CLASSIC_PAGE} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_READER_WRITE_PAGE_LOW_LEVEL} = Tinkerforge::Device->_RESPONSE_EXPECTED_TRUE;
	$self->{response_expected}->{&FUNCTION_READER_REQUEST_PAGE} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_READER_READ_PAGE_LOW_LEVEL} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_CARDEMU_GET_STATE} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_CARDEMU_START_DISCOVERY} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_CARDEMU_WRITE_NDEF_LOW_LEVEL} = Tinkerforge::Device->_RESPONSE_EXPECTED_TRUE;
	$self->{response_expected}->{&FUNCTION_CARDEMU_START_TRANSFER} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_P2P_GET_STATE} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_P2P_START_DISCOVERY} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_P2P_WRITE_NDEF_LOW_LEVEL} = Tinkerforge::Device->_RESPONSE_EXPECTED_TRUE;
	$self->{response_expected}->{&FUNCTION_P2P_START_TRANSFER} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_P2P_READ_NDEF_LOW_LEVEL} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_SET_DETECTION_LED_CONFIG} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_GET_DETECTION_LED_CONFIG} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_SET_MAXIMUM_TIMEOUT} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_GET_MAXIMUM_TIMEOUT} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_GET_SPITFP_ERROR_COUNT} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_SET_BOOTLOADER_MODE} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_GET_BOOTLOADER_MODE} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_SET_WRITE_FIRMWARE_POINTER} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_WRITE_FIRMWARE} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_SET_STATUS_LED_CONFIG} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_GET_STATUS_LED_CONFIG} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_GET_CHIP_TEMPERATURE} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_RESET} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_WRITE_UID} = Tinkerforge::Device->_RESPONSE_EXPECTED_FALSE;
	$self->{response_expected}->{&FUNCTION_READ_UID} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;
	$self->{response_expected}->{&FUNCTION_GET_IDENTITY} = Tinkerforge::Device->_RESPONSE_EXPECTED_ALWAYS_TRUE;

	$self->{callback_formats}->{&CALLBACK_READER_STATE_CHANGED} = 'C ?';
	$self->{callback_formats}->{&CALLBACK_CARDEMU_STATE_CHANGED} = 'C ?';
	$self->{callback_formats}->{&CALLBACK_P2P_STATE_CHANGED} = 'C ?';



	bless($self, $class);

	return $self;
}


=item set_mode()

Sets the mode. The NFC Bricklet supports four modes:

* Off
* Card Emulation (Cardemu): Emulates a tag for other readers
* Peer to Peer (P2P): Exchange data with other readers
* Reader: Reads and writes tags

If you change a mode, the Bricklet will reconfigure the hardware for this mode.
Therefore, you can only use functions corresponding to the current mode. For
example, in Reader mode you can only use Reader functions.

=cut

sub set_mode
{
	my ($self, $mode) = @_;

	$self->_send_request(&FUNCTION_SET_MODE, [$mode], 'C', '');
}

=item get_mode()

Returns the mode as set by :func:`Set Mode`.

=cut

sub get_mode
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_GET_MODE, [], '', 'C');
}

=item reader_request_tag_id()

To read or write a tag that is in proximity of the NFC Bricklet you
first have to call this function with the expected tag type as parameter.
It is no problem if you don't know the tag type. You can cycle through
the available tag types until the tag answers the request.

Currently the following tag types are supported:

* Mifare Classic
* NFC Forum Type 1
* NFC Forum Type 2
* NFC Forum Type 3
* NFC Forum Type 4

After you call :func:`Reader Request Tag ID` the NFC Bricklet will try to read
the tag ID from the tag. After this process is done the state will change.
You can either register the :cb:`Reader State Changed` callback or you can poll
:func:`Reader Get State` to find out about the state change.

If the state changes to *ReaderRequestTagIDError* it means that either there was
no tag present or that the tag has an incompatible type. If the state
changes to *ReaderRequestTagIDReady* it means that a compatible tag was found
and that the tag ID has been saved. You can now read out the tag ID by
calling :func:`Reader Get Tag ID`.

If two tags are in the proximity of the NFC Bricklet, this
function will cycle through the tags. To select a specific tag you have
to call :func:`Reader Request Tag ID` until the correct tag ID is found.

In case of any *ReaderError* state the selection is lost and you have to
start again by calling :func:`Reader Request Tag ID`.

=cut

sub reader_request_tag_id
{
	my ($self) = @_;

	$self->_send_request(&FUNCTION_READER_REQUEST_TAG_ID, [], '', '');
}

=item reader_get_tag_id_low_level()

Returns the tag type and the tag ID. This function can only be called if the
NFC Bricklet is currently in one of the *ReaderReady* states. The returned tag ID
is the tag ID that was saved through the last call of :func:`Reader Request Tag ID`.

To get the tag ID of a tag the approach is as follows:

1. Call :func:`Reader Request Tag ID`
2. Wait for state to change to *ReaderRequestTagIDReady* (see :func:`Reader Get State` or
   :cb:`Reader State Changed` callback)
3. Call :func:`Reader Get Tag ID`

=cut

sub reader_get_tag_id_low_level
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_READER_GET_TAG_ID_LOW_LEVEL, [], '', 'C C C32');
}

=item reader_get_state()

Returns the current reader state of the NFC Bricklet.

On startup the Bricklet will be in the *ReaderInitialization* state. The
initialization will only take about 20ms. After that it changes to *ReaderIdle*.

The Bricklet is also reinitialized if the mode is changed, see :func:`Set Mode`.

The functions of this Bricklet can be called in the *ReaderIdle* state and all of
the *ReaderReady* and *ReaderError* states.

Example: If you call :func:`Reader Request Page`, the state will change to
*ReaderRequestPage* until the reading of the page is finished. Then it will change
to either *ReaderRequestPageReady* if it worked or to *ReaderRequestPageError* if it
didn't. If the request worked you can get the page by calling :func:`Reader Read Page`.

The same approach is used analogously for the other API functions.

=cut

sub reader_get_state
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_READER_GET_STATE, [], '', 'C ?');
}

=item reader_write_ndef_low_level()

Writes NDEF formated data.

This function currently supports NFC Forum Type 2 and 4.

The general approach for writing a NDEF message is as follows:

1. Call :func:`Reader Request Tag ID`
2. Wait for state to change to *ReaderRequestTagIDReady* (see
   :func:`Reader Get State` or :cb:`Reader State Changed` callback)
3. If looking for a specific tag then call :func:`Reader Get Tag ID` and check
   if the expected tag was found, if it was not found got back to step 1
4. Call :func:`Reader Write NDEF` with the NDEF message that you want to write
5. Wait for state to change to *ReaderWriteNDEFReady* (see :func:`Reader Get State`
   or :cb:`Reader State Changed` callback)

=cut

sub reader_write_ndef_low_level
{
	my ($self, $ndef_length, $ndef_chunk_offset, $ndef_chunk_data) = @_;

	$self->_send_request(&FUNCTION_READER_WRITE_NDEF_LOW_LEVEL, [$ndef_length, $ndef_chunk_offset, $ndef_chunk_data], 'S S C60', '');
}

=item reader_request_ndef()

Reads NDEF formated data from a tag.

This function currently supports NFC Forum Type 1, 2, 3 and 4.

The general approach for reading a NDEF message is as follows:

1. Call :func:`Reader Request Tag ID`
2. Wait for state to change to *RequestTagIDReady* (see :func:`Reader Get State`
   or :cb:`Reader State Changed` callback)
3. If looking for a specific tag then call :func:`Reader Get Tag ID` and check if the
   expected tag was found, if it was not found got back to step 1
4. Call :func:`Reader Request NDEF`
5. Wait for state to change to *ReaderRequestNDEFReady* (see :func:`Reader Get State`
   or :cb:`Reader State Changed` callback)
6. Call :func:`Reader Read NDEF` to retrieve the NDEF message from the buffer

=cut

sub reader_request_ndef
{
	my ($self) = @_;

	$self->_send_request(&FUNCTION_READER_REQUEST_NDEF, [], '', '');
}

=item reader_read_ndef_low_level()

Returns the NDEF data from an internal buffer. To fill the buffer
with a NDEF message you have to call :func:`Reader Request NDEF` beforehand.

=cut

sub reader_read_ndef_low_level
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_READER_READ_NDEF_LOW_LEVEL, [], '', 'S S C60');
}

=item reader_authenticate_mifare_classic_page()

Mifare Classic tags use authentication. If you want to read from or write to
a Mifare Classic page you have to authenticate it beforehand.
Each page can be authenticated with two keys: A (``key_number`` = 0) and B
(``key_number`` = 1). A new Mifare Classic
tag that has not yet been written to can be accessed with key A
and the default key ``[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]``.

The approach to read or write a Mifare Classic page is as follows:

1. Call :func:`Reader Request Tag ID`
2. Wait for state to change to *ReaderRequestTagIDReady* (see :func:`Reader Get State`
   or :cb:`Reader State Changed` callback)
3. If looking for a specific tag then call :func:`Reader Get Tag ID` and check if the
   expected tag was found, if it was not found got back to step 1
4. Call :func:`Reader Authenticate Mifare Classic Page` with page and key for the page
5. Wait for state to change to *ReaderAuthenticatingMifareClassicPageReady* (see
   :func:`Reader Get State` or :cb:`Reader State Changed` callback)
6. Call :func:`Reader Request Page` or :func:`Reader Write Page` to read/write page

The authentication will always work for one whole sector (4 pages).

=cut

sub reader_authenticate_mifare_classic_page
{
	my ($self, $page, $key_number, $key) = @_;

	$self->_send_request(&FUNCTION_READER_AUTHENTICATE_MIFARE_CLASSIC_PAGE, [$page, $key_number, $key], 'S C C6', '');
}

=item reader_write_page_low_level()

Writes a maximum of 8192 bytes starting from the given page. How many pages are written
depends on the tag type. The page sizes are as follows:

* Mifare Classic page size: 16 byte
* NFC Forum Type 1 page size: 8 byte
* NFC Forum Type 2 page size: 4 byte
* NFC Forum Type 3 page size: 16 byte
* NFC Forum Type 4: No pages, page = file selection (CC or NDEF, see below)

The general approach for writing to a tag is as follows:

1. Call :func:`Reader Request Tag ID`
2. Wait for state to change to *ReaderRequestTagIDReady* (see :func:`Reader Get State` or
   :cb:`Reader State Changed` callback)
3. If looking for a specific tag then call :func:`Reader Get Tag ID` and check if the
   expected tag was found, if it was not found got back to step 1
4. Call :func:`Reader Write Page` with page number and data
5. Wait for state to change to *ReaderWritePageReady* (see :func:`Reader Get State` or
   :cb:`Reader State Changed` callback)

If you use a Mifare Classic tag you have to authenticate a page before you
can write to it. See :func:`Reader Authenticate Mifare Classic Page`.

NFC Forum Type 4 tags are not organized into pages but different files. We currently
support two files: Capability Container file (CC) and NDEF file.

Choose CC by setting page to 3 or NDEF by setting page to 4.

=cut

sub reader_write_page_low_level
{
	my ($self, $page, $data_length, $data_chunk_offset, $data_chunk_data) = @_;

	$self->_send_request(&FUNCTION_READER_WRITE_PAGE_LOW_LEVEL, [$page, $data_length, $data_chunk_offset, $data_chunk_data], 'S S S C58', '');
}

=item reader_request_page()

Reads a maximum of 8192 bytes starting from the given page and stores them into a buffer.
The buffer can then be read out with :func:`Reader Read Page`.
How many pages are read depends on the tag type. The page sizes are
as follows:

* Mifare Classic page size: 16 byte
* NFC Forum Type 1 page size: 8 byte
* NFC Forum Type 2 page size: 4 byte
* NFC Forum Type 3 page size: 16 byte
* NFC Forum Type 4: No pages, page = file selection (CC or NDEF, see below)

The general approach for reading a tag is as follows:

1. Call :func:`Reader Request Tag ID`
2. Wait for state to change to *RequestTagIDReady* (see :func:`Reader Get State`
   or :cb:`Reader State Changed` callback)
3. If looking for a specific tag then call :func:`Reader Get Tag ID` and check if the
   expected tag was found, if it was not found got back to step 1
4. Call :func:`Reader Request Page` with page number
5. Wait for state to change to *ReaderRequestPageReady* (see :func:`Reader Get State`
   or :cb:`Reader State Changed` callback)
6. Call :func:`Reader Read Page` to retrieve the page from the buffer

If you use a Mifare Classic tag you have to authenticate a page before you
can read it. See :func:`Reader Authenticate Mifare Classic Page`.

NFC Forum Type 4 tags are not organized into pages but different files. We currently
support two files: Capability Container file (CC) and NDEF file.

Choose CC by setting page to 3 or NDEF by setting page to 4.

=cut

sub reader_request_page
{
	my ($self, $page, $length) = @_;

	$self->_send_request(&FUNCTION_READER_REQUEST_PAGE, [$page, $length], 'S S', '');
}

=item reader_read_page_low_level()

Returns the page data from an internal buffer. To fill the buffer
with specific pages you have to call :func:`Reader Request Page` beforehand.

=cut

sub reader_read_page_low_level
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_READER_READ_PAGE_LOW_LEVEL, [], '', 'S S C60');
}

=item cardemu_get_state()

Returns the current cardemu state of the NFC Bricklet.

On startup the Bricklet will be in the *CardemuInitialization* state. The
initialization will only take about 20ms. After that it changes to *CardemuIdle*.

The Bricklet is also reinitialized if the mode is changed, see :func:`Set Mode`.

The functions of this Bricklet can be called in the *CardemuIdle* state and all of
the *CardemuReady* and *CardemuError* states.

Example: If you call :func:`Cardemu Start Discovery`, the state will change to
*CardemuDiscover* until the discovery is finished. Then it will change
to either *CardemuDiscoverReady* if it worked or to *CardemuDiscoverError* if it
didn't.

The same approach is used analogously for the other API functions.

=cut

sub cardemu_get_state
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_CARDEMU_GET_STATE, [], '', 'C ?');
}

=item cardemu_start_discovery()

Starts the discovery process. If you call this function while a NFC
reader device is near to the NFC Bricklet the state will change from
*CardemuDiscovery* to *CardemuDiscoveryReady*.

If no NFC reader device can be found or if there is an error during
discovery the cardemu state will change to *CardemuDiscoveryError*. In this case you
have to restart the discovery process.

If the cardemu state changes to *CardemuDiscoveryReady* you can start the NDEF message
transfer with :func:`Cardemu Write NDEF` and :func:`Cardemu Start Transfer`.

=cut

sub cardemu_start_discovery
{
	my ($self) = @_;

	$self->_send_request(&FUNCTION_CARDEMU_START_DISCOVERY, [], '', '');
}

=item cardemu_write_ndef_low_level()

Writes the NDEF messages that is to be transferred to the NFC peer.

The maximum supported NDEF message size in Cardemu mode is 255 byte.

You can call this function at any time in Cardemu mode. The internal buffer
will not be overwritten until you call this function again or change the
mode.

=cut

sub cardemu_write_ndef_low_level
{
	my ($self, $ndef_length, $ndef_chunk_offset, $ndef_chunk_data) = @_;

	$self->_send_request(&FUNCTION_CARDEMU_WRITE_NDEF_LOW_LEVEL, [$ndef_length, $ndef_chunk_offset, $ndef_chunk_data], 'S S C60', '');
}

=item cardemu_start_transfer()

You can start the transfer of a NDEF message if the cardemu state is *CardemuDiscoveryReady*.

Before you call this function to start a write transfer, the NDEF message that
is to be transferred has to be written via :func:`Cardemu Write NDEF` first.

After you call this function the state will change to *CardemuTransferNDEF*. It will
change to *CardemuTransferNDEFReady* if the transfer was successful or
*CardemuTransferNDEFError* if it wasn't.

=cut

sub cardemu_start_transfer
{
	my ($self, $transfer) = @_;

	$self->_send_request(&FUNCTION_CARDEMU_START_TRANSFER, [$transfer], 'C', '');
}

=item p2p_get_state()

Returns the current P2P state of the NFC Bricklet.

On startup the Bricklet will be in the *P2PInitialization* state. The
initialization will only take about 20ms. After that it changes to *P2PIdle*.

The Bricklet is also reinitialized if the mode is changed, see :func:`Set Mode`.

The functions of this Bricklet can be called in the *P2PIdle* state and all of
the *P2PReady* and *P2PError* states.

Example: If you call :func:`P2P Start Discovery`, the state will change to
*P2PDiscover* until the discovery is finished. Then it will change
to either P2PDiscoverReady* if it worked or to *P2PDiscoverError* if it
didn't.

The same approach is used analogously for the other API functions.

=cut

sub p2p_get_state
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_P2P_GET_STATE, [], '', 'C ?');
}

=item p2p_start_discovery()

Starts the discovery process. If you call this function while another NFC
P2P enabled device is near to the NFC Bricklet the state will change from
*P2PDiscovery* to *P2PDiscoveryReady*.

If no NFC P2P enabled device can be found or if there is an error during
discovery the P2P state will change to *P2PDiscoveryError*. In this case you
have to restart the discovery process.

If the P2P state changes to *P2PDiscoveryReady* you can start the NDEF message
transfer with :func:`P2P Start Transfer`.

=cut

sub p2p_start_discovery
{
	my ($self) = @_;

	$self->_send_request(&FUNCTION_P2P_START_DISCOVERY, [], '', '');
}

=item p2p_write_ndef_low_level()

Writes the NDEF messages that is to be transferred to the NFC peer.

The maximum supported NDEF message size for P2P transfer is 255 byte.

You can call this function at any time in P2P mode. The internal buffer
will not be overwritten until you call this function again, change the
mode or use P2P to read an NDEF messages.

=cut

sub p2p_write_ndef_low_level
{
	my ($self, $ndef_length, $ndef_chunk_offset, $ndef_chunk_data) = @_;

	$self->_send_request(&FUNCTION_P2P_WRITE_NDEF_LOW_LEVEL, [$ndef_length, $ndef_chunk_offset, $ndef_chunk_data], 'S S C60', '');
}

=item p2p_start_transfer()

You can start the transfer of a NDEF message if the P2P state is *P2PDiscoveryReady*.

Before you call this function to start a write transfer, the NDEF message that
is to be transferred has to be written via :func:`P2P Write NDEF` first.

After you call this function the P2P state will change to *P2PTransferNDEF*. It will
change to *P2PTransferNDEFReady* if the transfer was successfull or
*P2PTransferNDEFError* if it wasn't.

If you started a write transfer you are now done. If you started a read transfer
you can now use :func:`P2P Read NDEF` to read the NDEF message that was written
by the NFC peer.

=cut

sub p2p_start_transfer
{
	my ($self, $transfer) = @_;

	$self->_send_request(&FUNCTION_P2P_START_TRANSFER, [$transfer], 'C', '');
}

=item p2p_read_ndef_low_level()

Returns the NDEF message that was written by a NFC peer in NFC P2P mode.

The NDEF message is ready if you called :func:`P2P Start Transfer` with a
read transfer and the P2P state changed to *P2PTransferNDEFReady*.

=cut

sub p2p_read_ndef_low_level
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_P2P_READ_NDEF_LOW_LEVEL, [], '', 'S S C60');
}

=item set_detection_led_config()

Sets the detection LED configuration. By default the LED shows
if a card/reader is detected.

You can also turn the LED permanently on/off or show a heartbeat.

If the Bricklet is in bootloader mode, the LED is off.

=cut

sub set_detection_led_config
{
	my ($self, $config) = @_;

	$self->_send_request(&FUNCTION_SET_DETECTION_LED_CONFIG, [$config], 'C', '');
}

=item get_detection_led_config()

Returns the configuration as set by :func:`Set Detection LED Config`

=cut

sub get_detection_led_config
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_GET_DETECTION_LED_CONFIG, [], '', 'C');
}

=item set_maximum_timeout()

Sets the maximum timeout.

This is a global maximum used for all internal state timeouts. The timeouts depend heavily
on the used tags etc. For example: If you use a Type 2 tag and you want to detect if
it is present, you have to use :func:`Reader Request Tag ID` and wait for the state
to change to either the error state or the ready state.

With the default configuration this takes 2-3 seconds. By setting the maximum timeout to
100ms you can reduce this time to ~150-200ms. For Type 2 this would also still work
with a 20ms timeout (a Type 2 tag answers usually within 10ms). A type 4 tag can take
up to 500ms in our tests.

If you need a fast response time to discover if a tag is present or not you can find
a good timeout value by trial and error for your specific tag.

By default we use a very conservative timeout, to be sure that any tag can always
answer in time.

.. versionadded:: 2.0.1$nbsp;(Plugin)

=cut

sub set_maximum_timeout
{
	my ($self, $timeout) = @_;

	$self->_send_request(&FUNCTION_SET_MAXIMUM_TIMEOUT, [$timeout], 'S', '');
}

=item get_maximum_timeout()

Returns the timeout as set by :func:`Set Maximum Timeout`

.. versionadded:: 2.0.1$nbsp;(Plugin)

=cut

sub get_maximum_timeout
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_GET_MAXIMUM_TIMEOUT, [], '', 'S');
}

=item get_spitfp_error_count()

Returns the error count for the communication between Brick and Bricklet.

The errors are divided into

* ACK checksum errors,
* message checksum errors,
* framing errors and
* overflow errors.

The errors counts are for errors that occur on the Bricklet side. All
Bricks have a similar function that returns the errors on the Brick side.

=cut

sub get_spitfp_error_count
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_GET_SPITFP_ERROR_COUNT, [], '', 'L L L L');
}

=item set_bootloader_mode()

Sets the bootloader mode and returns the status after the requested
mode change was instigated.

You can change from bootloader mode to firmware mode and vice versa. A change
from bootloader mode to firmware mode will only take place if the entry function,
device identifier and CRC are present and correct.

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.

=cut

sub set_bootloader_mode
{
	my ($self, $mode) = @_;

	return $self->_send_request(&FUNCTION_SET_BOOTLOADER_MODE, [$mode], 'C', 'C');
}

=item get_bootloader_mode()

Returns the current bootloader mode, see :func:`Set Bootloader Mode`.

=cut

sub get_bootloader_mode
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_GET_BOOTLOADER_MODE, [], '', 'C');
}

=item set_write_firmware_pointer()

Sets the firmware pointer for :func:`Write Firmware`. The pointer has
to be increased by chunks of size 64. The data is written to flash
every 4 chunks (which equals to one page of size 256).

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.

=cut

sub set_write_firmware_pointer
{
	my ($self, $pointer) = @_;

	$self->_send_request(&FUNCTION_SET_WRITE_FIRMWARE_POINTER, [$pointer], 'L', '');
}

=item write_firmware()

Writes 64 Bytes of firmware at the position as written by
:func:`Set Write Firmware Pointer` before. The firmware is written
to flash every 4 chunks.

You can only write firmware in bootloader mode.

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.

=cut

sub write_firmware
{
	my ($self, $data) = @_;

	return $self->_send_request(&FUNCTION_WRITE_FIRMWARE, [$data], 'C64', 'C');
}

=item set_status_led_config()

Sets the status LED configuration. By default the LED shows
communication traffic between Brick and Bricklet, it flickers once
for every 10 received data packets.

You can also turn the LED permanently on/off or show a heartbeat.

If the Bricklet is in bootloader mode, the LED is will show heartbeat by default.

=cut

sub set_status_led_config
{
	my ($self, $config) = @_;

	$self->_send_request(&FUNCTION_SET_STATUS_LED_CONFIG, [$config], 'C', '');
}

=item get_status_led_config()

Returns the configuration as set by :func:`Set Status LED Config`

=cut

sub get_status_led_config
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_GET_STATUS_LED_CONFIG, [], '', 'C');
}

=item get_chip_temperature()

Returns the temperature in °C as measured inside the microcontroller. The
value returned is not the ambient temperature!

The temperature is only proportional to the real temperature and it has bad
accuracy. Practically it is only useful as an indicator for
temperature changes.

=cut

sub get_chip_temperature
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_GET_CHIP_TEMPERATURE, [], '', 's');
}

=item reset()

Calling this function will reset the Bricklet. All configurations
will be lost.

After a reset you have to create new device objects,
calling functions on the existing ones will result in
undefined behavior!

=cut

sub reset
{
	my ($self) = @_;

	$self->_send_request(&FUNCTION_RESET, [], '', '');
}

=item write_uid()

Writes a new UID into flash. If you want to set a new UID
you have to decode the Base58 encoded UID string into an
integer first.

We recommend that you use Brick Viewer to change the UID.

=cut

sub write_uid
{
	my ($self, $uid) = @_;

	$self->_send_request(&FUNCTION_WRITE_UID, [$uid], 'L', '');
}

=item read_uid()

Returns the current UID as an integer. Encode as
Base58 to get the usual string version.

=cut

sub read_uid
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_READ_UID, [], '', 'L');
}

=item get_identity()

Returns the UID, the UID where the Bricklet is connected to,
the position, the hardware and firmware version as well as the
device identifier.

The position can be 'a', 'b', 'c' or 'd'.

The device identifier numbers can be found :ref:`here <device_identifier>`.
|device_identifier_constant|

=cut

sub get_identity
{
	my ($self) = @_;

	return $self->_send_request(&FUNCTION_GET_IDENTITY, [], '', 'Z8 Z8 a C3 C3 S');
}

=item reader_get_tag_id()

Returns the tag type and the tag ID. This function can only be called if the
NFC Bricklet is currently in one of the *ReaderReady* states. The returned tag ID
is the tag ID that was saved through the last call of :func:`Reader Request Tag ID`.

To get the tag ID of a tag the approach is as follows:

1. Call :func:`Reader Request Tag ID`
2. Wait for state to change to *ReaderRequestTagIDReady* (see :func:`Reader Get State` or
   :cb:`Reader State Changed` callback)
3. Call :func:`Reader Get Tag ID`

=cut

sub reader_get_tag_id
{
    my ($self) = @_;
    my @ret = $self->reader_get_tag_id_low_level();

    splice(@{$ret[2]}, $ret[1]);

    return ($ret[0], $ret[2]);
}

=item reader_write_ndef()

Writes NDEF formated data.

This function currently supports NFC Forum Type 2 and 4.

The general approach for writing a NDEF message is as follows:

1. Call :func:`Reader Request Tag ID`
2. Wait for state to change to *ReaderRequestTagIDReady* (see
   :func:`Reader Get State` or :cb:`Reader State Changed` callback)
3. If looking for a specific tag then call :func:`Reader Get Tag ID` and check
   if the expected tag was found, if it was not found got back to step 1
4. Call :func:`Reader Write NDEF` with the NDEF message that you want to write
5. Wait for state to change to *ReaderWriteNDEFReady* (see :func:`Reader Get State`
   or :cb:`Reader State Changed` callback)

=cut

sub reader_write_ndef
{
    my ($self, $ndef) = @_;

    if(scalar(@{$ndef}) > 65535)
    {
        croak(Tinkerforge::Error->_new(Tinkerforge::Error->INVALID_PARAMETER, 'NDEF can be at most 65535 items long'));
    }

    my $ndef_length = scalar(@{$ndef});
    my $ndef_chunk_offset = 0;

    if($ndef_length == 0)
    {
        my $ndef_chunk_data = [0] x 60;

        $self->reader_write_ndef_low_level($ndef_length, $ndef_chunk_offset, $ndef_chunk_data);
    }
    else
    {
        lock(${$self->{stream_lock_ref}});

        while($ndef_chunk_offset < $ndef_length)
        {
            my $ndef_chunk_data = [];
            my $ndef_chunk_length = $ndef_length - $ndef_chunk_offset;

            if($ndef_chunk_length > 60) {
                $ndef_chunk_length = 60;
            }

            for(my $i = 0; $i < $ndef_chunk_length; $i++) {
                push(@{$ndef_chunk_data}, @{$ndef}[$ndef_chunk_offset + $i]);
            }

            if(scalar(@{$ndef_chunk_data}) < 60)
            {
                push(@{$ndef_chunk_data}, (0) x (60 - scalar(@{$ndef_chunk_data})));
            }

            $self->reader_write_ndef_low_level($ndef_length, $ndef_chunk_offset, $ndef_chunk_data);
            $ndef_chunk_offset += 60;
        }
    }
}

=item reader_read_ndef()

Returns the NDEF data from an internal buffer. To fill the buffer
with a NDEF message you have to call :func:`Reader Request NDEF` beforehand.

=cut

sub reader_read_ndef
{
    my ($self) = @_;

    lock(${$self->{stream_lock_ref}});

    my @ret = $self->reader_read_ndef_low_level();
    my $ndef_length = $ret[0];
    my $ndef_chunk_offset = $ret[1];
    my $ndef_out_of_sync = $ndef_chunk_offset != 0;
    my $ndef_data = $ret[2];

    while(!$ndef_out_of_sync && scalar(@{$ndef_data}) < $ndef_length)
    {
        @ret = $self->reader_read_ndef_low_level();
        $ndef_length = $ret[0];
        $ndef_chunk_offset = $ret[1];
        $ndef_out_of_sync = $ndef_chunk_offset != scalar(@{$ndef_data});
        push(@{$ndef_data}, @{$ret[2]});
    }

    if($ndef_out_of_sync) # discard remaining stream to bring it back in-sync
    {
        while($ndef_chunk_offset + 60 < $ndef_length)
        {
            @ret = $self->reader_read_ndef_low_level();
            $ndef_length = $ret[0];
            $ndef_chunk_offset = $ret[1];
        }

        croak(Tinkerforge::Error->_new(Tinkerforge::Error->STREAM_OUT_OF_SYNC, 'NDEF stream is out-of-sync'));
    }

    splice(@{$ndef_data}, $ndef_length);

    return $ndef_data;
}

=item reader_write_page()

Writes a maximum of 8192 bytes starting from the given page. How many pages are written
depends on the tag type. The page sizes are as follows:

* Mifare Classic page size: 16 byte
* NFC Forum Type 1 page size: 8 byte
* NFC Forum Type 2 page size: 4 byte
* NFC Forum Type 3 page size: 16 byte
* NFC Forum Type 4: No pages, page = file selection (CC or NDEF, see below)

The general approach for writing to a tag is as follows:

1. Call :func:`Reader Request Tag ID`
2. Wait for state to change to *ReaderRequestTagIDReady* (see :func:`Reader Get State` or
   :cb:`Reader State Changed` callback)
3. If looking for a specific tag then call :func:`Reader Get Tag ID` and check if the
   expected tag was found, if it was not found got back to step 1
4. Call :func:`Reader Write Page` with page number and data
5. Wait for state to change to *ReaderWritePageReady* (see :func:`Reader Get State` or
   :cb:`Reader State Changed` callback)

If you use a Mifare Classic tag you have to authenticate a page before you
can write to it. See :func:`Reader Authenticate Mifare Classic Page`.

NFC Forum Type 4 tags are not organized into pages but different files. We currently
support two files: Capability Container file (CC) and NDEF file.

Choose CC by setting page to 3 or NDEF by setting page to 4.

=cut

sub reader_write_page
{
    my ($self, $page, $data) = @_;

    if(scalar(@{$data}) > 65535)
    {
        croak(Tinkerforge::Error->_new(Tinkerforge::Error->INVALID_PARAMETER, 'Data can be at most 65535 items long'));
    }

    my $data_length = scalar(@{$data});
    my $data_chunk_offset = 0;

    if($data_length == 0)
    {
        my $data_chunk_data = [0] x 58;

        $self->reader_write_page_low_level($page, $data_length, $data_chunk_offset, $data_chunk_data);
    }
    else
    {
        lock(${$self->{stream_lock_ref}});

        while($data_chunk_offset < $data_length)
        {
            my $data_chunk_data = [];
            my $data_chunk_length = $data_length - $data_chunk_offset;

            if($data_chunk_length > 58) {
                $data_chunk_length = 58;
            }

            for(my $i = 0; $i < $data_chunk_length; $i++) {
                push(@{$data_chunk_data}, @{$data}[$data_chunk_offset + $i]);
            }

            if(scalar(@{$data_chunk_data}) < 58)
            {
                push(@{$data_chunk_data}, (0) x (58 - scalar(@{$data_chunk_data})));
            }

            $self->reader_write_page_low_level($page, $data_length, $data_chunk_offset, $data_chunk_data);
            $data_chunk_offset += 58;
        }
    }
}

=item reader_read_page()

Returns the page data from an internal buffer. To fill the buffer
with specific pages you have to call :func:`Reader Request Page` beforehand.

=cut

sub reader_read_page
{
    my ($self) = @_;

    lock(${$self->{stream_lock_ref}});

    my @ret = $self->reader_read_page_low_level();
    my $data_length = $ret[0];
    my $data_chunk_offset = $ret[1];
    my $data_out_of_sync = $data_chunk_offset != 0;
    my $data_data = $ret[2];

    while(!$data_out_of_sync && scalar(@{$data_data}) < $data_length)
    {
        @ret = $self->reader_read_page_low_level();
        $data_length = $ret[0];
        $data_chunk_offset = $ret[1];
        $data_out_of_sync = $data_chunk_offset != scalar(@{$data_data});
        push(@{$data_data}, @{$ret[2]});
    }

    if($data_out_of_sync) # discard remaining stream to bring it back in-sync
    {
        while($data_chunk_offset + 60 < $data_length)
        {
            @ret = $self->reader_read_page_low_level();
            $data_length = $ret[0];
            $data_chunk_offset = $ret[1];
        }

        croak(Tinkerforge::Error->_new(Tinkerforge::Error->STREAM_OUT_OF_SYNC, 'Data stream is out-of-sync'));
    }

    splice(@{$data_data}, $data_length);

    return $data_data;
}

=item cardemu_write_ndef()

Writes the NDEF messages that is to be transferred to the NFC peer.

The maximum supported NDEF message size in Cardemu mode is 255 byte.

You can call this function at any time in Cardemu mode. The internal buffer
will not be overwritten until you call this function again or change the
mode.

=cut

sub cardemu_write_ndef
{
    my ($self, $ndef) = @_;

    if(scalar(@{$ndef}) > 65535)
    {
        croak(Tinkerforge::Error->_new(Tinkerforge::Error->INVALID_PARAMETER, 'NDEF can be at most 65535 items long'));
    }

    my $ndef_length = scalar(@{$ndef});
    my $ndef_chunk_offset = 0;

    if($ndef_length == 0)
    {
        my $ndef_chunk_data = [0] x 60;

        $self->cardemu_write_ndef_low_level($ndef_length, $ndef_chunk_offset, $ndef_chunk_data);
    }
    else
    {
        lock(${$self->{stream_lock_ref}});

        while($ndef_chunk_offset < $ndef_length)
        {
            my $ndef_chunk_data = [];
            my $ndef_chunk_length = $ndef_length - $ndef_chunk_offset;

            if($ndef_chunk_length > 60) {
                $ndef_chunk_length = 60;
            }

            for(my $i = 0; $i < $ndef_chunk_length; $i++) {
                push(@{$ndef_chunk_data}, @{$ndef}[$ndef_chunk_offset + $i]);
            }

            if(scalar(@{$ndef_chunk_data}) < 60)
            {
                push(@{$ndef_chunk_data}, (0) x (60 - scalar(@{$ndef_chunk_data})));
            }

            $self->cardemu_write_ndef_low_level($ndef_length, $ndef_chunk_offset, $ndef_chunk_data);
            $ndef_chunk_offset += 60;
        }
    }
}

=item p2p_write_ndef()

Writes the NDEF messages that is to be transferred to the NFC peer.

The maximum supported NDEF message size for P2P transfer is 255 byte.

You can call this function at any time in P2P mode. The internal buffer
will not be overwritten until you call this function again, change the
mode or use P2P to read an NDEF messages.

=cut

sub p2p_write_ndef
{
    my ($self, $ndef) = @_;

    if(scalar(@{$ndef}) > 65535)
    {
        croak(Tinkerforge::Error->_new(Tinkerforge::Error->INVALID_PARAMETER, 'NDEF can be at most 65535 items long'));
    }

    my $ndef_length = scalar(@{$ndef});
    my $ndef_chunk_offset = 0;

    if($ndef_length == 0)
    {
        my $ndef_chunk_data = [0] x 60;

        $self->p2p_write_ndef_low_level($ndef_length, $ndef_chunk_offset, $ndef_chunk_data);
    }
    else
    {
        lock(${$self->{stream_lock_ref}});

        while($ndef_chunk_offset < $ndef_length)
        {
            my $ndef_chunk_data = [];
            my $ndef_chunk_length = $ndef_length - $ndef_chunk_offset;

            if($ndef_chunk_length > 60) {
                $ndef_chunk_length = 60;
            }

            for(my $i = 0; $i < $ndef_chunk_length; $i++) {
                push(@{$ndef_chunk_data}, @{$ndef}[$ndef_chunk_offset + $i]);
            }

            if(scalar(@{$ndef_chunk_data}) < 60)
            {
                push(@{$ndef_chunk_data}, (0) x (60 - scalar(@{$ndef_chunk_data})));
            }

            $self->p2p_write_ndef_low_level($ndef_length, $ndef_chunk_offset, $ndef_chunk_data);
            $ndef_chunk_offset += 60;
        }
    }
}

=item p2p_read_ndef()

Returns the NDEF message that was written by a NFC peer in NFC P2P mode.

The NDEF message is ready if you called :func:`P2P Start Transfer` with a
read transfer and the P2P state changed to *P2PTransferNDEFReady*.

=cut

sub p2p_read_ndef
{
    my ($self) = @_;

    lock(${$self->{stream_lock_ref}});

    my @ret = $self->p2p_read_ndef_low_level();
    my $ndef_length = $ret[0];
    my $ndef_chunk_offset = $ret[1];
    my $ndef_out_of_sync = $ndef_chunk_offset != 0;
    my $ndef_data = $ret[2];

    while(!$ndef_out_of_sync && scalar(@{$ndef_data}) < $ndef_length)
    {
        @ret = $self->p2p_read_ndef_low_level();
        $ndef_length = $ret[0];
        $ndef_chunk_offset = $ret[1];
        $ndef_out_of_sync = $ndef_chunk_offset != scalar(@{$ndef_data});
        push(@{$ndef_data}, @{$ret[2]});
    }

    if($ndef_out_of_sync) # discard remaining stream to bring it back in-sync
    {
        while($ndef_chunk_offset + 60 < $ndef_length)
        {
            @ret = $self->p2p_read_ndef_low_level();
            $ndef_length = $ret[0];
            $ndef_chunk_offset = $ret[1];
        }

        croak(Tinkerforge::Error->_new(Tinkerforge::Error->STREAM_OUT_OF_SYNC, 'NDEF stream is out-of-sync'));
    }

    splice(@{$ndef_data}, $ndef_length);

    return $ndef_data;
}

=back
=cut

1;
