blob: 9b859e2bdebcdebfef89037604b753e682043712 [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 Card Emulation
* mode.
*
******************************************************************************/
#include <log/log.h>
#include <string.h>
#include <android-base/stringprintf.h>
#include <base/logging.h>
#include "nfc_target.h"
#include "bt_types.h"
#include "ce_api.h"
#include "ce_int.h"
#include "nfc_int.h"
#include "tags_int.h"
#include "trace_api.h"
using android::base::StringPrintf;
extern bool nfc_debug_enabled;
#if (CE_TEST_INCLUDED == TRUE) /* test only */
bool mapping_aid_test_enabled = false;
uint8_t ce_test_tag_app_id[T4T_V20_NDEF_TAG_AID_LEN] = {0xD2, 0x76, 0x00, 0x00,
0x85, 0x01, 0x01};
#endif
/*******************************************************************************
**
** Function ce_t4t_send_to_lower
**
** Description Send packet to lower layer
**
** Returns TRUE if success
**
*******************************************************************************/
static bool ce_t4t_send_to_lower(NFC_HDR* p_r_apdu) {
if (NFC_SendData(NFC_RF_CONN_ID, p_r_apdu) != NFC_STATUS_OK) {
LOG(ERROR) << StringPrintf("failed");
return false;
}
return true;
}
/*******************************************************************************
**
** Function ce_t4t_send_status
**
** Description Send status on R-APDU to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static bool ce_t4t_send_status(uint16_t status) {
NFC_HDR* p_r_apdu;
uint8_t* p;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("Status:0x%04X", status);
p_r_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_CE_POOL_ID);
if (!p_r_apdu) {
LOG(ERROR) << StringPrintf("Cannot allocate buffer");
return false;
}
p_r_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p = (uint8_t*)(p_r_apdu + 1) + p_r_apdu->offset;
UINT16_TO_BE_STREAM(p, status);
p_r_apdu->len = T4T_RSP_STATUS_WORDS_SIZE;
if (!ce_t4t_send_to_lower(p_r_apdu)) {
return false;
}
return true;
}
/*******************************************************************************
**
** Function ce_t4t_select_file
**
** Description Select a file
**
** Returns TRUE if success
**
*******************************************************************************/
static bool ce_t4t_select_file(uint16_t file_id) {
tCE_T4T_MEM* p_t4t = &ce_cb.mem.t4t;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("FileID:0x%04X", file_id);
if (file_id == T4T_CC_FILE_ID) {
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("Select CC file");
p_t4t->status |= CE_T4T_STATUS_CC_FILE_SELECTED;
p_t4t->status &= ~(CE_T4T_STATUS_NDEF_SELECTED);
return true;
}
if (file_id == CE_T4T_MANDATORY_NDEF_FILE_ID) {
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"NLEN:0x%04X, MaxFileSize:0x%04X, "
"WriteAccess:%s",
p_t4t->nlen, p_t4t->max_file_size,
(p_t4t->status & CE_T4T_STATUS_NDEF_FILE_READ_ONLY ? "RW" : "RO"));
p_t4t->status |= CE_T4T_STATUS_NDEF_SELECTED;
p_t4t->status &= ~(CE_T4T_STATUS_CC_FILE_SELECTED);
return true;
} else {
LOG(ERROR) << StringPrintf("Cannot find file ID (0x%04X)", file_id);
p_t4t->status &= ~(CE_T4T_STATUS_CC_FILE_SELECTED);
p_t4t->status &= ~(CE_T4T_STATUS_NDEF_SELECTED);
return false;
}
}
/*******************************************************************************
**
** Function ce_t4t_read_binary
**
** Description Read data from selected file and send R-APDU to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static bool ce_t4t_read_binary(uint16_t offset, uint8_t length) {
tCE_T4T_MEM* p_t4t = &ce_cb.mem.t4t;
uint8_t *p_src = NULL, *p_dst;
NFC_HDR* p_r_apdu;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"Offset:0x%04X, Length:0x%04X, selected status = "
"0x%02X",
offset, length, p_t4t->status);
if (p_t4t->status & CE_T4T_STATUS_CC_FILE_SELECTED) {
p_src = p_t4t->cc_file;
} else if (p_t4t->status & CE_T4T_STATUS_NDEF_SELECTED) {
if (p_t4t->p_scratch_buf)
p_src = p_t4t->p_scratch_buf;
else
p_src = p_t4t->p_ndef_msg;
}
if (p_src) {
p_r_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_CE_POOL_ID);
if (!p_r_apdu) {
LOG(ERROR) << StringPrintf("Cannot allocate buffer");
return false;
}
p_r_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
p_dst = (uint8_t*)(p_r_apdu + 1) + p_r_apdu->offset;
p_r_apdu->len = length;
/* add NLEN before NDEF message and adjust offset */
/* if NDEF file is selected and offset < T4T_FILE_LENGTH_SIZE */
if ((p_t4t->status & CE_T4T_STATUS_NDEF_SELECTED) && (length > 0)) {
if (offset == 0) {
UINT16_TO_BE_STREAM(p_dst, p_t4t->nlen);
if (length == 1) {
length = 0;
p_dst--;
} else /* length >= 2 */
length -= T4T_FILE_LENGTH_SIZE;
} else if (offset == 1) {
UINT8_TO_BE_STREAM(p_dst, (uint8_t)(p_t4t->nlen));
offset = 0;
length--;
} else {
offset -= T4T_FILE_LENGTH_SIZE;
}
}
if (length > 0) {
memcpy(p_dst, p_src + offset, length);
p_dst += length;
}
UINT16_TO_BE_STREAM(p_dst, T4T_RSP_CMD_CMPLTED);
p_r_apdu->len += T4T_RSP_STATUS_WORDS_SIZE;
if (!ce_t4t_send_to_lower(p_r_apdu)) {
return false;
}
return true;
} else {
LOG(ERROR) << StringPrintf("No selected file");
if (!ce_t4t_send_status(T4T_RSP_CMD_NOT_ALLOWED)) {
return false;
}
return true;
}
}
/*******************************************************************************
**
** Function ce_t4t_update_binary
**
** Description Update file and send R-APDU to peer
**
** Returns TRUE if success
**
*******************************************************************************/
static bool ce_t4t_update_binary(uint16_t offset, uint8_t length,
uint8_t* p_data) {
tCE_T4T_MEM* p_t4t = &ce_cb.mem.t4t;
uint8_t* p;
uint8_t file_length[2];
uint16_t starting_offset;
tCE_DATA ce_data;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"Offset:0x%04X, Length:0x%04X, selected status "
"= 0x%02X",
offset, length, p_t4t->status);
starting_offset = offset;
/* update file size (NLEN) */
if ((offset < T4T_FILE_LENGTH_SIZE) && (length > 0)) {
p = file_length;
UINT16_TO_BE_STREAM(p, p_t4t->nlen);
while ((offset < T4T_FILE_LENGTH_SIZE) && (length > 0)) {
*(file_length + offset++) = *(p_data++);
length--;
}
p = file_length;
BE_STREAM_TO_UINT16(p_t4t->nlen, p);
}
if (length > 0)
memcpy(p_t4t->p_scratch_buf + offset - T4T_FILE_LENGTH_SIZE, p_data,
length);
/* if this is the last step: writing non-zero length in NLEN */
if ((starting_offset == 0) && (p_t4t->nlen > 0)) {
nfc_stop_quick_timer(&p_t4t->timer);
if (ce_cb.p_cback) {
ce_data.update_info.status = NFC_STATUS_OK;
ce_data.update_info.length = p_t4t->nlen;
ce_data.update_info.p_data = p_t4t->p_scratch_buf;
(*ce_cb.p_cback)(CE_T4T_NDEF_UPDATE_CPLT_EVT, &ce_data);
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("Sent CE_T4T_NDEF_UPDATE_CPLT_EVT");
}
p_t4t->status &= ~(CE_T4T_STATUS_NDEF_FILE_UPDATING);
} else if (!(p_t4t->status & CE_T4T_STATUS_NDEF_FILE_UPDATING)) {
/* starting of updating */
p_t4t->status |= CE_T4T_STATUS_NDEF_FILE_UPDATING;
nfc_start_quick_timer(
&p_t4t->timer, NFC_TTYPE_CE_T4T_UPDATE,
(CE_T4T_TOUT_UPDATE * QUICK_TIMER_TICKS_PER_SEC) / 1000);
if (ce_cb.p_cback) (*ce_cb.p_cback)(CE_T4T_NDEF_UPDATE_START_EVT, NULL);
}
if (!ce_t4t_send_status(T4T_RSP_CMD_CMPLTED)) {
return false;
} else {
return true;
}
}
/*******************************************************************************
**
** Function ce_t4t_set_version_in_cc
**
** Description update version in CC file
** If reader selects NDEF Tag Application with V1.0 AID then
** set V1.0 into CC file.
** If reader selects NDEF Tag Application with V2.0 AID then
** set V2.0 into CC file.
**
** Returns None
**
*******************************************************************************/
static void ce_t4t_set_version_in_cc(uint8_t version) {
tCE_T4T_MEM* p_t4t = &ce_cb.mem.t4t;
uint8_t* p;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("version = 0x%02X", version);
p = p_t4t->cc_file + T4T_VERSION_OFFSET_IN_CC;
UINT8_TO_BE_STREAM(p, version);
}
/*******************************************************************************
**
** Function ce_t4t_process_select_file_cmd
**
** Description This function processes Select Command by file ID.
**
** Returns TRUE if success
**
*******************************************************************************/
static bool ce_t4t_process_select_file_cmd(uint8_t* p_cmd) {
uint8_t data_len;
uint16_t file_id, status_words;
DLOG_IF(INFO, nfc_debug_enabled) << __func__;
p_cmd++; /* skip P2 */
/* Lc Byte */
BE_STREAM_TO_UINT8(data_len, p_cmd);
if (data_len == T4T_FILE_ID_SIZE) {
/* File ID */
BE_STREAM_TO_UINT16(file_id, p_cmd);
if (ce_t4t_select_file(file_id)) {
status_words = T4T_RSP_CMD_CMPLTED;
} else {
status_words = T4T_RSP_NOT_FOUND;
}
} else {
status_words = T4T_RSP_WRONG_LENGTH;
}
if (!ce_t4t_send_status(status_words)) {
return false;
}
if (status_words == T4T_RSP_CMD_CMPLTED) {
return true;
}
return false;
}
/*******************************************************************************
**
** Function ce_t4t_process_select_app_cmd
**
** Description This function processes Select Command by AID.
**
** Returns none
**
*******************************************************************************/
static void ce_t4t_process_select_app_cmd(uint8_t* p_cmd, NFC_HDR* p_c_apdu) {
uint8_t data_len;
uint16_t status_words = 0x0000; /* invalid status words */
tCE_DATA ce_data;
uint8_t xx;
DLOG_IF(INFO, nfc_debug_enabled) << __func__;
p_cmd++; /* skip P2 */
/* Lc Byte */
BE_STREAM_TO_UINT8(data_len, p_cmd);
/*CLS+INS+P1+P2+Lc+Data*/
if (data_len > (p_c_apdu->len - T4T_CMD_MAX_HDR_SIZE)) {
LOG(ERROR) << StringPrintf("Wrong length in ce_t4t_process_select_app_cmd");
android_errorWriteLog(0x534e4554, "115635871");
ce_t4t_send_status(T4T_RSP_WRONG_LENGTH);
GKI_freebuf(p_c_apdu);
return;
}
#if (CE_TEST_INCLUDED == TRUE)
if (mapping_aid_test_enabled) {
if ((data_len == T4T_V20_NDEF_TAG_AID_LEN) &&
(!memcmp(p_cmd, ce_test_tag_app_id, data_len)) &&
(ce_cb.mem.t4t.p_ndef_msg)) {
GKI_freebuf(p_c_apdu);
ce_t4t_send_status((uint16_t)T4T_RSP_CMD_CMPLTED);
return;
}
}
#endif
/*
** Compare AIDs registered by applications
** if found, use callback of the application
** otherwise, return error and maintain the same status
*/
ce_cb.mem.t4t.selected_aid_idx = CE_T4T_MAX_REG_AID;
for (xx = 0; xx < CE_T4T_MAX_REG_AID; xx++) {
if ((ce_cb.mem.t4t.reg_aid[xx].aid_len > 0) &&
(ce_cb.mem.t4t.reg_aid[xx].aid_len == data_len) &&
(!(memcmp(ce_cb.mem.t4t.reg_aid[xx].aid, p_cmd, data_len)))) {
ce_cb.mem.t4t.selected_aid_idx = xx;
break;
}
}
/* if found matched AID */
if (ce_cb.mem.t4t.selected_aid_idx < CE_T4T_MAX_REG_AID) {
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_CC_FILE_SELECTED);
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_NDEF_SELECTED);
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_T4T_APP_SELECTED);
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_WILDCARD_AID_SELECTED);
ce_cb.mem.t4t.status |= CE_T4T_STATUS_REG_AID_SELECTED;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"Registered AID[%02X%02X%02X%02X...] "
"is selected",
ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].aid[0],
ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].aid[1],
ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].aid[2],
ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].aid[3]);
ce_data.raw_frame.status = NFC_STATUS_OK;
ce_data.raw_frame.p_data = p_c_apdu;
ce_data.raw_frame.aid_handle = ce_cb.mem.t4t.selected_aid_idx;
p_c_apdu = NULL;
(*(ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].p_cback))(
CE_T4T_RAW_FRAME_EVT, &ce_data);
} else if ((data_len == T4T_V20_NDEF_TAG_AID_LEN) &&
(!memcmp(p_cmd, t4t_v20_ndef_tag_aid, data_len - 1)) &&
(ce_cb.mem.t4t.p_ndef_msg)) {
p_cmd += data_len - 1;
/* adjust version if possible */
if ((*p_cmd) == 0x00) {
ce_t4t_set_version_in_cc(T4T_VERSION_1_0);
status_words = T4T_RSP_CMD_CMPLTED;
} else if ((*p_cmd) == 0x01) {
ce_t4t_set_version_in_cc(T4T_VERSION_2_0);
status_words = T4T_RSP_CMD_CMPLTED;
} else {
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("Not found matched AID");
status_words = T4T_RSP_NOT_FOUND;
}
} else if (ce_cb.mem.t4t.p_wildcard_aid_cback) {
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_CC_FILE_SELECTED);
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_NDEF_SELECTED);
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_T4T_APP_SELECTED);
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_REG_AID_SELECTED);
ce_cb.mem.t4t.status |= CE_T4T_STATUS_WILDCARD_AID_SELECTED;
ce_data.raw_frame.status = NFC_STATUS_OK;
ce_data.raw_frame.p_data = p_c_apdu;
ce_data.raw_frame.aid_handle = CE_T4T_WILDCARD_AID_HANDLE;
p_c_apdu = NULL;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"CET4T: Forward raw frame (SELECT APP) to wildcard AID handler");
(*(ce_cb.mem.t4t.p_wildcard_aid_cback))(CE_T4T_RAW_FRAME_EVT, &ce_data);
} else {
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"Not found matched AID or not "
"listening T4T NDEF");
status_words = T4T_RSP_NOT_FOUND;
}
if (status_words) {
/* if T4T CE can support */
if (status_words == T4T_RSP_CMD_CMPLTED) {
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_CC_FILE_SELECTED);
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_NDEF_SELECTED);
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_REG_AID_SELECTED);
ce_cb.mem.t4t.status &= ~(CE_T4T_STATUS_WILDCARD_AID_SELECTED);
ce_cb.mem.t4t.status |= CE_T4T_STATUS_T4T_APP_SELECTED;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("T4T CE App selected");
}
ce_t4t_send_status(status_words);
GKI_freebuf(p_c_apdu);
}
/* if status_words is not set then upper layer will send R-APDU */
return;
}
/*******************************************************************************
**
** Function ce_t4t_process_timeout
**
** Description process timeout event
**
** Returns none
**
*******************************************************************************/
void ce_t4t_process_timeout(TIMER_LIST_ENT* p_tle) {
tCE_T4T_MEM* p_t4t = &ce_cb.mem.t4t;
tCE_DATA ce_data;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("event=%d", p_tle->event);
if (p_tle->event == NFC_TTYPE_CE_T4T_UPDATE) {
if (p_t4t->status & CE_T4T_STATUS_NDEF_FILE_UPDATING) {
ce_data.status = NFC_STATUS_TIMEOUT;
if (ce_cb.p_cback)
(*ce_cb.p_cback)(CE_T4T_NDEF_UPDATE_ABORT_EVT, &ce_data);
p_t4t->status &= ~(CE_T4T_STATUS_NDEF_FILE_UPDATING);
}
} else {
LOG(ERROR) << StringPrintf("unknown event=%d", p_tle->event);
}
}
/*******************************************************************************
**
** Function ce_t4t_data_cback
**
** Description This callback function receives the data from NFCC.
**
** Returns none
**
*******************************************************************************/
static void ce_t4t_data_cback(uint8_t conn_id, tNFC_CONN_EVT event,
tNFC_CONN* p_data) {
NFC_HDR* p_c_apdu;
uint8_t* p_cmd;
uint8_t cla = 0, instruct = 0, select_type = 0, length = 0;
uint16_t offset, max_file_size;
tCE_DATA ce_data;
if (event == NFC_DEACTIVATE_CEVT) {
NFC_SetStaticRfCback(NULL);
return;
}
if (event != NFC_DATA_CEVT) {
return;
}
p_c_apdu = (NFC_HDR*)p_data->data.p_data;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("conn_id = 0x%02X", conn_id);
p_cmd = (uint8_t*)(p_c_apdu + 1) + p_c_apdu->offset;
if (p_c_apdu->len == 0) {
LOG(ERROR) << StringPrintf("Wrong length in ce_t4t_data_cback");
android_errorWriteLog(0x534e4554, "115635871");
ce_t4t_send_status(T4T_RSP_WRONG_LENGTH);
if (p_c_apdu) GKI_freebuf(p_c_apdu);
return;
}
/* Class Byte */
BE_STREAM_TO_UINT8(cla, p_cmd);
/* Don't check class if registered AID has been selected */
if ((cla != T4T_CMD_CLASS) &&
((ce_cb.mem.t4t.status & CE_T4T_STATUS_REG_AID_SELECTED) == 0) &&
((ce_cb.mem.t4t.status & CE_T4T_STATUS_WILDCARD_AID_SELECTED) == 0)) {
GKI_freebuf(p_c_apdu);
ce_t4t_send_status(T4T_RSP_CLASS_NOT_SUPPORTED);
return;
}
/*CLA+INS+P1+P2 = 4 bytes*/
if (p_c_apdu->len >= T4T_CMD_MIN_HDR_SIZE) {
/* Instruction Byte */
BE_STREAM_TO_UINT8(instruct, p_cmd);
if ((cla == T4T_CMD_CLASS) && (instruct == T4T_CMD_INS_SELECT)) {
/* P1 Byte */
BE_STREAM_TO_UINT8(select_type, p_cmd);
if (select_type == T4T_CMD_P1_SELECT_BY_NAME) {
/*CLA+INS+P1+P2+Lc = 5 bytes*/
if (p_c_apdu->len >= T4T_CMD_MAX_HDR_SIZE) {
ce_t4t_process_select_app_cmd(p_cmd, p_c_apdu);
return;
} else {
LOG(ERROR) << StringPrintf("Wrong length in select app cmd");
android_errorWriteLog(0x534e4554, "115635871");
ce_t4t_send_status(T4T_RSP_NOT_FOUND);
if (p_c_apdu) GKI_freebuf(p_c_apdu);
return;
}
}
}
}
/* if registered AID is selected */
if (ce_cb.mem.t4t.status & CE_T4T_STATUS_REG_AID_SELECTED) {
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("CET4T: Forward raw frame to registered AID");
/* forward raw frame to upper layer */
if (ce_cb.mem.t4t.selected_aid_idx < CE_T4T_MAX_REG_AID) {
ce_data.raw_frame.status = p_data->data.status;
ce_data.raw_frame.p_data = p_c_apdu;
ce_data.raw_frame.aid_handle = ce_cb.mem.t4t.selected_aid_idx;
p_c_apdu = NULL;
(*(ce_cb.mem.t4t.reg_aid[ce_cb.mem.t4t.selected_aid_idx].p_cback))(
CE_T4T_RAW_FRAME_EVT, &ce_data);
} else {
GKI_freebuf(p_c_apdu);
ce_t4t_send_status(T4T_RSP_NOT_FOUND);
}
} else if (ce_cb.mem.t4t.status & CE_T4T_STATUS_WILDCARD_AID_SELECTED) {
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("CET4T: Forward raw frame to wildcard AID handler");
/* forward raw frame to upper layer */
ce_data.raw_frame.status = p_data->data.status;
ce_data.raw_frame.p_data = p_c_apdu;
ce_data.raw_frame.aid_handle = CE_T4T_WILDCARD_AID_HANDLE;
p_c_apdu = NULL;
(*(ce_cb.mem.t4t.p_wildcard_aid_cback))(CE_T4T_RAW_FRAME_EVT, &ce_data);
} else if (ce_cb.mem.t4t.status & CE_T4T_STATUS_T4T_APP_SELECTED) {
if (instruct == T4T_CMD_INS_SELECT) {
/* P1 Byte is already parsed */
if (select_type == T4T_CMD_P1_SELECT_BY_FILE_ID) {
ce_t4t_process_select_file_cmd(p_cmd);
} else {
LOG(ERROR) << StringPrintf("CET4T: Bad P1 byte (0x%02X)", select_type);
ce_t4t_send_status(T4T_RSP_WRONG_PARAMS);
}
} else if (instruct == T4T_CMD_INS_READ_BINARY) {
if ((ce_cb.mem.t4t.status & CE_T4T_STATUS_CC_FILE_SELECTED) ||
(ce_cb.mem.t4t.status & CE_T4T_STATUS_NDEF_SELECTED)) {
if (ce_cb.mem.t4t.status & CE_T4T_STATUS_CC_FILE_SELECTED) {
max_file_size = T4T_FC_TLV_OFFSET_IN_CC + T4T_FILE_CONTROL_TLV_SIZE;
} else {
max_file_size = ce_cb.mem.t4t.max_file_size;
}
BE_STREAM_TO_UINT16(offset, p_cmd); /* Offset */
BE_STREAM_TO_UINT8(length, p_cmd); /* Le */
/* check if valid parameters */
if ((uint32_t)length <= CE_T4T_MAX_LE) {
/* CE allows to read more than current file size but not max file size
*/
if (length + offset > max_file_size) {
if (offset < max_file_size) {
length = (uint8_t)(max_file_size - offset);
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"CET4T: length is reduced to %d by max_file_size (%d)",
length, max_file_size);
} else {
LOG(ERROR) << StringPrintf(
"CET4T: offset (%d) must be less than max_file_size (%d)",
offset, max_file_size);
length = 0;
}
}
} else {
LOG(ERROR) << StringPrintf(
"CET4T: length (%d) must be less than MLe (%zu)", length,
CE_T4T_MAX_LE);
length = 0;
}
if (length > 0)
ce_t4t_read_binary(offset, length);
else
ce_t4t_send_status(T4T_RSP_WRONG_PARAMS);
} else {
LOG(ERROR) << StringPrintf("CET4T: File has not been selected");
ce_t4t_send_status(T4T_RSP_CMD_NOT_ALLOWED);
}
} else if (instruct == T4T_CMD_INS_UPDATE_BINARY) {
if (ce_cb.mem.t4t.status & CE_T4T_STATUS_NDEF_FILE_READ_ONLY) {
LOG(ERROR) << StringPrintf("CET4T: No access right");
ce_t4t_send_status(T4T_RSP_CMD_NOT_ALLOWED);
} else if (ce_cb.mem.t4t.status & CE_T4T_STATUS_NDEF_SELECTED) {
BE_STREAM_TO_UINT16(offset, p_cmd); /* Offset */
BE_STREAM_TO_UINT8(length, p_cmd); /* Lc */
/* check if valid parameters */
if ((uint32_t)length <= CE_T4T_MAX_LC) {
if (length + offset > ce_cb.mem.t4t.max_file_size) {
LOG(ERROR) << StringPrintf(
"CET4T: length (%d) + offset (%d) must be less than "
"max_file_size (%d)",
length, offset, ce_cb.mem.t4t.max_file_size);
length = 0;
}
} else {
LOG(ERROR) << StringPrintf(
"CET4T: length (%d) must be less than MLc (%zu)", length,
CE_T4T_MAX_LC);
length = 0;
}
if (length > 0)
ce_t4t_update_binary(offset, length, p_cmd);
else
ce_t4t_send_status(T4T_RSP_WRONG_PARAMS);
} else {
LOG(ERROR) << StringPrintf("CET4T: NDEF File has not been selected");
ce_t4t_send_status(T4T_RSP_CMD_NOT_ALLOWED);
}
} else {
LOG(ERROR) << StringPrintf("CET4T: Unsupported Instruction byte (0x%02X)",
instruct);
ce_t4t_send_status(T4T_RSP_INSTR_NOT_SUPPORTED);
}
} else {
LOG(ERROR) << StringPrintf("CET4T: Application has not been selected");
ce_t4t_send_status(T4T_RSP_CMD_NOT_ALLOWED);
}
if (p_c_apdu) GKI_freebuf(p_c_apdu);
}
/*******************************************************************************
**
** Function ce_select_t4t
**
** Description Select Type 4 Tag
**
** Returns NFC_STATUS_OK if success
**
*******************************************************************************/
tNFC_STATUS ce_select_t4t(void) {
tCE_T4T_MEM* p_t4t = &ce_cb.mem.t4t;
DLOG_IF(INFO, nfc_debug_enabled) << __func__;
nfc_stop_quick_timer(&p_t4t->timer);
/* clear other than read-only flag */
p_t4t->status &= CE_T4T_STATUS_NDEF_FILE_READ_ONLY;
NFC_SetStaticRfCback(ce_t4t_data_cback);
return NFC_STATUS_OK;
}
/*******************************************************************************
**
** Function CE_T4tSetLocalNDEFMsg
**
** Description Initialise CE Type 4 Tag with mandatory NDEF message
**
** The following event may be returned
** CE_T4T_UPDATE_START_EVT for starting update
** CE_T4T_UPDATE_CPLT_EVT for complete update
** CE_T4T_UPDATE_ABORT_EVT for failure of update
** CE_T4T_RAW_FRAME_EVT for raw frame
**
** read_only: TRUE if read only
** ndef_msg_max: Max NDEF message size
** ndef_msg_len: NDEF message size
** p_ndef_msg: NDEF message (excluding NLEN)
** p_scratch_buf: temp storage for update
**
** Returns NFC_STATUS_OK if success
**
*******************************************************************************/
tNFC_STATUS CE_T4tSetLocalNDEFMsg(bool read_only, uint16_t ndef_msg_max,
uint16_t ndef_msg_len, uint8_t* p_ndef_msg,
uint8_t* p_scratch_buf) {
tCE_T4T_MEM* p_t4t = &ce_cb.mem.t4t;
uint8_t* p;
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("read_only=%d, ndef_msg_max=%d, ndef_msg_len=%d",
read_only, ndef_msg_max, ndef_msg_len);
if (!p_ndef_msg) {
p_t4t->p_ndef_msg = NULL;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("T4T is disabled");
return NFC_STATUS_OK;
}
if ((!read_only) && (!p_scratch_buf)) {
LOG(ERROR) << StringPrintf(
"p_scratch_buf cannot be NULL if not "
"read-only");
return NFC_STATUS_FAILED;
}
#if (CE_TEST_INCLUDED == TRUE)
mapping_aid_test_enabled = false;
#endif
/* Initialise CC file */
p = p_t4t->cc_file;
UINT16_TO_BE_STREAM(p, T4T_CC_FILE_MIN_LEN);
UINT8_TO_BE_STREAM(p, T4T_MY_VERSION);
UINT16_TO_BE_STREAM(p, CE_T4T_MAX_LE);
UINT16_TO_BE_STREAM(p, CE_T4T_MAX_LC);
/* Mandatory NDEF File Control TLV */
UINT8_TO_BE_STREAM(p, T4T_NDEF_FILE_CONTROL_TYPE); /* type */
UINT8_TO_BE_STREAM(p, T4T_FILE_CONTROL_LENGTH); /* length */
UINT16_TO_BE_STREAM(p, CE_T4T_MANDATORY_NDEF_FILE_ID); /* file ID */
UINT16_TO_BE_STREAM(
p, ndef_msg_max + T4T_FILE_LENGTH_SIZE); /* max NDEF file size */
UINT8_TO_BE_STREAM(p, T4T_FC_READ_ACCESS); /* read access */
if (read_only) {
UINT8_TO_BE_STREAM(p, T4T_FC_NO_WRITE_ACCESS); /* read only */
p_t4t->status |= CE_T4T_STATUS_NDEF_FILE_READ_ONLY;
} else {
UINT8_TO_BE_STREAM(p, T4T_FC_WRITE_ACCESS); /* write access */
p_t4t->status &= ~(CE_T4T_STATUS_NDEF_FILE_READ_ONLY);
}
/* set mandatory NDEF file */
p_t4t->p_ndef_msg = p_ndef_msg;
p_t4t->nlen = ndef_msg_len;
p_t4t->max_file_size = ndef_msg_max + T4T_FILE_LENGTH_SIZE;
/* Initialize scratch buffer */
p_t4t->p_scratch_buf = p_scratch_buf;
if (p_scratch_buf) {
memcpy(p_scratch_buf, p_ndef_msg, ndef_msg_len);
}
return NFC_STATUS_OK;
}
/*******************************************************************************
**
** Function CE_T4tRegisterAID
**
** Description Register AID in CE T4T
**
** aid_len: length of AID (up to NFC_MAX_AID_LEN)
** p_aid: AID
** p_cback: Raw frame will be forwarded with CE_RAW_FRAME_EVT
**
** Returns tCE_T4T_AID_HANDLE if successful,
** CE_T4T_AID_HANDLE_INVALID otherwisse
**
*******************************************************************************/
tCE_T4T_AID_HANDLE CE_T4tRegisterAID(uint8_t aid_len, uint8_t* p_aid,
tCE_CBACK* p_cback) {
tCE_T4T_MEM* p_t4t = &ce_cb.mem.t4t;
uint8_t xx;
/* Handle registering callback for wildcard AID (all AIDs) */
if (aid_len == 0) {
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("registering callback for wildcard AID ");
/* Check if a wildcard callback is already registered (only one is allowed)
*/
if (p_t4t->p_wildcard_aid_cback != NULL) {
LOG(ERROR) << StringPrintf(
"only one wildcard AID can be registered at "
"time.");
return CE_T4T_AID_HANDLE_INVALID;
}
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("handle 0x%02x registered (for wildcard AID)",
CE_T4T_WILDCARD_AID_HANDLE);
p_t4t->p_wildcard_aid_cback = p_cback;
return CE_T4T_WILDCARD_AID_HANDLE;
}
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("AID [%02X%02X%02X%02X...], %d bytes", *p_aid,
*(p_aid + 1), *(p_aid + 2), *(p_aid + 3), aid_len);
if (aid_len > NFC_MAX_AID_LEN) {
LOG(ERROR) << StringPrintf("AID is up to %d bytes", NFC_MAX_AID_LEN);
return CE_T4T_AID_HANDLE_INVALID;
}
if (p_cback == NULL) {
LOG(ERROR) << StringPrintf("callback must be provided");
return CE_T4T_AID_HANDLE_INVALID;
}
for (xx = 0; xx < CE_T4T_MAX_REG_AID; xx++) {
if ((p_t4t->reg_aid[xx].aid_len == aid_len) &&
(!(memcmp(p_t4t->reg_aid[xx].aid, p_aid, aid_len)))) {
LOG(ERROR) << StringPrintf("already registered");
return CE_T4T_AID_HANDLE_INVALID;
}
}
for (xx = 0; xx < CE_T4T_MAX_REG_AID; xx++) {
if (p_t4t->reg_aid[xx].aid_len == 0) {
p_t4t->reg_aid[xx].aid_len = aid_len;
p_t4t->reg_aid[xx].p_cback = p_cback;
memcpy(p_t4t->reg_aid[xx].aid, p_aid, aid_len);
break;
}
}
if (xx >= CE_T4T_MAX_REG_AID) {
LOG(ERROR) << StringPrintf("No resource");
return CE_T4T_AID_HANDLE_INVALID;
} else {
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("handle 0x%02x registered", xx);
}
return (xx);
}
/*******************************************************************************
**
** Function CE_T4tDeregisterAID
**
** Description Deregister AID in CE T4T
**
** Returns NFC_STATUS_OK if success
**
*******************************************************************************/
extern void CE_T4tDeregisterAID(tCE_T4T_AID_HANDLE aid_handle) {
tCE_T4T_MEM* p_t4t = &ce_cb.mem.t4t;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("handle 0x%02x", aid_handle);
/* Check if deregistering wildcard AID */
if (aid_handle == CE_T4T_WILDCARD_AID_HANDLE) {
if (p_t4t->p_wildcard_aid_cback != NULL) {
p_t4t->p_wildcard_aid_cback = NULL;
} else {
LOG(ERROR) << StringPrintf("Invalid handle");
}
return;
}
/* Deregister AID */
if ((aid_handle >= CE_T4T_MAX_REG_AID) ||
(p_t4t->reg_aid[aid_handle].aid_len == 0)) {
LOG(ERROR) << StringPrintf("Invalid handle");
} else {
p_t4t->reg_aid[aid_handle].aid_len = 0;
p_t4t->reg_aid[aid_handle].p_cback = NULL;
}
}