Reading the last paragraph of the following it looks like there is a way to send large blocks of data in response to an NFC issuing an inDataExchange call by breaking it up into small chunks:
3.3.7.4 Transfer of large amount of data
Chaining mechanism
- from initiator to target: Large amount of data are sent by the initiator with InDataExchange function, in packets of 252 bytes of data. The initiator must send InDataExchange command as many times as necessary to transfer the complete amount of data. The target must perform TgGetData and TgSetData functions as many times as necessary to retrieve all packets sent by the initiator.
Metachaining mechanism
- From initiator to target: One bit called MI (more information), in InDataExchange first parameter, indicates to the target if data received are part of a large block. In that case, the target can directly continue the exchange with TgGetData (no TgSetData needed).
- From target to initiator: The target can provide the initiator with large amount of data using TgSetMetaData function. The initiator has sent a InDataExchange function. The response to the initiator is sent via TgSetMetaData function instead of TgSetData function. In that case, one bit indicates to the initiator that some data are still available at target side. The initiator shall go on with a InDataExchange function (with no data sent from the intiator to the target). Last packet of data will be transferred with TgSetData function. Refer to the PN532 User manual (reference 1) for detailed explanation.
When I do a "normal" response to an APDU call in my Android HCE, it looks something like this:
private static final byte[] SELECT_OK_SW = HexStringToByteArray("9000");
private static final byte[] UNKNOWN_CMD_SW = HexStringToByteArray("0000");
@Override
public byte[] processCommandApdu(byte[] commandApdu, Bundle extras) {
Log.d(TAG, "Received APDU: " + ByteArrayToHexString(commandApdu));
// If the APDU matches the SELECT AID command for this service,
// send the account name, followed by a SELECT_OK status trailer (0x9000).
if (Arrays.equals(SELECT_APDU, commandApdu)) {
String account="Tommyburger";
byte[] accountBytes = account.getBytes();
Log.d(TAG, "Sending account number: " + account);
return ConcatArrays(accountBytes, SELECT_OK_SW);
} else {
Log.d(TAG, "AID didn't match");
return UNKNOWN_CMD_SW;
}
}
Looking at the "command constants" from the 532.h file:
// PN532 Commands
#define PN532_COMMAND_DIAGNOSE (0x00)
#define PN532_COMMAND_GETFIRMWAREVERSION (0x02)
#define PN532_COMMAND_GETGENERALSTATUS (0x04)
#define PN532_COMMAND_READREGISTER (0x06)
#define PN532_COMMAND_WRITEREGISTER (0x08)
#define PN532_COMMAND_READGPIO (0x0C)
#define PN532_COMMAND_WRITEGPIO (0x0E)
#define PN532_COMMAND_SETSERIALBAUDRATE (0x10)
#define PN532_COMMAND_SETPARAMETERS (0x12)
#define PN532_COMMAND_SAMCONFIGURATION (0x14)
#define PN532_COMMAND_POWERDOWN (0x16)
#define PN532_COMMAND_RFCONFIGURATION (0x32)
#define PN532_COMMAND_RFREGULATIONTEST (0x58)
#define PN532_COMMAND_INJUMPFORDEP (0x56)
#define PN532_COMMAND_INJUMPFORPSL (0x46)
#define PN532_COMMAND_INLISTPASSIVETARGET (0x4A)
#define PN532_COMMAND_INATR (0x50)
#define PN532_COMMAND_INPSL (0x4E)
#define PN532_COMMAND_INDATAEXCHANGE (0x40)
#define PN532_COMMAND_INCOMMUNICATETHRU (0x42)
#define PN532_COMMAND_INDESELECT (0x44)
#define PN532_COMMAND_INRELEASE (0x52)
#define PN532_COMMAND_INSELECT (0x54)
#define PN532_COMMAND_INAUTOPOLL (0x60)
#define PN532_COMMAND_TGINITASTARGET (0x8C)
#define PN532_COMMAND_TGSETGENERALBYTES (0x92)
#define PN532_COMMAND_TGGETDATA (0x86)
#define PN532_COMMAND_TGSETDATA (0x8E)
#define PN532_COMMAND_TGSETMETADATA (0x94)
#define PN532_COMMAND_TGGETINITIATORCOMMAND (0x88)
#define PN532_COMMAND_TGRESPONSETOINITIATOR (0x90)
#define PN532_COMMAND_TGGETTARGETSTATUS (0x8A)
#define PN532_RESPONSE_INDATAEXCHANGE (0x41)
#define PN532_RESPONSE_INLISTPASSIVETARGET (0x4B)
It would seem that I usually do the TGRESPONSETOINITIATOR command 0x9000 as SELECT_OK_SW in Android HCE. So to send, for example, 1k bytes, I would break it up into blocks of 256 bytes (or a little smaller) and send with TGSETMETADATA command byte 0x9400, except the the last block, which would use TGSETDATA command byte 0x8E00? Has anyone done this before?