blob: c634591cc52ace6a8791bb891d2c25d02bd6cff3 [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2010-2014 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* This file contains the implementation for Type 4 tag in Reader/Writer
* mode.
*
******************************************************************************/
#include <string.h>
#include "nfc_target.h"
#include "bt_types.h"
#include "trace_api.h"
#if (NFC_INCLUDED == TRUE)
#include "nfc_api.h"
#include "nfc_int.h"
#include "rw_api.h"
#include "rw_int.h"
#include "tags_int.h"
#include "gki.h"
/* main state */
#define RW_T4T_STATE_NOT_ACTIVATED 0x00 /* T4T is not activated */
#define RW_T4T_STATE_IDLE 0x01 /* waiting for upper layer API */
#define RW_T4T_STATE_DETECT_NDEF 0x02 /* performing NDEF detection precedure */
#define RW_T4T_STATE_READ_NDEF 0x03 /* performing read NDEF procedure */
#define RW_T4T_STATE_UPDATE_NDEF 0x04 /* performing update NDEF procedure */
#define RW_T4T_STATE_PRESENCE_CHECK 0x05 /* checking presence of tag */
#define RW_T4T_STATE_SET_READ_ONLY 0x06 /* convert tag to read only */
#define RW_T4T_STATE_NDEF_FORMAT 0x07 /* performing NDEF format */
/* sub state */
#define RW_T4T_SUBSTATE_WAIT_SELECT_APP 0x00 /* waiting for response of selecting AID */
#define RW_T4T_SUBSTATE_WAIT_SELECT_CC 0x01 /* waiting for response of selecting CC */
#define RW_T4T_SUBSTATE_WAIT_CC_FILE 0x02 /* waiting for response of reading CC */
#define RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE 0x03 /* waiting for response of selecting NDEF */
#define RW_T4T_SUBSTATE_WAIT_READ_NLEN 0x04 /* waiting for response of reading NLEN */
#define RW_T4T_SUBSTATE_WAIT_READ_RESP 0x05 /* waiting for response of reading file */
#define RW_T4T_SUBSTATE_WAIT_UPDATE_RESP 0x06 /* waiting for response of updating file */
#define RW_T4T_SUBSTATE_WAIT_UPDATE_NLEN 0x07 /* waiting for response of updating NLEN */
#define RW_T4T_SUBSTATE_WAIT_UPDATE_CC 0x08 /* waiting for response of updating CC */
#define RW_T4T_SUBSTATE_WAIT_GET_HW_VERSION 0x09
#define RW_T4T_SUBSTATE_WAIT_GET_SW_VERSION 0x0A
#define RW_T4T_SUBSTATE_WAIT_GET_UID 0x0B
#define RW_T4T_SUBSTATE_WAIT_CREATE_APP 0x0C
#define RW_T4T_SUBSTATE_WAIT_CREATE_CC 0x0D
#define RW_T4T_SUBSTATE_WAIT_CREATE_NDEF 0x0E
#define RW_T4T_SUBSTATE_WAIT_WRITE_CC 0x0F
#define RW_T4T_SUBSTATE_WAIT_WRITE_NDEF 0x10
#if (BT_TRACE_VERBOSE == TRUE)
static char *rw_t4t_get_state_name (UINT8 state);
static char *rw_t4t_get_sub_state_name (UINT8 sub_state);
#endif
static BOOLEAN rw_t4t_send_to_lower (BT_HDR *p_c_apdu);
static BOOLEAN rw_t4t_select_file (UINT16 file_id);
static BOOLEAN rw_t4t_read_file (UINT16 offset, UINT16 length, BOOLEAN is_continue);
static BOOLEAN rw_t4t_update_nlen (UINT16 ndef_len);
static BOOLEAN rw_t4t_update_file (void);
static BOOLEAN rw_t4t_update_cc_to_readonly (void);
static BOOLEAN rw_t4t_select_application (UINT8 version);
static BOOLEAN rw_t4t_validate_cc_file (void);
static BOOLEAN rw_t4t_get_hw_version (void);
static BOOLEAN rw_t4t_get_sw_version (void);
static BOOLEAN rw_t4t_create_app (void);
static BOOLEAN rw_t4t_select_app (void);
static BOOLEAN rw_t4t_create_ccfile (void);
static BOOLEAN rw_t4t_create_ndef (void);
static BOOLEAN rw_t4t_write_cc (void);
static BOOLEAN rw_t4t_write_ndef (void);
static void rw_t4t_handle_error (tNFC_STATUS status, UINT8 sw1, UINT8 sw2);
static void rw_t4t_sm_detect_ndef (BT_HDR *p_r_apdu);
static void rw_t4t_sm_read_ndef (BT_HDR *p_r_apdu);
static void rw_t4t_sm_update_ndef (BT_HDR *p_r_apdu);
static void rw_t4t_sm_set_readonly (BT_HDR *p_r_apdu);
static void rw_t4t_data_cback (UINT8 conn_id, tNFC_CONN_EVT event, tNFC_CONN *p_data);
static void rw_t4t_sm_ndef_format (BT_HDR *p_r_apdu);
/*******************************************************************************
**
** Function rw_t4t_send_to_lower
**
** Description Send C-APDU to lower layer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_send_to_lower (BT_HDR *p_c_apdu)
{
#if (BT_TRACE_PROTOCOL == TRUE)
DispRWT4Tags (p_c_apdu, FALSE);
#endif
if (NFC_SendData (NFC_RF_CONN_ID, p_c_apdu) != NFC_STATUS_OK)
{
RW_TRACE_ERROR0 ("rw_t4t_send_to_lower (): NFC_SendData () failed");
return FALSE;
}
nfc_start_quick_timer (&rw_cb.tcb.t4t.timer, NFC_TTYPE_RW_T4T_RESPONSE,
(RW_T4T_TOUT_RESP * QUICK_TIMER_TICKS_PER_SEC) / 1000);
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_get_hw_version
**
** Description Send get hw version cmd to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_get_hw_version (void)
{
BT_HDR *p_c_apdu;
UINT8 *p;
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_get_hw_version (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_INS_GET_HW_VERSION);
UINT16_TO_BE_STREAM (p, 0x0000);
UINT8_TO_BE_FIELD (p, 0x00);
p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_get_sw_version
**
** Description Send get sw version cmd to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_get_sw_version (void)
{
BT_HDR *p_c_apdu;
UINT8 *p;
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_get_sw_version (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_CLASS);
UINT8_TO_BE_STREAM (p, T4T_ADDI_FRAME_RESP);
UINT16_TO_BE_STREAM (p, 0x0000);
UINT8_TO_BE_FIELD (p, 0x00);
p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_update_version_details
**
** Description Updates the size of the card
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_update_version_details (BT_HDR *p_r_apdu)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
UINT8 *p;
UINT16 major_version, minor_version;
p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;
major_version = *(p + 3);
minor_version = *(p + 4);
if ((T4T_DESEV0_MAJOR_VERSION == major_version) &&
(T4T_DESEV0_MINOR_VERSION == minor_version))
{
p_t4t->card_size = 0xEDE;
}
else if (major_version >= T4T_DESEV1_MAJOR_VERSION)
{
p_t4t->card_type = T4T_TYPE_DESFIRE_EV1;
switch (*(p + 5))
{
case T4T_SIZE_IDENTIFIER_2K:
p_t4t->card_size = 2048;
break;
case T4T_SIZE_IDENTIFIER_4K:
p_t4t->card_size = 4096;
break;
case T4T_SIZE_IDENTIFIER_8K:
p_t4t->card_size = 7680;
break;
default:
return FALSE;
}
}
else
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_get_uid_details
**
** Description Send get uid cmd to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_get_uid_details (void)
{
BT_HDR *p_c_apdu;
UINT8 *p;
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_get_uid_details (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_CLASS);
UINT8_TO_BE_STREAM (p, T4T_ADDI_FRAME_RESP);
UINT16_TO_BE_STREAM (p, 0x0000);
UINT8_TO_BE_FIELD (p, 0x00);
p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_create_app
**
** Description Send create application cmd to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_create_app (void)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
BT_HDR *p_c_apdu;
UINT8 *p;
UINT8 df_name[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01};
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_create_app (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_CREATE_AID);
UINT16_TO_BE_STREAM (p, 0x0000);
if (p_t4t->card_type == T4T_TYPE_DESFIRE_EV1)
{
UINT8_TO_BE_STREAM (p, (T4T_CMD_MAX_HDR_SIZE + sizeof(df_name) + 2));
UINT24_TO_BE_STREAM (p, T4T_DES_EV1_NFC_APP_ID);
UINT16_TO_BE_STREAM (p, 0x0F21); /*Key settings and no.of keys */
UINT16_TO_BE_STREAM (p, 0x05E1); /* ISO file ID */
ARRAY_TO_BE_STREAM (p, df_name, sizeof(df_name)); /*DF file name */
UINT8_TO_BE_STREAM (p, 0x00); /* Le */
p_c_apdu->len = 20;
}
else
{
UINT8_TO_BE_STREAM (p, T4T_CMD_MAX_HDR_SIZE);
UINT24_TO_BE_STREAM (p, T4T_DES_EV0_NFC_APP_ID);
UINT16_TO_BE_STREAM (p, 0x0F01); /*Key settings and no.of keys */
UINT8_TO_BE_STREAM (p, 0x00); /* Le */
p_c_apdu->len = 11;
}
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_select_app
**
** Description Select application cmd to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_select_app (void)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
BT_HDR *p_c_apdu;
UINT8 *p;
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_select_app (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_SELECT_APP);
UINT16_TO_BE_STREAM (p, 0x0000);
UINT8_TO_BE_STREAM (p, 0x03); /* Lc: length of wrapped data */
if (p_t4t->card_type == T4T_TYPE_DESFIRE_EV1)
{
UINT24_TO_BE_STREAM (p, T4T_DES_EV1_NFC_APP_ID);
}
else
{
UINT24_TO_BE_STREAM(p, T4T_DES_EV0_NFC_APP_ID);
}
UINT8_TO_BE_STREAM (p, 0x00); /* Le */
p_c_apdu->len = 9;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_create_ccfile
**
** Description create capability container file cmd to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_create_ccfile (void)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
BT_HDR *p_c_apdu;
UINT8 *p;
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_create_ccfile (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_CREATE_DATAFILE);
UINT16_TO_BE_STREAM (p, 0x0000);
if (p_t4t->card_type == T4T_TYPE_DESFIRE_EV1)
{
UINT8_TO_BE_STREAM (p, 0x09); /* Lc: length of wrapped data */
UINT8_TO_BE_STREAM (p, 0x01); /* EV1 CC file id */
UINT16_TO_BE_STREAM (p, 0x03E1); /* ISO file id */
}
else
{
UINT8_TO_BE_STREAM (p, 0x07); /* Lc: length of wrapped data */
UINT8_TO_BE_STREAM (p, 0x03); /* DESFire CC file id */
}
UINT8_TO_BE_STREAM (p, 0x00); /* COMM settings */
UINT16_TO_BE_STREAM (p, 0xEEEE); /* Access rights */
UINT24_TO_BE_STREAM (p, 0x0F0000); /* Set file size */
UINT8_TO_BE_STREAM (p, 0x00); /* Le */
p_c_apdu->len = (p_t4t->card_type == T4T_TYPE_DESFIRE_EV1) ? 15 : 13;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_create_ndef
**
** Description creates an ndef file cmd to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_create_ndef (void)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
BT_HDR *p_c_apdu;
UINT8 *p;
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_create_ndef (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_CREATE_DATAFILE);
UINT16_TO_BE_STREAM (p, 0x0000);
if (p_t4t->card_type == T4T_TYPE_DESFIRE_EV1)
{
UINT8_TO_BE_STREAM (p, 0x09); /* Lc: length of wrapped data */
UINT8_TO_BE_STREAM (p, 0x02); /* DESFEv1 NDEF file id */
UINT16_TO_BE_STREAM (p, 0x04E1); /* ISO file id */
}
else
{
UINT8_TO_BE_STREAM (p, 0x07);
UINT8_TO_BE_STREAM (p, 0x04); /* DESF4 NDEF file id */
}
UINT8_TO_BE_STREAM (p, 0x00); /* COMM settings */
UINT16_TO_BE_STREAM (p, 0xEEEE); /* Access rights */
UINT16_TO_STREAM (p, p_t4t->card_size);
UINT8_TO_BE_STREAM (p, 0x00); /* Set card size */
UINT8_TO_BE_STREAM (p, 0x00); /* Le */
p_c_apdu->len = (p_t4t->card_type == T4T_TYPE_DESFIRE_EV1) ? 15 : 13;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_write_cc
**
** Description sends write cc file cmd to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_write_cc (void)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
BT_HDR *p_c_apdu;
UINT8 *p;
UINT8 CCFileBytes[] = {0x00, 0x0F, 0x10, 0x00, 0x3B, 0x00, 0x34, 0x04, 0x06, 0xE1, 0x04, 0x04, 0x00, 0x00, 0x00};
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_write_cc (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_WRITE);
UINT16_TO_BE_STREAM (p, 0x0000);
UINT8_TO_BE_STREAM (p, 0x16); /* Lc: length of wrapped data */
if (p_t4t->card_type == T4T_TYPE_DESFIRE_EV1)
{
CCFileBytes[2] = 0x20;
CCFileBytes[11] = p_t4t->card_size >> 8;
CCFileBytes[12] = (UINT8) p_t4t->card_size;
UINT8_TO_BE_STREAM (p, 0x01); /* CC file id */
}
else
{
UINT8_TO_BE_STREAM (p, 0x03);
}
UINT24_TO_BE_STREAM (p, 0x000000); /* Set the offset */
UINT24_TO_BE_STREAM (p, 0x0F0000); /* Set available length */
ARRAY_TO_BE_STREAM (p, CCFileBytes, sizeof(CCFileBytes));
UINT8_TO_BE_STREAM (p, 0x00); /* Le */
p_c_apdu->len = 28;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_write_ndef
**
** Description sends write ndef file cmd to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_write_ndef (void)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
BT_HDR *p_c_apdu;
UINT8 *p;
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_write_ndef (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_DES_WRITE);
UINT16_TO_BE_STREAM (p, 0x0000);
UINT8_TO_BE_STREAM (p, 0x09); /* Lc: length of wrapped data */
if (p_t4t->card_type == T4T_TYPE_DESFIRE_EV1)
{
UINT8_TO_BE_STREAM (p, 0x02); /* DESFEv1 Ndef file id */
}
else
{
UINT8_TO_BE_STREAM (p, 0x04);
}
UINT24_TO_BE_STREAM (p, 0x000000); /* Set the offset */
UINT24_TO_BE_STREAM (p, 0x020000); /* Set available length */
UINT16_TO_BE_STREAM (p, 0x0000); /* Ndef file bytes */
UINT8_TO_BE_STREAM (p, 0x00); /* Le */
p_c_apdu->len = 15;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_select_file
**
** Description Send Select Command (by File ID) to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_select_file (UINT16 file_id)
{
BT_HDR *p_c_apdu;
UINT8 *p;
RW_TRACE_DEBUG1 ("rw_t4t_select_file (): File ID:0x%04X", file_id);
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_select_file (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_INS_SELECT);
UINT8_TO_BE_STREAM (p, T4T_CMD_P1_SELECT_BY_FILE_ID);
/* if current version mapping is V2.0 */
if (rw_cb.tcb.t4t.version == T4T_VERSION_2_0)
{
UINT8_TO_BE_STREAM (p, T4T_CMD_P2_FIRST_OR_ONLY_0CH);
}
else /* version 1.0 */
{
UINT8_TO_BE_STREAM (p, T4T_CMD_P2_FIRST_OR_ONLY_00H);
}
UINT8_TO_BE_STREAM (p, T4T_FILE_ID_SIZE);
UINT16_TO_BE_STREAM (p, file_id);
p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + T4T_FILE_ID_SIZE;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_read_file
**
** Description Send ReadBinary Command to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_read_file (UINT16 offset, UINT16 length, BOOLEAN is_continue)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
BT_HDR *p_c_apdu;
UINT8 *p;
RW_TRACE_DEBUG3 ("rw_t4t_read_file () offset:%d, length:%d, is_continue:%d, ",
offset, length, is_continue);
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_read_file (): Cannot allocate buffer");
return FALSE;
}
/* if this is the first reading */
if (is_continue == FALSE)
{
/* initialise starting offset and total length */
/* these will be updated when receiving response */
p_t4t->rw_offset = offset;
p_t4t->rw_length = length;
}
/* adjust reading length if payload is bigger than max size per single command */
if (length > p_t4t->max_read_size)
{
length = (UINT8) (p_t4t->max_read_size);
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, (T4T_CMD_CLASS | rw_cb.tcb.t4t.channel));
UINT8_TO_BE_STREAM (p, T4T_CMD_INS_READ_BINARY);
UINT16_TO_BE_STREAM (p, offset);
UINT8_TO_BE_STREAM (p, length); /* Le */
p_c_apdu->len = T4T_CMD_MIN_HDR_SIZE + 1; /* adding Le */
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_update_nlen
**
** Description Send UpdateBinary Command to update NLEN to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_update_nlen (UINT16 ndef_len)
{
BT_HDR *p_c_apdu;
UINT8 *p;
RW_TRACE_DEBUG1 ("rw_t4t_update_nlen () NLEN:%d", ndef_len);
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_update_nlen (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_INS_UPDATE_BINARY);
UINT16_TO_BE_STREAM (p, 0x0000); /* offset for NLEN */
UINT8_TO_BE_STREAM (p, T4T_FILE_LENGTH_SIZE);
UINT16_TO_BE_STREAM (p, ndef_len);
p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + T4T_FILE_LENGTH_SIZE;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_update_file
**
** Description Send UpdateBinary Command to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_update_file (void)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
BT_HDR *p_c_apdu;
UINT8 *p;
UINT16 length;
RW_TRACE_DEBUG2 ("rw_t4t_update_file () rw_offset:%d, rw_length:%d",
p_t4t->rw_offset, p_t4t->rw_length);
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_write_file (): Cannot allocate buffer");
return FALSE;
}
/* try to send all of remaining data */
length = p_t4t->rw_length;
/* adjust updating length if payload is bigger than max size per single command */
if (length > p_t4t->max_update_size)
{
length = (UINT8) (p_t4t->max_update_size);
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_INS_UPDATE_BINARY);
UINT16_TO_BE_STREAM (p, p_t4t->rw_offset);
UINT8_TO_BE_STREAM (p, length);
memcpy (p, p_t4t->p_update_data, length);
p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + length;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
/* adjust offset, length and pointer for remaining data */
p_t4t->rw_offset += length;
p_t4t->rw_length -= length;
p_t4t->p_update_data += length;
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_update_cc_to_readonly
**
** Description Send UpdateBinary Command for changing Write access
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_update_cc_to_readonly (void)
{
BT_HDR *p_c_apdu;
UINT8 *p;
RW_TRACE_DEBUG0 ("rw_t4t_update_cc_to_readonly (): Remove Write access from CC");
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_update_cc_to_readonly (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
/* Add Command Header */
UINT8_TO_BE_STREAM (p, T4T_CMD_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_INS_UPDATE_BINARY);
UINT16_TO_BE_STREAM (p, (T4T_FC_TLV_OFFSET_IN_CC + T4T_FC_WRITE_ACCESS_OFFSET_IN_TLV)); /* Offset for Read Write access byte of CC */
UINT8_TO_BE_STREAM (p, 1); /* Length of write access field in cc interms of bytes */
/* Remove Write access */
UINT8_TO_BE_STREAM (p, T4T_FC_NO_WRITE_ACCESS);
p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + 1;
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_select_application
**
** Description Select Application
**
** NDEF Tag Application Select - C-APDU
**
** CLA INS P1 P2 Lc Data(AID) Le
** V1.0: 00 A4 04 00 07 D2760000850100 -
** V2.0: 00 A4 04 00 07 D2760000850101 00
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_select_application (UINT8 version)
{
BT_HDR *p_c_apdu;
UINT8 *p;
RW_TRACE_DEBUG1 ("rw_t4t_select_application () version:0x%X", version);
p_c_apdu = (BT_HDR *) GKI_getpoolbuf (NFC_RW_POOL_ID);
if (!p_c_apdu)
{
RW_TRACE_ERROR0 ("rw_t4t_select_application (): Cannot allocate buffer");
return FALSE;
}
p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (UINT8 *) (p_c_apdu + 1) + p_c_apdu->offset;
UINT8_TO_BE_STREAM (p, T4T_CMD_CLASS);
UINT8_TO_BE_STREAM (p, T4T_CMD_INS_SELECT);
UINT8_TO_BE_STREAM (p, T4T_CMD_P1_SELECT_BY_NAME);
UINT8_TO_BE_STREAM (p, T4T_CMD_P2_FIRST_OR_ONLY_00H);
if (version == T4T_VERSION_1_0) /* this is for V1.0 */
{
UINT8_TO_BE_STREAM (p, T4T_V10_NDEF_TAG_AID_LEN);
memcpy (p, t4t_v10_ndef_tag_aid, T4T_V10_NDEF_TAG_AID_LEN);
p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + T4T_V10_NDEF_TAG_AID_LEN;
}
else if (version == T4T_VERSION_2_0) /* this is for V2.0 */
{
UINT8_TO_BE_STREAM (p, T4T_V20_NDEF_TAG_AID_LEN);
memcpy (p, t4t_v20_ndef_tag_aid, T4T_V20_NDEF_TAG_AID_LEN);
p += T4T_V20_NDEF_TAG_AID_LEN;
UINT8_TO_BE_STREAM (p, 0x00); /* Le set to 0x00 */
p_c_apdu->len = T4T_CMD_MAX_HDR_SIZE + T4T_V20_NDEF_TAG_AID_LEN + 1;
}
else
{
return FALSE;
}
if (!rw_t4t_send_to_lower (p_c_apdu))
{
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_validate_cc_file
**
** Description Validate CC file and mandatory NDEF TLV
**
** Returns TRUE if success
**
*******************************************************************************/
static BOOLEAN rw_t4t_validate_cc_file (void)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
RW_TRACE_DEBUG0 ("rw_t4t_validate_cc_file ()");
if (p_t4t->cc_file.cclen < T4T_CC_FILE_MIN_LEN)
{
RW_TRACE_ERROR1 ("rw_t4t_validate_cc_file (): CCLEN (%d) is too short",
p_t4t->cc_file.cclen);
return FALSE;
}
if (T4T_GET_MAJOR_VERSION (p_t4t->cc_file.version) != T4T_GET_MAJOR_VERSION (p_t4t->version))
{
RW_TRACE_ERROR2 ("rw_t4t_validate_cc_file (): Peer version (0x%02X) is matched to ours (0x%02X)",
p_t4t->cc_file.version, p_t4t->version);
return FALSE;
}
if (p_t4t->cc_file.max_le < 0x000F)
{
RW_TRACE_ERROR1 ("rw_t4t_validate_cc_file (): MaxLe (%d) is too small",
p_t4t->cc_file.max_le);
return FALSE;
}
if (p_t4t->cc_file.max_lc < 0x0001)
{
RW_TRACE_ERROR1 ("rw_t4t_validate_cc_file (): MaxLc (%d) is too small",
p_t4t->cc_file.max_lc);
return FALSE;
}
if ( (p_t4t->cc_file.ndef_fc.file_id == T4T_CC_FILE_ID)
||(p_t4t->cc_file.ndef_fc.file_id == 0xE102)
||(p_t4t->cc_file.ndef_fc.file_id == 0xE103)
||((p_t4t->cc_file.ndef_fc.file_id == 0x0000) && (p_t4t->cc_file.version == 0x20))
||(p_t4t->cc_file.ndef_fc.file_id == 0x3F00)
||(p_t4t->cc_file.ndef_fc.file_id == 0x3FFF)
||(p_t4t->cc_file.ndef_fc.file_id == 0xFFFF) )
{
RW_TRACE_ERROR1 ("rw_t4t_validate_cc_file (): File ID (0x%04X) is invalid",
p_t4t->cc_file.ndef_fc.file_id);
return FALSE;
}
if ( (p_t4t->cc_file.ndef_fc.max_file_size < 0x0005)
||(p_t4t->cc_file.ndef_fc.max_file_size == 0xFFFF) )
{
RW_TRACE_ERROR1 ("rw_t4t_validate_cc_file (): max_file_size (%d) is reserved",
p_t4t->cc_file.ndef_fc.max_file_size);
return FALSE;
}
if (p_t4t->cc_file.ndef_fc.read_access != T4T_FC_READ_ACCESS)
{
RW_TRACE_ERROR1 ("rw_t4t_validate_cc_file (): Read Access (0x%02X) is invalid",
p_t4t->cc_file.ndef_fc.read_access);
return FALSE;
}
if ( (p_t4t->cc_file.ndef_fc.write_access != T4T_FC_WRITE_ACCESS)
&&(p_t4t->cc_file.ndef_fc.write_access != T4T_FC_NO_WRITE_ACCESS) )
{
RW_TRACE_ERROR1 ("rw_t4t_validate_cc_file (): Write Access (0x%02X) is invalid",
p_t4t->cc_file.ndef_fc.write_access);
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function rw_t4t_handle_error
**
** Description notify error to application and clean up
**
** Returns none
**
*******************************************************************************/
static void rw_t4t_handle_error (tNFC_STATUS status, UINT8 sw1, UINT8 sw2)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
tRW_DATA rw_data;
tRW_EVENT event;
RW_TRACE_DEBUG4 ("rw_t4t_handle_error (): status:0x%02X, sw1:0x%02X, sw2:0x%02X, state:0x%X",
status, sw1, sw2, p_t4t->state);
nfc_stop_quick_timer (&p_t4t->timer);
if (rw_cb.p_cback)
{
rw_data.status = status;
rw_data.t4t_sw.sw1 = sw1;
rw_data.t4t_sw.sw2 = sw2;
switch (p_t4t->state)
{
case RW_T4T_STATE_DETECT_NDEF:
rw_data.ndef.flags = RW_NDEF_FL_UNKNOWN;
event = RW_T4T_NDEF_DETECT_EVT;
break;
case RW_T4T_STATE_READ_NDEF:
event = RW_T4T_NDEF_READ_FAIL_EVT;
break;
case RW_T4T_STATE_UPDATE_NDEF:
event = RW_T4T_NDEF_UPDATE_FAIL_EVT;
break;
case RW_T4T_STATE_PRESENCE_CHECK:
event = RW_T4T_PRESENCE_CHECK_EVT;
rw_data.status = NFC_STATUS_FAILED;
break;
case RW_T4T_STATE_SET_READ_ONLY:
event = RW_T4T_SET_TO_RO_EVT;
break;
case RW_T4T_STATE_NDEF_FORMAT:
event = RW_T4T_NDEF_FORMAT_CPLT_EVT;
rw_data.status = NFC_STATUS_FAILED;
break;
default:
event = RW_T4T_MAX_EVT;
break;
}
p_t4t->state = RW_T4T_STATE_IDLE;
if (event != RW_T4T_MAX_EVT)
{
(*(rw_cb.p_cback)) (event, &rw_data);
}
}
else
{
p_t4t->state = RW_T4T_STATE_IDLE;
}
}
/*******************************************************************************
**
** Function rw_t4t_sm_ndef_format
**
** Description State machine for NDEF format procedure
**
** Returns none
**
*******************************************************************************/
static void rw_t4t_sm_ndef_format (BT_HDR *p_r_apdu)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
UINT8 *p, type, length;
UINT16 status_words, nlen;
tRW_DATA rw_data;
#if (BT_TRACE_VERBOSE == TRUE)
RW_TRACE_DEBUG2 ("rw_t4t_sm_ndef_format (): sub_state:%s (%d)",
rw_t4t_get_sub_state_name (p_t4t->sub_state), p_t4t->sub_state);
#else
RW_TRACE_DEBUG1 ("rw_t4t_sm_ndef_format (): sub_state=%d", p_t4t->sub_state);
#endif
/* get status words */
p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;
switch (p_t4t->sub_state)
{
case RW_T4T_SUBSTATE_WAIT_GET_HW_VERSION:
p += (p_r_apdu->len - 1);
if (*(p) == T4T_ADDI_FRAME_RESP)
{
if (!rw_t4t_get_sw_version ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_GET_SW_VERSION;
}
}
else
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
break;
case RW_T4T_SUBSTATE_WAIT_GET_SW_VERSION:
p += (p_r_apdu->len - 1);
if (*(p) == T4T_ADDI_FRAME_RESP)
{
if (!rw_t4t_update_version_details (p_r_apdu))
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
if (!rw_t4t_get_uid_details ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_GET_UID;
}
else
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
break;
case RW_T4T_SUBSTATE_WAIT_GET_UID:
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words != 0x9100)
{
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p - 2), *(p - 1));
}
else
{
if (!rw_t4t_create_app ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_CREATE_APP;
}
}
break;
case RW_T4T_SUBSTATE_WAIT_CREATE_APP:
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words == 0x91DE) /* DUPLICATE_ERROR, file already exist*/
{
status_words = 0x9100;
}
if (status_words != 0x9100)
{
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p - 2), *(p - 1));
}
else
{
if (!rw_t4t_select_app ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_APP;
}
}
break;
case RW_T4T_SUBSTATE_WAIT_SELECT_APP:
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words != 0x9100)
{
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p - 2), *(p - 1));
}
else
{
if (!rw_t4t_create_ccfile ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_CREATE_CC;
}
}
break;
case RW_T4T_SUBSTATE_WAIT_CREATE_CC:
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words == 0x91DE) /* DUPLICATE_ERROR, file already exist*/
{
status_words = 0x9100;
}
if (status_words != 0x9100)
{
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p - 2), *(p - 1));
}
else
{
if (!rw_t4t_create_ndef ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_CREATE_NDEF;
}
}
break;
case RW_T4T_SUBSTATE_WAIT_CREATE_NDEF:
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words == 0x91DE) /* DUPLICATE_ERROR, file already exist*/
{
status_words = 0x9100;
}
if (status_words != 0x9100)
{
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p - 2), *(p - 1));
}
else
{
if (!rw_t4t_write_cc ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_WRITE_CC;
}
}
break;
case RW_T4T_SUBSTATE_WAIT_WRITE_CC:
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words != 0x9100)
{
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p - 2), *(p - 1));
}
else
{
if (!rw_t4t_write_ndef ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_WRITE_NDEF;
}
}
break;
case RW_T4T_SUBSTATE_WAIT_WRITE_NDEF:
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words != 0x9100)
{
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p - 2), *(p - 1));
}
else
{
p_t4t->state = RW_T4T_STATE_IDLE;
if (rw_cb.p_cback)
{
rw_data.ndef.status = NFC_STATUS_OK;
rw_data.ndef.protocol = NFC_PROTOCOL_ISO_DEP;
rw_data.ndef.max_size = p_t4t->card_size;
rw_data.ndef.cur_size = 0x00;
(*(rw_cb.p_cback)) (RW_T4T_NDEF_FORMAT_CPLT_EVT, &rw_data);
RW_TRACE_DEBUG0 ("rw_t4t_ndef_format (): Sent RW_T4T_NDEF_FORMAT_CPLT_EVT");
}
}
break;
default:
RW_TRACE_ERROR1 ("rw_t4t_sm_ndef_format (): unknown sub_state=%d", p_t4t->sub_state);
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
break;
}
}
/*******************************************************************************
**
** Function rw_t4t_sm_detect_ndef
**
** Description State machine for NDEF detection procedure
**
** Returns none
**
*******************************************************************************/
static void rw_t4t_sm_detect_ndef (BT_HDR *p_r_apdu)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
UINT8 *p, type, length;
UINT16 status_words, nlen;
tRW_DATA rw_data;
#if (BT_TRACE_VERBOSE == TRUE)
RW_TRACE_DEBUG2 ("rw_t4t_sm_detect_ndef (): sub_state:%s (%d)",
rw_t4t_get_sub_state_name (p_t4t->sub_state), p_t4t->sub_state);
#else
RW_TRACE_DEBUG1 ("rw_t4t_sm_detect_ndef (): sub_state=%d", p_t4t->sub_state);
#endif
/* get status words */
p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words != T4T_RSP_CMD_CMPLTED)
{
/* try V1.0 after failing of V2.0 */
if ( (p_t4t->sub_state == RW_T4T_SUBSTATE_WAIT_SELECT_APP)
&&(p_t4t->version == T4T_VERSION_2_0) )
{
p_t4t->version = T4T_VERSION_1_0;
RW_TRACE_DEBUG1 ("rw_t4t_sm_detect_ndef (): retry with version=0x%02X",
p_t4t->version);
if (!rw_t4t_select_application (T4T_VERSION_1_0))
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
return;
}
p_t4t->ndef_status &= ~ (RW_T4T_NDEF_STATUS_NDEF_DETECTED);
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p-2), *(p-1));
return;
}
switch (p_t4t->sub_state)
{
case RW_T4T_SUBSTATE_WAIT_SELECT_APP:
/* NDEF Tag application has been selected then select CC file */
if (!rw_t4t_select_file (T4T_CC_FILE_ID))
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_CC;
}
break;
case RW_T4T_SUBSTATE_WAIT_SELECT_CC:
/* CC file has been selected then read mandatory part of CC file */
if (!rw_t4t_read_file (0x00, T4T_CC_FILE_MIN_LEN, FALSE))
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_CC_FILE;
}
break;
case RW_T4T_SUBSTATE_WAIT_CC_FILE:
/* CC file has been read then validate and select mandatory NDEF file */
if (p_r_apdu->len >= T4T_CC_FILE_MIN_LEN + T4T_RSP_STATUS_WORDS_SIZE)
{
p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;
BE_STREAM_TO_UINT16 (p_t4t->cc_file.cclen, p);
BE_STREAM_TO_UINT8 (p_t4t->cc_file.version, p);
BE_STREAM_TO_UINT16 (p_t4t->cc_file.max_le, p);
BE_STREAM_TO_UINT16 (p_t4t->cc_file.max_lc, p);
BE_STREAM_TO_UINT8 (type, p);
BE_STREAM_TO_UINT8 (length, p);
if ( (type == T4T_NDEF_FILE_CONTROL_TYPE)
&&(length == T4T_FILE_CONTROL_LENGTH) )
{
BE_STREAM_TO_UINT16 (p_t4t->cc_file.ndef_fc.file_id, p);
BE_STREAM_TO_UINT16 (p_t4t->cc_file.ndef_fc.max_file_size, p);
BE_STREAM_TO_UINT8 (p_t4t->cc_file.ndef_fc.read_access, p);
BE_STREAM_TO_UINT8 (p_t4t->cc_file.ndef_fc.write_access, p);
#if (BT_TRACE_VERBOSE == TRUE)
RW_TRACE_DEBUG0 ("Capability Container (CC) file");
RW_TRACE_DEBUG1 (" CCLEN: 0x%04X", p_t4t->cc_file.cclen);
RW_TRACE_DEBUG1 (" Version:0x%02X", p_t4t->cc_file.version);
RW_TRACE_DEBUG1 (" MaxLe: 0x%04X", p_t4t->cc_file.max_le);
RW_TRACE_DEBUG1 (" MaxLc: 0x%04X", p_t4t->cc_file.max_lc);
RW_TRACE_DEBUG0 (" NDEF File Control TLV");
RW_TRACE_DEBUG1 (" FileID: 0x%04X", p_t4t->cc_file.ndef_fc.file_id);
RW_TRACE_DEBUG1 (" MaxFileSize: 0x%04X", p_t4t->cc_file.ndef_fc.max_file_size);
RW_TRACE_DEBUG1 (" ReadAccess: 0x%02X", p_t4t->cc_file.ndef_fc.read_access);
RW_TRACE_DEBUG1 (" WriteAccess: 0x%02X", p_t4t->cc_file.ndef_fc.write_access);
#endif
if (rw_t4t_validate_cc_file ())
{
if (!rw_t4t_select_file (p_t4t->cc_file.ndef_fc.file_id))
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE;
}
break;
}
}
}
/* invalid response or CC file */
p_t4t->ndef_status &= ~ (RW_T4T_NDEF_STATUS_NDEF_DETECTED);
rw_t4t_handle_error (NFC_STATUS_BAD_RESP, 0, 0);
break;
case RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE:
/* NDEF file has been selected then read the first 2 bytes (NLEN) */
if (!rw_t4t_read_file (0, T4T_FILE_LENGTH_SIZE, FALSE))
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_READ_NLEN;
}
break;
case RW_T4T_SUBSTATE_WAIT_READ_NLEN:
/* NLEN has been read then report upper layer */
if (p_r_apdu->len == T4T_FILE_LENGTH_SIZE + T4T_RSP_STATUS_WORDS_SIZE)
{
/* get length of NDEF */
p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;
BE_STREAM_TO_UINT16 (nlen, p);
if (nlen <= p_t4t->cc_file.ndef_fc.max_file_size - T4T_FILE_LENGTH_SIZE)
{
p_t4t->ndef_status = RW_T4T_NDEF_STATUS_NDEF_DETECTED;
if (p_t4t->cc_file.ndef_fc.write_access != T4T_FC_WRITE_ACCESS)
{
p_t4t->ndef_status |= RW_T4T_NDEF_STATUS_NDEF_READ_ONLY;
}
/* Get max bytes to read per command */
if (p_t4t->cc_file.max_le >= RW_T4T_MAX_DATA_PER_READ)
{
p_t4t->max_read_size = RW_T4T_MAX_DATA_PER_READ;
}
else
{
p_t4t->max_read_size = p_t4t->cc_file.max_le;
}
/* Le: valid range is 0x01 to 0xFF */
if (p_t4t->max_read_size >= T4T_MAX_LENGTH_LE)
{
p_t4t->max_read_size = T4T_MAX_LENGTH_LE;
}
/* Get max bytes to update per command */
if (p_t4t->cc_file.max_lc >= RW_T4T_MAX_DATA_PER_WRITE)
{
p_t4t->max_update_size = RW_T4T_MAX_DATA_PER_WRITE;
}
else
{
p_t4t->max_update_size = p_t4t->cc_file.max_lc;
}
/* Lc: valid range is 0x01 to 0xFF */
if (p_t4t->max_update_size >= T4T_MAX_LENGTH_LC)
{
p_t4t->max_update_size = T4T_MAX_LENGTH_LC;
}
p_t4t->ndef_length = nlen;
p_t4t->state = RW_T4T_STATE_IDLE;
if (rw_cb.p_cback)
{
rw_data.ndef.status = NFC_STATUS_OK;
rw_data.ndef.protocol = NFC_PROTOCOL_ISO_DEP;
rw_data.ndef.max_size = (UINT32) (p_t4t->cc_file.ndef_fc.max_file_size - (UINT16) T4T_FILE_LENGTH_SIZE);
rw_data.ndef.cur_size = nlen;
rw_data.ndef.flags = RW_NDEF_FL_SUPPORTED | RW_NDEF_FL_FORMATED;
if (p_t4t->cc_file.ndef_fc.write_access != T4T_FC_WRITE_ACCESS)
{
rw_data.ndef.flags |= RW_NDEF_FL_READ_ONLY;
}
(*(rw_cb.p_cback)) (RW_T4T_NDEF_DETECT_EVT, &rw_data);
RW_TRACE_DEBUG0 ("rw_t4t_sm_detect_ndef (): Sent RW_T4T_NDEF_DETECT_EVT");
}
}
else
{
/* NLEN should be less than max file size */
RW_TRACE_ERROR2 ("rw_t4t_sm_detect_ndef (): NLEN (%d) + 2 must be <= max file size (%d)",
nlen, p_t4t->cc_file.ndef_fc.max_file_size);
p_t4t->ndef_status &= ~ (RW_T4T_NDEF_STATUS_NDEF_DETECTED);
rw_t4t_handle_error (NFC_STATUS_BAD_RESP, 0, 0);
}
}
else
{
/* response payload size should be T4T_FILE_LENGTH_SIZE */
RW_TRACE_ERROR2 ("rw_t4t_sm_detect_ndef (): Length (%d) of R-APDU must be %d",
p_r_apdu->len, T4T_FILE_LENGTH_SIZE + T4T_RSP_STATUS_WORDS_SIZE);
p_t4t->ndef_status &= ~ (RW_T4T_NDEF_STATUS_NDEF_DETECTED);
rw_t4t_handle_error (NFC_STATUS_BAD_RESP, 0, 0);
}
break;
default:
RW_TRACE_ERROR1 ("rw_t4t_sm_detect_ndef (): unknown sub_state=%d", p_t4t->sub_state);
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
break;
}
}
/*******************************************************************************
**
** Function rw_t4t_sm_read_ndef
**
** Description State machine for NDEF read procedure
**
** Returns none
**
*******************************************************************************/
static void rw_t4t_sm_read_ndef (BT_HDR *p_r_apdu)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
UINT8 *p;
UINT16 status_words;
tRW_DATA rw_data;
#if (BT_TRACE_VERBOSE == TRUE)
RW_TRACE_DEBUG2 ("rw_t4t_sm_read_ndef (): sub_state:%s (%d)",
rw_t4t_get_sub_state_name (p_t4t->sub_state), p_t4t->sub_state);
#else
RW_TRACE_DEBUG1 ("rw_t4t_sm_read_ndef (): sub_state=%d", p_t4t->sub_state);
#endif
/* get status words */
p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words != T4T_RSP_CMD_CMPLTED)
{
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p-2), *(p-1));
GKI_freebuf (p_r_apdu);
return;
}
switch (p_t4t->sub_state)
{
case RW_T4T_SUBSTATE_WAIT_READ_RESP:
/* Read partial or complete data */
p_r_apdu->len -= T4T_RSP_STATUS_WORDS_SIZE;
if ((p_r_apdu->len > 0) && (p_r_apdu->len <= p_t4t->rw_length))
{
p_t4t->rw_length -= p_r_apdu->len;
p_t4t->rw_offset += p_r_apdu->len;
if (rw_cb.p_cback)
{
rw_data.data.status = NFC_STATUS_OK;
rw_data.data.p_data = p_r_apdu;
/* if need to read more data */
if (p_t4t->rw_length > 0)
{
(*(rw_cb.p_cback)) (RW_T4T_NDEF_READ_EVT, &rw_data);
if (!rw_t4t_read_file (p_t4t->rw_offset, p_t4t->rw_length, TRUE))
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
}
else
{
p_t4t->state = RW_T4T_STATE_IDLE;
(*(rw_cb.p_cback)) (RW_T4T_NDEF_READ_CPLT_EVT, &rw_data);
RW_TRACE_DEBUG0 ("rw_t4t_sm_read_ndef (): Sent RW_T4T_NDEF_READ_CPLT_EVT");
}
p_r_apdu = NULL;
}
else
{
p_t4t->rw_length = 0;
p_t4t->state = RW_T4T_STATE_IDLE;
}
}
else
{
RW_TRACE_ERROR2 ("rw_t4t_sm_read_ndef (): invalid payload length (%d), rw_length (%d)",
p_r_apdu->len, p_t4t->rw_length);
rw_t4t_handle_error (NFC_STATUS_BAD_RESP, 0, 0);
}
break;
default:
RW_TRACE_ERROR1 ("rw_t4t_sm_read_ndef (): unknown sub_state = %d", p_t4t->sub_state);
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
break;
}
if (p_r_apdu)
GKI_freebuf (p_r_apdu);
}
/*******************************************************************************
**
** Function rw_t4t_sm_update_ndef
**
** Description State machine for NDEF update procedure
**
** Returns none
**
*******************************************************************************/
static void rw_t4t_sm_update_ndef (BT_HDR *p_r_apdu)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
UINT8 *p;
UINT16 status_words;
tRW_DATA rw_data;
#if (BT_TRACE_VERBOSE == TRUE)
RW_TRACE_DEBUG2 ("rw_t4t_sm_update_ndef (): sub_state:%s (%d)",
rw_t4t_get_sub_state_name (p_t4t->sub_state), p_t4t->sub_state);
#else
RW_TRACE_DEBUG1 ("rw_t4t_sm_update_ndef (): sub_state=%d", p_t4t->sub_state);
#endif
/* Get status words */
p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words != T4T_RSP_CMD_CMPLTED)
{
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p-2), *(p-1));
return;
}
switch (p_t4t->sub_state)
{
case RW_T4T_SUBSTATE_WAIT_UPDATE_NLEN:
/* NLEN has been updated */
/* if need to update data */
if (p_t4t->p_update_data)
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_UPDATE_RESP;
if (!rw_t4t_update_file ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
p_t4t->p_update_data = NULL;
}
}
else
{
p_t4t->state = RW_T4T_STATE_IDLE;
/* just finished last step of updating (updating NLEN) */
if (rw_cb.p_cback)
{
rw_data.status = NFC_STATUS_OK;
(*(rw_cb.p_cback)) (RW_T4T_NDEF_UPDATE_CPLT_EVT, &rw_data);
RW_TRACE_DEBUG0 ("rw_t4t_sm_update_ndef (): Sent RW_T4T_NDEF_UPDATE_CPLT_EVT");
}
}
break;
case RW_T4T_SUBSTATE_WAIT_UPDATE_RESP:
/* if updating is not completed */
if (p_t4t->rw_length > 0)
{
if (!rw_t4t_update_file ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
p_t4t->p_update_data = NULL;
}
}
else
{
p_t4t->p_update_data = NULL;
/* update NLEN as last step of updating file */
if (!rw_t4t_update_nlen (p_t4t->ndef_length))
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_UPDATE_NLEN;
}
}
break;
default:
RW_TRACE_ERROR1 ("rw_t4t_sm_update_ndef (): unknown sub_state = %d", p_t4t->sub_state);
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
break;
}
}
/*******************************************************************************
**
** Function rw_t4t_sm_set_readonly
**
** Description State machine for CC update procedure
**
** Returns none
**
*******************************************************************************/
static void rw_t4t_sm_set_readonly (BT_HDR *p_r_apdu)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
UINT8 *p;
UINT16 status_words;
tRW_DATA rw_data;
#if (BT_TRACE_VERBOSE == TRUE)
RW_TRACE_DEBUG2 ("rw_t4t_sm_set_readonly (): sub_state:%s (%d)",
rw_t4t_get_sub_state_name (p_t4t->sub_state), p_t4t->sub_state);
#else
RW_TRACE_DEBUG1 ("rw_t4t_sm_set_readonly (): sub_state=%d", p_t4t->sub_state);
#endif
/* Get status words */
p = (UINT8 *) (p_r_apdu + 1) + p_r_apdu->offset;
p += (p_r_apdu->len - T4T_RSP_STATUS_WORDS_SIZE);
BE_STREAM_TO_UINT16 (status_words, p);
if (status_words != T4T_RSP_CMD_CMPLTED)
{
rw_t4t_handle_error (NFC_STATUS_CMD_NOT_CMPLTD, *(p-2), *(p-1));
return;
}
switch (p_t4t->sub_state)
{
case RW_T4T_SUBSTATE_WAIT_SELECT_CC:
/* CC file has been selected then update write access to read-only in CC file */
if (!rw_t4t_update_cc_to_readonly ())
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_UPDATE_CC;
}
break;
case RW_T4T_SUBSTATE_WAIT_UPDATE_CC:
/* CC Updated, Select NDEF File to allow NDEF operation */
p_t4t->cc_file.ndef_fc.write_access = T4T_FC_NO_WRITE_ACCESS;
p_t4t->ndef_status |= RW_T4T_NDEF_STATUS_NDEF_READ_ONLY;
if (!rw_t4t_select_file (p_t4t->cc_file.ndef_fc.file_id))
{
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
}
else
{
p_t4t->sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE;
}
break;
case RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE:
p_t4t->state = RW_T4T_STATE_IDLE;
/* just finished last step of configuring tag read only (Selecting NDEF file CC) */
if (rw_cb.p_cback)
{
rw_data.status = NFC_STATUS_OK;
RW_TRACE_DEBUG0 ("rw_t4t_sm_set_readonly (): Sent RW_T4T_SET_TO_RO_EVT");
(*(rw_cb.p_cback)) (RW_T4T_SET_TO_RO_EVT, &rw_data);
}
break;
default:
RW_TRACE_ERROR1 ("rw_t4t_sm_set_readonly (): unknown sub_state = %d", p_t4t->sub_state);
rw_t4t_handle_error (NFC_STATUS_FAILED, 0, 0);
break;
}
}
/*******************************************************************************
**
** Function rw_t4t_process_timeout
**
** Description process timeout event
**
** Returns none
**
*******************************************************************************/
void rw_t4t_process_timeout (TIMER_LIST_ENT *p_tle)
{
RW_TRACE_DEBUG1 ("rw_t4t_process_timeout () event=%d", p_tle->event);
if (p_tle->event == NFC_TTYPE_RW_T4T_RESPONSE)
{
rw_t4t_handle_error (NFC_STATUS_TIMEOUT, 0, 0);
}
else
{
RW_TRACE_ERROR1 ("rw_t4t_process_timeout () unknown event=%d", p_tle->event);
}
}
/*******************************************************************************
**
** Function rw_t4t_data_cback
**
** Description This callback function receives the data from NFCC.
**
** Returns none
**
*******************************************************************************/
static void rw_t4t_data_cback (UINT8 conn_id, tNFC_CONN_EVT event, tNFC_CONN *p_data)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
BT_HDR *p_r_apdu;
tRW_DATA rw_data;
#if (BT_TRACE_VERBOSE == TRUE)
UINT8 begin_state = p_t4t->state;
#endif
RW_TRACE_DEBUG1 ("rw_t4t_data_cback () event = 0x%X", event);
nfc_stop_quick_timer (&p_t4t->timer);
switch (event)
{
case NFC_DEACTIVATE_CEVT:
NFC_SetStaticRfCback (NULL);
p_t4t->state = RW_T4T_STATE_NOT_ACTIVATED;
return;
case NFC_ERROR_CEVT:
if (p_t4t->state == RW_T4T_STATE_PRESENCE_CHECK)
{
p_t4t->state = RW_T4T_STATE_IDLE;
rw_data.status = NFC_STATUS_FAILED;
(*(rw_cb.p_cback)) (RW_T4T_PRESENCE_CHECK_EVT, &rw_data);
}
else if (p_t4t->state == RW_T4T_STATE_NDEF_FORMAT)
{
p_t4t->state = RW_T4T_STATE_IDLE;
rw_data.status = NFC_STATUS_FAILED;
(*(rw_cb.p_cback)) (RW_T4T_NDEF_FORMAT_CPLT_EVT, &rw_data);
}
else if (p_t4t->state != RW_T4T_STATE_IDLE)
{
rw_t4t_handle_error (rw_data.status, 0, 0);
}
else
{
p_t4t->state = RW_T4T_STATE_IDLE;
rw_data.status = (tNFC_STATUS) (*(UINT8*) p_data);
(*(rw_cb.p_cback)) (RW_T4T_INTF_ERROR_EVT, &rw_data);
}
return;
case NFC_DATA_CEVT:
p_r_apdu = (BT_HDR *) p_data->data.p_data;
break;
default:
return;
}
#if (BT_TRACE_PROTOCOL == TRUE)
if (p_t4t->state != RW_T4T_STATE_IDLE)
DispRWT4Tags (p_r_apdu, TRUE);
#endif
#if (BT_TRACE_VERBOSE == TRUE)
RW_TRACE_DEBUG2 ("RW T4T state: <%s (%d)>",
rw_t4t_get_state_name (p_t4t->state), p_t4t->state);
#else
RW_TRACE_DEBUG1 ("RW T4T state: %d", p_t4t->state);
#endif
switch (p_t4t->state)
{
case RW_T4T_STATE_IDLE:
/* Unexpected R-APDU, it should be raw frame response */
/* forward to upper layer without parsing */
#if (BT_TRACE_VERBOSE == TRUE)
RW_TRACE_DEBUG2 ("RW T4T Raw Frame: Len [0x%X] Status [%s]", p_r_apdu->len, NFC_GetStatusName (p_data->data.status));
#else
RW_TRACE_DEBUG2 ("RW T4T Raw Frame: Len [0x%X] Status [0x%X]", p_r_apdu->len, p_data->data.status);
#endif
if (rw_cb.p_cback)
{
rw_data.raw_frame.status = p_data->data.status;
rw_data.raw_frame.p_data = p_r_apdu;
(*(rw_cb.p_cback)) (RW_T4T_RAW_FRAME_EVT, &rw_data);
p_r_apdu = NULL;
}
else
{
GKI_freebuf (p_r_apdu);
}
break;
case RW_T4T_STATE_DETECT_NDEF:
rw_t4t_sm_detect_ndef (p_r_apdu);
GKI_freebuf (p_r_apdu);
break;
case RW_T4T_STATE_READ_NDEF:
rw_t4t_sm_read_ndef (p_r_apdu);
/* p_r_apdu may send upper lyaer */
break;
case RW_T4T_STATE_UPDATE_NDEF:
rw_t4t_sm_update_ndef (p_r_apdu);
GKI_freebuf (p_r_apdu);
break;
case RW_T4T_STATE_PRESENCE_CHECK:
/* if any response, send presence check with ok */
rw_data.status = NFC_STATUS_OK;
p_t4t->state = RW_T4T_STATE_IDLE;
(*(rw_cb.p_cback)) (RW_T4T_PRESENCE_CHECK_EVT, &rw_data);
GKI_freebuf (p_r_apdu);
break;
case RW_T4T_STATE_SET_READ_ONLY:
rw_t4t_sm_set_readonly (p_r_apdu);
GKI_freebuf (p_r_apdu);
break;
case RW_T4T_STATE_NDEF_FORMAT:
rw_t4t_sm_ndef_format (p_r_apdu);
GKI_freebuf (p_r_apdu);
break;
default:
RW_TRACE_ERROR1 ("rw_t4t_data_cback (): invalid state=%d", p_t4t->state);
GKI_freebuf (p_r_apdu);
break;
}
#if (BT_TRACE_VERBOSE == TRUE)
if (begin_state != p_t4t->state)
{
RW_TRACE_DEBUG2 ("RW T4T state changed:<%s> -> <%s>",
rw_t4t_get_state_name (begin_state),
rw_t4t_get_state_name (p_t4t->state));
}
#endif
}
/*******************************************************************************
**
** Function RW_T4tFormatNDef
**
** Description format T4T tag
**
** Returns NFC_STATUS_OK if success
**
*******************************************************************************/
tNFC_STATUS RW_T4tFormatNDef (void)
{
RW_TRACE_API0 ("RW_T4tFormatNDef ()");
if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE)
{
RW_TRACE_ERROR1 ("RW_T4tFormatNDef ():Unable to start command at state (0x%X)",
rw_cb.tcb.t4t.state);
return NFC_STATUS_FAILED;
}
rw_cb.tcb.t4t.card_type = 0x00;
if (!rw_t4t_get_hw_version ())
{
return NFC_STATUS_FAILED;
}
rw_cb.tcb.t4t.state = RW_T4T_STATE_NDEF_FORMAT;
rw_cb.tcb.t4t.sub_state = RW_T4T_SUBSTATE_WAIT_GET_HW_VERSION;
return NFC_STATUS_OK;
}
/*******************************************************************************
**
** Function rw_t4t_select
**
** Description Initialise T4T
**
** Returns NFC_STATUS_OK if success
**
*******************************************************************************/
tNFC_STATUS rw_t4t_select (void)
{
tRW_T4T_CB *p_t4t = &rw_cb.tcb.t4t;
RW_TRACE_DEBUG0 ("rw_t4t_select ()");
NFC_SetStaticRfCback (rw_t4t_data_cback);
p_t4t->state = RW_T4T_STATE_IDLE;
p_t4t->version = T4T_MY_VERSION;
/* set it min of max R-APDU data size before reading CC file */
p_t4t->cc_file.max_le = T4T_MIN_MLE;
/* These will be udated during NDEF detection */
p_t4t->max_read_size = T4T_MAX_LENGTH_LE;
p_t4t->max_update_size = T4T_MAX_LENGTH_LC;
return NFC_STATUS_OK;
}
/*******************************************************************************
**
** Function RW_T4tDetectNDef
**
** Description This function performs NDEF detection procedure
**
** RW_T4T_NDEF_DETECT_EVT will be returned
**
** Returns NFC_STATUS_OK if success
** NFC_STATUS_FAILED if T4T is busy or other error
**
*******************************************************************************/
tNFC_STATUS RW_T4tDetectNDef (void)
{
RW_TRACE_API0 ("RW_T4tDetectNDef ()");
if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE)
{
RW_TRACE_ERROR1 ("RW_T4tDetectNDef ():Unable to start command at state (0x%X)",
rw_cb.tcb.t4t.state);
return NFC_STATUS_FAILED;
}
if (rw_cb.tcb.t4t.ndef_status & RW_T4T_NDEF_STATUS_NDEF_DETECTED)
{
/* NDEF Tag application has been selected then select CC file */
if (!rw_t4t_select_file (T4T_CC_FILE_ID))
{
return NFC_STATUS_FAILED;
}
rw_cb.tcb.t4t.sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_CC;
}
else
{
/* Select NDEF Tag Application */
if (!rw_t4t_select_application (rw_cb.tcb.t4t.version))
{
return NFC_STATUS_FAILED;
}
rw_cb.tcb.t4t.sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_APP;
}
rw_cb.tcb.t4t.state = RW_T4T_STATE_DETECT_NDEF;
return NFC_STATUS_OK;
}
/*******************************************************************************
**
** Function RW_T4tReadNDef
**
** Description This function performs NDEF read procedure
** Note: RW_T4tDetectNDef () must be called before using this
**
** The following event will be returned
** RW_T4T_NDEF_READ_EVT for each segmented NDEF message
** RW_T4T_NDEF_READ_CPLT_EVT for the last segment or complete NDEF
** RW_T4T_NDEF_READ_FAIL_EVT for failure
**
** Returns NFC_STATUS_OK if success
** NFC_STATUS_FAILED if T4T is busy or other error
**
*******************************************************************************/
tNFC_STATUS RW_T4tReadNDef (void)
{
RW_TRACE_API0 ("RW_T4tReadNDef ()");
if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE)
{
RW_TRACE_ERROR1 ("RW_T4tReadNDef ():Unable to start command at state (0x%X)",
rw_cb.tcb.t4t.state);
return NFC_STATUS_FAILED;
}
/* if NDEF has been detected */
if (rw_cb.tcb.t4t.ndef_status & RW_T4T_NDEF_STATUS_NDEF_DETECTED)
{
/* start reading NDEF */
if (!rw_t4t_read_file (T4T_FILE_LENGTH_SIZE, rw_cb.tcb.t4t.ndef_length, FALSE))
{
return NFC_STATUS_FAILED;
}
rw_cb.tcb.t4t.state = RW_T4T_STATE_READ_NDEF;
rw_cb.tcb.t4t.sub_state = RW_T4T_SUBSTATE_WAIT_READ_RESP;
return NFC_STATUS_OK;
}
else
{
RW_TRACE_ERROR0 ("RW_T4tReadNDef ():No NDEF detected");
return NFC_STATUS_FAILED;
}
}
/*******************************************************************************
**
** Function RW_T4tUpdateNDef
**
** Description This function performs NDEF update procedure
** Note: RW_T4tDetectNDef () must be called before using this
** Updating data must not be removed until returning event
**
** The following event will be returned
** RW_T4T_NDEF_UPDATE_CPLT_EVT for complete
** RW_T4T_NDEF_UPDATE_FAIL_EVT for failure
**
** Returns NFC_STATUS_OK if success
** NFC_STATUS_FAILED if T4T is busy or other error
**
*******************************************************************************/
tNFC_STATUS RW_T4tUpdateNDef (UINT16 length, UINT8 *p_data)
{
RW_TRACE_API1 ("RW_T4tUpdateNDef () length:%d", length);
if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE)
{
RW_TRACE_ERROR1 ("RW_T4tUpdateNDef ():Unable to start command at state (0x%X)",
rw_cb.tcb.t4t.state);
return NFC_STATUS_FAILED;
}
/* if NDEF has been detected */
if (rw_cb.tcb.t4t.ndef_status & RW_T4T_NDEF_STATUS_NDEF_DETECTED)
{
/* if read-only */
if (rw_cb.tcb.t4t.ndef_status & RW_T4T_NDEF_STATUS_NDEF_READ_ONLY)
{
RW_TRACE_ERROR0 ("RW_T4tUpdateNDef ():NDEF is read-only");
return NFC_STATUS_FAILED;
}
if (rw_cb.tcb.t4t.cc_file.ndef_fc.max_file_size < length + T4T_FILE_LENGTH_SIZE)
{
RW_TRACE_ERROR2 ("RW_T4tUpdateNDef ():data (%d bytes) plus NLEN is more than max file size (%d)",
length, rw_cb.tcb.t4t.cc_file.ndef_fc.max_file_size);
return NFC_STATUS_FAILED;
}
/* store NDEF length and data */
rw_cb.tcb.t4t.ndef_length = length;
rw_cb.tcb.t4t.p_update_data = p_data;
rw_cb.tcb.t4t.rw_offset = T4T_FILE_LENGTH_SIZE;
rw_cb.tcb.t4t.rw_length = length;
/* set NLEN to 0x0000 for the first step */
if (!rw_t4t_update_nlen (0x0000))
{
return NFC_STATUS_FAILED;
}
rw_cb.tcb.t4t.state = RW_T4T_STATE_UPDATE_NDEF;
rw_cb.tcb.t4t.sub_state = RW_T4T_SUBSTATE_WAIT_UPDATE_NLEN;
return NFC_STATUS_OK;
}
else
{
RW_TRACE_ERROR0 ("RW_T4tUpdateNDef ():No NDEF detected");
return NFC_STATUS_FAILED;
}
}
/*****************************************************************************
**
** Function RW_T4tPresenceCheck
**
** Description
** Check if the tag is still in the field.
**
** The RW_T4T_PRESENCE_CHECK_EVT w/ status is used to indicate presence
** or non-presence.
** option is RW_T4T_CHK_EMPTY_I_BLOCK, use empty I block for presence check.
**
** Returns
** NFC_STATUS_OK, if raw data frame sent
** NFC_STATUS_NO_BUFFERS: unable to allocate a buffer for this operation
** NFC_STATUS_FAILED: other error
**
*****************************************************************************/
tNFC_STATUS RW_T4tPresenceCheck (UINT8 option)
{
tNFC_STATUS retval = NFC_STATUS_OK;
tRW_DATA evt_data;
BOOLEAN status;
BT_HDR *p_data;
RW_TRACE_API1 ("RW_T4tPresenceCheck () %d", option);
/* If RW_SelectTagType was not called (no conn_callback) return failure */
if (!rw_cb.p_cback)
{
retval = NFC_STATUS_FAILED;
}
/* If we are not activated, then RW_T4T_PRESENCE_CHECK_EVT with NFC_STATUS_FAILED */
else if (rw_cb.tcb.t4t.state == RW_T4T_STATE_NOT_ACTIVATED)
{
evt_data.status = NFC_STATUS_FAILED;
(*rw_cb.p_cback) (RW_T4T_PRESENCE_CHECK_EVT, &evt_data);
}
/* If command is pending, assume tag is still present */
else if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE)
{
evt_data.status = NFC_STATUS_OK;
(*rw_cb.p_cback) (RW_T4T_PRESENCE_CHECK_EVT, &evt_data);
}
else
{
status = FALSE;
if (option == RW_T4T_CHK_EMPTY_I_BLOCK)
{
/* use empty I block for presence check */
if ((p_data = (BT_HDR *) GKI_getbuf (NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE)) != NULL)
{
p_data->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p_data->len = 0;
if (NFC_SendData (NFC_RF_CONN_ID, (BT_HDR*) p_data) == NFC_STATUS_OK)
status = TRUE;
}
}
else
{
/* use read binary on the given channel */
rw_cb.tcb.t4t.channel = 0;
if (option <= RW_T4T_CHK_READ_BINARY_CH3)
rw_cb.tcb.t4t.channel = option;
status = rw_t4t_read_file (0, 1, FALSE);
rw_cb.tcb.t4t.channel = 0;
}
if (status == TRUE)
{
rw_cb.tcb.t4t.state = RW_T4T_STATE_PRESENCE_CHECK;
}
else
{
retval = NFC_STATUS_NO_BUFFERS;
}
}
return (retval);
}
/*****************************************************************************
**
** Function RW_T4tSetNDefReadOnly
**
** Description This function performs NDEF read-only procedure
** Note: RW_T4tDetectNDef() must be called before using this
**
** The RW_T4T_SET_TO_RO_EVT event will be returned.
**
** Returns NFC_STATUS_OK if success
** NFC_STATUS_FAILED if T4T is busy or other error
**
*****************************************************************************/
tNFC_STATUS RW_T4tSetNDefReadOnly (void)
{
tNFC_STATUS retval = NFC_STATUS_OK;
tRW_DATA evt_data;
RW_TRACE_API0 ("RW_T4tSetNDefReadOnly ()");
if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE)
{
RW_TRACE_ERROR1 ("RW_T4tSetNDefReadOnly ():Unable to start command at state (0x%X)",
rw_cb.tcb.t4t.state);
return NFC_STATUS_FAILED;
}
/* if NDEF has been detected */
if (rw_cb.tcb.t4t.ndef_status & RW_T4T_NDEF_STATUS_NDEF_DETECTED)
{
/* if read-only */
if (rw_cb.tcb.t4t.ndef_status & RW_T4T_NDEF_STATUS_NDEF_READ_ONLY)
{
RW_TRACE_API0 ("RW_T4tSetNDefReadOnly (): NDEF is already read-only");
evt_data.status = NFC_STATUS_OK;
(*rw_cb.p_cback) (RW_T4T_SET_TO_RO_EVT, &evt_data);
return (retval);
}
/* NDEF Tag application has been selected then select CC file */
if (!rw_t4t_select_file (T4T_CC_FILE_ID))
{
return NFC_STATUS_FAILED;
}
rw_cb.tcb.t4t.state = RW_T4T_STATE_SET_READ_ONLY;
rw_cb.tcb.t4t.sub_state = RW_T4T_SUBSTATE_WAIT_SELECT_CC;
return NFC_STATUS_OK;
}
else
{
RW_TRACE_ERROR0 ("RW_T4tSetNDefReadOnly ():No NDEF detected");
return NFC_STATUS_FAILED;
}
return (retval);
}
#if (BT_TRACE_VERBOSE == TRUE)
/*******************************************************************************
**
** Function rw_t4t_get_state_name
**
** Description This function returns the state name.
**
** NOTE conditionally compiled to save memory.
**
** Returns pointer to the name
**
*******************************************************************************/
static char *rw_t4t_get_state_name (UINT8 state)
{
switch (state)
{
case RW_T4T_STATE_NOT_ACTIVATED:
return ("NOT_ACTIVATED");
case RW_T4T_STATE_IDLE:
return ("IDLE");
case RW_T4T_STATE_DETECT_NDEF:
return ("NDEF_DETECTION");
case RW_T4T_STATE_READ_NDEF:
return ("READ_NDEF");
case RW_T4T_STATE_UPDATE_NDEF:
return ("UPDATE_NDEF");
case RW_T4T_STATE_PRESENCE_CHECK:
return ("PRESENCE_CHECK");
case RW_T4T_STATE_SET_READ_ONLY:
return ("SET_READ_ONLY");
default:
return ("???? UNKNOWN STATE");
}
}
/*******************************************************************************
**
** Function rw_t4t_get_sub_state_name
**
** Description This function returns the sub_state name.
**
** NOTE conditionally compiled to save memory.
**
** Returns pointer to the name
**
*******************************************************************************/
static char *rw_t4t_get_sub_state_name (UINT8 sub_state)
{
switch (sub_state)
{
case RW_T4T_SUBSTATE_WAIT_SELECT_APP:
return ("WAIT_SELECT_APP");
case RW_T4T_SUBSTATE_WAIT_SELECT_CC:
return ("WAIT_SELECT_CC");
case RW_T4T_SUBSTATE_WAIT_CC_FILE:
return ("WAIT_CC_FILE");
case RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE:
return ("WAIT_SELECT_NDEF_FILE");
case RW_T4T_SUBSTATE_WAIT_READ_NLEN:
return ("WAIT_READ_NLEN");
case RW_T4T_SUBSTATE_WAIT_READ_RESP:
return ("WAIT_READ_RESP");
case RW_T4T_SUBSTATE_WAIT_UPDATE_RESP:
return ("WAIT_UPDATE_RESP");
case RW_T4T_SUBSTATE_WAIT_UPDATE_NLEN:
return ("WAIT_UPDATE_NLEN");
case RW_T4T_SUBSTATE_WAIT_GET_HW_VERSION:
return ("WAIT_GET_HW_VERSION");
case RW_T4T_SUBSTATE_WAIT_GET_SW_VERSION :
return ("WAIT_GET_SW_VERSION");
case RW_T4T_SUBSTATE_WAIT_GET_UID:
return ("WAIT_GET_UID");
case RW_T4T_SUBSTATE_WAIT_CREATE_APP:
return ("WAIT_CREATE_APP");
case RW_T4T_SUBSTATE_WAIT_CREATE_CC:
return ("WAIT_CREATE_CC");
case RW_T4T_SUBSTATE_WAIT_CREATE_NDEF:
return ("WAIT_CREATE_NDEF");
case RW_T4T_SUBSTATE_WAIT_WRITE_CC :
return ("WAIT_WRITE_CC");
case RW_T4T_SUBSTATE_WAIT_WRITE_NDEF:
return ("WAIT_WRITE_NDEF");
default:
return ("???? UNKNOWN SUBSTATE");
}
}
#endif
#endif /* (NFC_INCLUDED == TRUE) */