Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FeliCa support #127

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 301 additions & 1 deletion Adafruit_PN532.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ byte pn532response_firmwarevers[] = {
#define PN532DEBUGPRINT Serial ///< Fixed name for debug Serial instance
//#define PN532DEBUGPRINT SerialUSB ///< Fixed name for debug Serial instance

#define PN532_PACKBUFFSIZ 64 ///< Packet buffer size in bytes
#define PN532_PACKBUFFSIZ 240 ///< Packet buffer size in bytes
byte pn532_packetbuffer[PN532_PACKBUFFSIZ]; ///< Packet buffer used in various
///< transactions

Expand Down Expand Up @@ -1520,6 +1520,306 @@ uint8_t Adafruit_PN532::ntag2xx_WriteNDEFURI(uint8_t uriIdentifier, char *url,
return 1;
}

/***** Felica Functions ******/

/**************************************************************************/
/*!
@brief Poll FeliCa card. PN532 acting as reader/initiator,
peer acting as card/responder.
@param[in] systemCode Designation of System Code. When sending
FFFFh as System Code, all FeliCa cards can return response.
@param[in] requestCode Designation of Request Data as follows:
00h: No Request
01h: System Code request (to acquire
System Code of the card) 02h: Communication perfomance request
@param[out] idm IDm of the card (8 bytes)
@param[out] pmm PMm of the card (8 bytes)
@param[out] systemCodeResponse System Code of the card (Optional,
2bytes)
@param[in] timeout Timeout for every command
@return = 1: A FeliCa card has detected
= 0: No card has detected
*/
/**************************************************************************/
uint8_t Adafruit_PN532::felica_Polling(uint16_t systemCode, uint8_t requestCode,
uint8_t *idm, uint8_t *pmm,
uint16_t *systemCodeResponse,
uint16_t timeout) {
pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET;
pn532_packetbuffer[1] = 1;
pn532_packetbuffer[2] = 1;
pn532_packetbuffer[3] = FELICA_CMD_POLLING;
pn532_packetbuffer[4] = (systemCode >> 8) & 0xFF;
pn532_packetbuffer[5] = systemCode & 0xFF;
pn532_packetbuffer[6] = requestCode;
pn532_packetbuffer[7] = 0;

if (!sendCommandCheckAck(pn532_packetbuffer, 8, timeout)) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("Error sending command");
#endif
return 0;
}
/* Response structure:
0-9 not used
10 response code(should be 01)
11-19 IDm
19-27 PMm
27-29 Request data(only when request code is not 0x00 and supported by
card)
*/
readdata(pn532_packetbuffer, requestCode == 0x01 ? 29 : 27);
#ifdef PN532DEBUG
PrintHex(pn532_packetbuffer, requestCode == 0x01 ? 29 : 27);
#endif
if ((sizeof(pn532_packetbuffer) / sizeof(byte)) == 0) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("Could not receive response");
#endif
return 0;
}

// Check for Response code == 0x01
if (pn532_packetbuffer[10] != 0x01) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("No card had detected");
#endif
return 0;
}

_inListedTag = pn532_packetbuffer[8];

// Fill data
memcpy(idm, &pn532_packetbuffer[11], 8);
memcpy(_felicaIDm, &pn532_packetbuffer[11], 8);
memcpy(pmm, &pn532_packetbuffer[19], 8);
memcpy(_felicaPMm, &pn532_packetbuffer[19], 8);
if (requestCode == 0x01)
*systemCodeResponse =
(pn532_packetbuffer[27] << 8) + pn532_packetbuffer[28];

return 1;
}

/**************************************************************************/
/*!
@brief Sends FeliCa command to the currently inlisted peer and get response
in buffer

@param[in] command FeliCa command packet. (e.g. 00 FF FF 00 00 for
Polling command)
@param[in] commandlength Length of the FeliCa command packet. (e.g. 0x05
for above Polling command )
@param[out] responseLength Length of the FeliCa response packet. (e.g. 0x11
for above Polling command )
@return = 1: Success
= 0: error
*/
/**************************************************************************/
uint8_t Adafruit_PN532::felica_SendCommand(const uint8_t *command,
uint8_t commandlength,
uint8_t responseLength) {
if (commandlength > 0xFE) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("Command length too long");
#endif
return -1;
}

pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE;
pn532_packetbuffer[1] = _inListedTag;
pn532_packetbuffer[2] = commandlength + 1;
memcpy(&pn532_packetbuffer[3], command, commandlength);

if (!sendCommandCheckAck(pn532_packetbuffer, 3 + commandlength)) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("Could not send FeliCa command");
#endif
return 0;
}

/* Read data in packet buffer */
readdata(pn532_packetbuffer, responseLength);

return 1;
}

/**************************************************************************/
/*!
@brief Sends FeliCa Read Without Encryption command

@param[in] numService Length of the serviceCodeList
@param[in] serviceCodeList Service Code List (Big Endian)
@param[in] numBlock Length of the blockList
@param[in] blockList Block List (Big Endian, This API only accepts
2-byte block list element)
@param[out] blockData Block Data
@return = 1: Success
= 0: error
*/
/**************************************************************************/
uint8_t Adafruit_PN532::felica_ReadWithoutEncryption(
uint8_t numService, const uint16_t *serviceCodeList, uint8_t numBlock,
const uint16_t *blockList, uint8_t blockData[][16]) {
if (numService > FELICA_READ_MAX_SERVICE_NUM) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("numService is too large");
#endif
return 0;
}
if (numBlock > FELICA_READ_MAX_BLOCK_NUM) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("numBlock is too large");
#endif
return 0;
}

uint8_t i;
uint8_t cmdLen = 1 + 8 + 1 + 2 * numService + 1 + 2 * numBlock;
uint8_t cmd[cmdLen];
cmd[0] = FELICA_CMD_READ_WITHOUT_ENCRYPTION;
memcpy(&cmd[1], _felicaIDm, 8); // Fill card IDm

/* Fill number of service and service code list */
cmd[9] = numService;
int j = 10;
for (i = 0; i < numService; ++i) {
cmd[j++] = serviceCodeList[i] & 0xFF;
cmd[j++] = (serviceCodeList[i] >> 8) & 0xff;
}

/* Fill number of block and blocklist */
cmd[j++] = numBlock;
for (i = 0; i < numBlock; ++i) {
cmd[j++] = (blockList[i] >> 8) & 0xFF;
cmd[j++] = blockList[i] & 0xff;
}

const uint8_t responseLength = 20 + 16 * numBlock;
if (felica_SendCommand(cmd, cmdLen, responseLength) != 1) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("Read Without Encryption command failed");
#endif
return 0;
}

if (pn532_packetbuffer[9] != 0x07) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("Invalid response");
#endif
return 0;
}

if (pn532_packetbuffer[18] != 0x00) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("Invalid status flag");
#endif
return 0;
}

/* Copy block data from buffer to blockData parameter */
for (size_t i = 0; i < numBlock; i++) {
memcpy(blockData[i], &pn532_packetbuffer[(21 * i) + 21], 16);
}

return 1;
}

/**************************************************************************/
/*!
@brief Sends FeliCa Write Without Encryption command

@param[in] numService Length of the serviceCodeList
@param[in] serviceCodeList Service Code List (Big Endian)
@param[in] numBlock Length of the blockList
@param[in] blockList Block List (Big Endian, This API only accepts
2-byte block list element)
@param[in] blockData Block Data (each Block has 16 bytes)
@return = 1: Success
= 0: error
*/
/**************************************************************************/
uint8_t Adafruit_PN532::felica_WriteWithoutEncryption(
uint8_t numService, const uint16_t *serviceCodeList, uint8_t numBlock,
const uint16_t *blockList, uint8_t blockData[][16]) {
if (numService > FELICA_WRITE_MAX_SERVICE_NUM) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("numService is too large");
#endif
return 0;
}
if (numBlock > FELICA_WRITE_MAX_BLOCK_NUM) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("numBlock is too large");
#endif
return 0;
}

uint8_t i, k;
uint8_t cmdLen =
1 + 8 + 1 + 2 * numService + 1 + 2 * numBlock + 16 * numBlock;
uint8_t cmd[cmdLen];
cmd[0] = FELICA_CMD_WRITE_WITHOUT_ENCRYPTION;
memcpy(&cmd[1], _felicaIDm, 8); // Fill card IDm
int j = 10;
cmd[j++] = numService;
for (i = 0; i < numService; ++i) {
cmd[j++] = serviceCodeList[i] & 0xFF;
cmd[j++] = (serviceCodeList[i] >> 8) & 0xff;
}
cmd[j++] = numBlock;
for (i = 0; i < numBlock; ++i) {
cmd[j++] = (blockList[i] >> 8) & 0xFF;
cmd[j++] = blockList[i] & 0xff;
}
for (i = 0; i < numBlock; ++i) {
for (k = 0; k < 16; k++) {
cmd[j++] = blockData[i][k];
}
}

if (felica_SendCommand(cmd, cmdLen, 11) != 1) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("Write Without Encryption command failed");
#endif
return 0;
}

if (pn532_packetbuffer[9] != 0x09) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.printf("Response code doesn't match. Status: %i\n",
pn532_packetbuffer[9]);
#endif
return 0;
}
return 1;
}

/**************************************************************************/
/*!
@brief Release FeliCa card
@return = 1: Success
= 0: error
*/
/**************************************************************************/
uint8_t Adafruit_PN532::felica_Release() {
// InRelease
pn532_packetbuffer[0] = PN532_COMMAND_INRELEASE;
pn532_packetbuffer[1] = 0x00; // All target
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("Release all FeliCa target");
#endif

if (sendCommandCheckAck(pn532_packetbuffer, 2)) {
#ifdef PN532DEBUG
PN532DEBUGPRINT.println("No ACK");
#endif
return 0; // no ACK
}

return 1;
}

/************** high level communication functions (handles both I2C and SPI) */

/**************************************************************************/
Expand Down
43 changes: 39 additions & 4 deletions Adafruit_PN532.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@
#define MIFARE_CMD_STORE (0xC2) ///< Store
#define MIFARE_ULTRALIGHT_CMD_WRITE (0xA2) ///< Write (MiFare Ultralight)

// FeliCa Commands
#define FELICA_CMD_POLLING (0x00) ///< Polling cmd
#define FELICA_CMD_READ_WITHOUT_ENCRYPTION (0x06) ///< Read without encryption
#define FELICA_CMD_WRITE_WITHOUT_ENCRYPTION (0x08) ///< Write without encryption

// Prefixes for NDEF Records (to identify record type)
#define NDEF_URIPREFIX_NONE (0x00) ///< No prefix
#define NDEF_URIPREFIX_HTTP_WWWDOT (0x01) ///< HTTP www. prefix
Expand Down Expand Up @@ -135,6 +140,16 @@
#define PN532_GPIO_P34 (4) ///< GPIO 34
#define PN532_GPIO_P35 (5) ///< GPIO 35

// FeliCa consts
#define FELICA_READ_MAX_SERVICE_NUM \
16 ///< Maximum number of services code during reading
#define FELICA_READ_MAX_BLOCK_NUM 12 ///< for typical FeliCa card
#define FELICA_WRITE_MAX_SERVICE_NUM \
16 ///< Maximum number of services code during writing
#define FELICA_WRITE_MAX_BLOCK_NUM 10 ///< for typical FeliCa card
#define FELICA_REQ_SERVICE_MAX_NODE_NUM \
32 ///< Max node for a request service call

/**
* @brief Class for working with Adafruit PN532 NFC/RFID breakout boards.
*/
Expand Down Expand Up @@ -195,16 +210,36 @@ class Adafruit_PN532 {
uint8_t ntag2xx_WriteNDEFURI(uint8_t uriIdentifier, char *url,
uint8_t dataLen);

// Felica functions
uint8_t felica_Polling(uint16_t systemCode, uint8_t requestCode, uint8_t *idm,
uint8_t *pmm, uint16_t *systemCodeResponse,
uint16_t timeout = 1000);
uint8_t felica_SendCommand(const uint8_t *command, uint8_t commandlength,
uint8_t responseLength);
uint8_t felica_ReadWithoutEncryption(uint8_t numService,
const uint16_t *serviceCodeList,
uint8_t numBlock,
const uint16_t *blockList,
uint8_t blockData[][16]);
uint8_t felica_WriteWithoutEncryption(uint8_t numService,
const uint16_t *serviceCodeList,
uint8_t numBlock,
const uint16_t *blockList,
uint8_t blockData[][16]);
uint8_t felica_Release();

// Help functions to display formatted text
static void PrintHex(const byte *data, const uint32_t numBytes);
static void PrintHexChar(const byte *pbtData, const uint32_t numBytes);

private:
int8_t _irq = -1, _reset = -1, _cs = -1;
int8_t _uid[7]; // ISO14443A uid
int8_t _uidLen; // uid len
int8_t _key[6]; // Mifare Classic key
int8_t _inListedTag; // Tg number of inlisted tag.
int8_t _uid[7]; // ISO14443A uid
int8_t _uidLen; // uid len
int8_t _key[6]; // Mifare Classic key
int8_t _inListedTag; // Tg number of inlisted tag.
uint8_t _felicaIDm[8]; // FeliCa IDm (NFCID2)
uint8_t _felicaPMm[8]; // FeliCa PMm (PAD)

// Low level communication functions that handle both SPI and I2C.
void readdata(uint8_t *buff, uint8_t n);
Expand Down
Loading
Loading