blob: 060c44f45b6442763acec6268135a1fc16f2c30b [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2020 STMicroelectronics
*
* 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 specific NFC Forum T5T operations
* in Reader/Writer mode.
*
******************************************************************************/
#include <android-base/stringprintf.h>
#include <base/logging.h>
#include <log/log.h>
#include <string.h>
#include "bt_types.h"
#include "nfc_api.h"
#include "nfc_int.h"
#include "nfc_target.h"
#include "rw_api.h"
#include "rw_int.h"
using android::base::StringPrintf;
extern void rw_i93_handle_error(tNFC_STATUS);
extern tNFC_STATUS rw_i93_get_next_blocks(uint16_t);
extern tNFC_STATUS rw_i93_send_cmd_read_single_block(uint16_t, bool);
extern tNFC_STATUS rw_i93_send_cmd_write_single_block(uint16_t, uint8_t*);
extern tNFC_STATUS rw_i93_send_cmd_lock_block(uint16_t);
extern bool nfc_debug_enabled;
/*******************************************************************************
**
** Function rw_t5t_sm_detect_ndef
**
** Description Process NDEF detection procedure
**
** 1. Get UID if not having yet
** 2. Get System Info if not having yet
** 3. Read first block for CC
** 4. Search NDEF Type and length
** 5. Get block status to get max NDEF size and read-only
** status
**
** Returns void
**
*******************************************************************************/
void rw_t5t_sm_detect_ndef(NFC_HDR* p_resp) {
uint8_t* p = (uint8_t*)(p_resp + 1) + p_resp->offset;
uint8_t flags, cc[8];
uint32_t t5t_area_len = 0;
uint16_t length = p_resp->len, xx;
tRW_I93_CB* p_i93 = &rw_cb.tcb.i93;
tRW_DATA rw_data;
tNFC_STATUS status = NFC_STATUS_FAILED;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s - sub_state:%s (0x%x)", __func__,
rw_i93_get_sub_state_name(p_i93->sub_state).c_str(), p_i93->sub_state);
if (length == 0) {
android_errorWriteLog(0x534e4554, "121260197");
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
STREAM_TO_UINT8(flags, p);
length--;
if (flags & I93_FLAG_ERROR_DETECTED) {
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("%s - Got error flags (0x%02x)", __func__, flags);
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
switch (p_i93->sub_state) {
case RW_I93_SUBSTATE_WAIT_CC_EXT:
/* assume block size is 4 */
STREAM_TO_ARRAY(cc, p, 4);
status = NFC_STATUS_FAILED;
/*
** Capability Container (CC) second block
**
** CC[4] : RFU
** CC[5] : RFU
** CC[6] : MSB MLEN
** CC[7] : LSB MLEN
*/
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("%s - cc[4-7]: 0x%02X 0x%02X 0x%02X 0x%02X", __func__,
cc[0], cc[1], cc[2], cc[3]);
/* T5T_Area length = 8 * MLEN */
/* CC is 8-byte, MLEN is defined by bytes 6 & 7 */
t5t_area_len = cc[3] + (cc[2] << 8);
t5t_area_len <<= 3;
p_i93->max_ndef_length = t5t_area_len;
p_i93->t5t_area_last_offset = t5t_area_len + 8 - 1;
memcpy((uint8_t*)&rw_cb.tcb.i93.gre_cc_content[4], (uint8_t*)cc, 4);
p_i93->num_block = t5t_area_len / p_i93->block_size;
p_i93->t5t_area_start_block = 2;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s - T5T Area size:%d, Nb blocks:0x%04X, Block size:0x%02X, "
"T5T Area last offset:%d",
__func__, t5t_area_len, p_i93->num_block, p_i93->block_size,
p_i93->t5t_area_last_offset);
status = NFC_STATUS_OK;
/* search NDEF TLV from offset 8 when CC file coded on 8 bytes NFC Forum
*/
p_i93->rw_offset = 8;
if (p_i93->gre_cc_content[0] == I93_ICODE_CC_MAGIC_NUMER_E2) {
p_i93->intl_flags |= RW_I93_FLAG_EXT_COMMANDS;
}
if (rw_i93_get_next_blocks(p_i93->rw_offset) == NFC_STATUS_OK) {
p_i93->sub_state = RW_I93_SUBSTATE_SEARCH_NDEF_TLV;
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_TYPE;
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
break;
case RW_I93_SUBSTATE_WAIT_CC:
if (length < RW_I93_CC_SIZE) {
android_errorWriteLog(0x534e4554, "139188579");
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
/* assume block size is more than RW_I93_CC_SIZE 4 */
STREAM_TO_ARRAY(cc, p, RW_I93_CC_SIZE);
status = NFC_STATUS_FAILED;
/*
** Capability Container (CC)
**
** CC[0] : magic number (0xE1)
** CC[1] : Bit 7-6:Major version number
** : Bit 5-4:Minor version number
** : Bit 3-2:Read access condition (00b: read access granted
** without any security)
** : Bit 1-0:Write access condition (00b: write access granted
** without any security)
** CC[2] : Memory size in 8 bytes (Ex. 0x04 is 32 bytes) [STM, set to
** 0xFF if more than 2040bytes]
** CC[3] : Bit 0:Read multiple blocks is supported [NXP, STM]
** : Bit 1:Inventory page read is supported [NXP]
** : Bit 2:More than 2040 bytes are supported [STM]
*/
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("%s - cc[0-3]: 0x%02X 0x%02X 0x%02X 0x%02X", __func__,
cc[0], cc[1], cc[2], cc[3]);
if ((cc[0] == I93_ICODE_CC_MAGIC_NUMER_E1) ||
(cc[0] == I93_ICODE_CC_MAGIC_NUMER_E2)) {
if ((cc[1] & 0xC0) > I93_VERSION_1_x) {
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s - Major mapping version above 1 %d.x", __func__, cc[1] >> 6);
/* major mapping version above 1 not supported */
rw_i93_handle_error(NFC_STATUS_FAILED);
break;
}
if ((cc[1] & I93_ICODE_CC_READ_ACCESS_MASK) ==
I93_ICODE_CC_READ_ACCESS_GRANTED) {
if ((cc[1] & I93_ICODE_CC_WRITE_ACCESS_MASK) !=
I93_ICODE_CC_WRITE_ACCESS_GRANTED) {
/* read-only or password required to write */
p_i93->intl_flags |= RW_I93_FLAG_READ_ONLY;
}
if (cc[3] & I93_ICODE_CC_MBREAD_MASK) {
/* tag supports read multiple blocks command */
p_i93->intl_flags |= RW_I93_FLAG_READ_MULTI_BLOCK;
}
if (cc[3] & I93_ICODE_CC_SPECIAL_FRAME_MASK) {
/* tag supports Special Frame for Write-Alike commands */
p_i93->intl_flags |= RW_I93_FLAG_SPECIAL_FRAME;
}
/* get block size from length of the first READ_SINGLE_BLOCK response
*/
if (length == I93_BLEN_4BYTES)
p_i93->block_size = I93_BLEN_4BYTES;
else if (length == I93_BLEN_8BYTES)
p_i93->block_size = I93_BLEN_8BYTES;
else if (length == I93_BLEN_16BYTES)
p_i93->block_size = I93_BLEN_16BYTES;
else if (length == I93_BLEN_32BYTES)
p_i93->block_size = I93_BLEN_32BYTES;
else {
rw_i93_handle_error(NFC_STATUS_FAILED);
break;
}
/* T5T_Area length = 8 * MLEN */
if (cc[2] == 0) {
/* CC is 8-byte, MLEN is defined by bytes 6 & 7 */
if (length >= I93_BLEN_8BYTES) {
STREAM_TO_ARRAY(&cc[4], p, 4);
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("%s - cc[4-7]: 0x%02X 0x%02X 0x%02X 0x%02X",
__func__, cc[4], cc[5], cc[6], cc[7]);
t5t_area_len = cc[7] + (cc[6] << 8);
t5t_area_len <<= 3;
p_i93->t5t_area_last_offset = t5t_area_len + 8 - 1;
p_i93->max_ndef_length = t5t_area_len;
p_i93->t5t_area_start_block = 1;
memcpy(p_i93->gre_cc_content, cc, 8);
} else {
/* require an additional read to get the second half of the
* CC from block 1 */
if (rw_i93_send_cmd_read_single_block(0x0001, false) ==
NFC_STATUS_OK) {
p_i93->sub_state = RW_I93_SUBSTATE_WAIT_CC_EXT;
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
memcpy(p_i93->gre_cc_content, cc, 4);
break;
}
} else {
/* CC is 4-byte, MLEN is defined by byte 2 */
t5t_area_len = cc[2] << 3;
p_i93->t5t_area_last_offset = t5t_area_len + 4 - 1;
p_i93->t5t_area_start_block = 1;
memcpy(p_i93->gre_cc_content, cc, 4);
}
p_i93->num_block = t5t_area_len / p_i93->block_size;
p_i93->max_ndef_length = t5t_area_len;
if (cc[0] == I93_ICODE_CC_MAGIC_NUMER_E2) {
/* can only be done here if CC is coded over 4 bytes */
p_i93->intl_flags |= RW_I93_FLAG_EXT_COMMANDS;
}
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s - T5T Area size:%d, Nb blocks:0x%04X, "
"Block size:0x%02X, T5T Area last offset:%d",
__func__, t5t_area_len, p_i93->num_block, p_i93->block_size,
p_i93->t5t_area_last_offset);
status = NFC_STATUS_OK;
}
}
if (status == NFC_STATUS_OK) {
/* search NDEF TLV from offset 4 when CC file coded on 4 bytes
* NFC Forum during the next block reading, if CC file is coded
* on more than 4 bytes, start searching for NDEF TLV in the current
* block read except if the CC
*/
if (cc[2] == 0) {
/* 8-byte CC length */
p_i93->rw_offset = 8;
if (length > I93_BLEN_8BYTES) {
/* Rest of the block contains T5T_Area, look for NDEF TLV */
p_i93->sub_state = RW_I93_SUBSTATE_SEARCH_NDEF_TLV;
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_TYPE;
/* start TLV search after CC in the first block */
} else {
/* 8-byte block length, NDEF TLV search must continue
* in the next block */
if (rw_i93_get_next_blocks(p_i93->rw_offset) == NFC_STATUS_OK) {
p_i93->sub_state = RW_I93_SUBSTATE_SEARCH_NDEF_TLV;
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_TYPE;
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
break;
}
} else {
/* 4-byte CC length */
p_i93->rw_offset = 4;
if (length > I93_BLEN_4BYTES) {
/* Rest of the block contains T5T_Area, look for NDEF TLV */
p_i93->sub_state = RW_I93_SUBSTATE_SEARCH_NDEF_TLV;
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_TYPE;
/* start TLV search after CC in the first block */
} else {
if (rw_i93_get_next_blocks(p_i93->rw_offset) == NFC_STATUS_OK) {
p_i93->sub_state = RW_I93_SUBSTATE_SEARCH_NDEF_TLV;
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_TYPE;
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
break;
}
}
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
break;
}
FALLTHROUGH_INTENDED;
case RW_I93_SUBSTATE_SEARCH_NDEF_TLV:
/* search TLV within read blocks */
for (xx = 0; xx < length; xx++) {
/* if looking for type */
if (p_i93->tlv_detect_state == RW_I93_TLV_DETECT_STATE_TYPE) {
if (*(p + xx) == I93_ICODE_TLV_TYPE_NDEF) {
/* store found type and get length field */
p_i93->tlv_type = *(p + xx);
p_i93->ndef_tlv_start_offset = p_i93->rw_offset + xx;
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_LENGTH_1;
} else if (*(p + xx) == I93_ICODE_TLV_TYPE_TERM) {
/* no NDEF TLV found */
p_i93->tlv_type = I93_ICODE_TLV_TYPE_TERM;
break;
} else {
/* TLV with Tag field set as RFU are not interpreted */
p_i93->tlv_type = *(p + xx);
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_LENGTH_1;
}
} else if (p_i93->tlv_detect_state ==
RW_I93_TLV_DETECT_STATE_LENGTH_1) {
/* if 3 bytes length field */
if (*(p + xx) == 0xFF) {
/* need 2 more bytes for length field */
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_LENGTH_2;
} else {
p_i93->tlv_length = *(p + xx);
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_VALUE;
if (p_i93->tlv_type == I93_ICODE_TLV_TYPE_NDEF) {
p_i93->ndef_tlv_last_offset =
p_i93->ndef_tlv_start_offset + 1 + p_i93->tlv_length;
break;
}
}
} else if (p_i93->tlv_detect_state ==
RW_I93_TLV_DETECT_STATE_LENGTH_2) {
/* the second byte of 3 bytes length field */
p_i93->tlv_length = *(p + xx);
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_LENGTH_3;
} else if (p_i93->tlv_detect_state ==
RW_I93_TLV_DETECT_STATE_LENGTH_3) {
/* the last byte of 3 bytes length field */
p_i93->tlv_length = (p_i93->tlv_length << 8) + *(p + xx);
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_VALUE;
if (p_i93->tlv_type == I93_ICODE_TLV_TYPE_NDEF) {
p_i93->ndef_tlv_last_offset =
p_i93->ndef_tlv_start_offset + 3 + p_i93->tlv_length;
break;
}
} else if (p_i93->tlv_detect_state == RW_I93_TLV_DETECT_STATE_VALUE) {
/* this is other than NDEF TLV */
if (p_i93->tlv_length <= length - xx) {
/* skip value field */
xx += (uint8_t)p_i93->tlv_length - 1;
p_i93->tlv_detect_state = RW_I93_TLV_DETECT_STATE_TYPE;
} else {
/* read more data */
p_i93->tlv_length -= (length - xx);
break;
}
}
}
/* found NDEF TLV and read length field */
if ((p_i93->tlv_type == I93_ICODE_TLV_TYPE_NDEF) &&
(p_i93->tlv_detect_state == RW_I93_TLV_DETECT_STATE_VALUE)) {
p_i93->ndef_length = p_i93->tlv_length;
rw_data.ndef.status = NFC_STATUS_OK;
rw_data.ndef.protocol = NFC_PROTOCOL_T5T;
rw_data.ndef.flags = 0;
rw_data.ndef.flags |= RW_NDEF_FL_SUPPORTED;
rw_data.ndef.flags |= RW_NDEF_FL_FORMATED; // NOTYPO
rw_data.ndef.flags |= RW_NDEF_FL_FORMATABLE;
rw_data.ndef.cur_size = p_i93->ndef_length;
if (p_i93->intl_flags & RW_I93_FLAG_READ_ONLY) {
rw_data.ndef.flags |= RW_NDEF_FL_READ_ONLY;
rw_data.ndef.max_size = p_i93->ndef_length;
} else {
rw_data.ndef.flags |= RW_NDEF_FL_HARD_LOCKABLE;
/* Assume tag is already in INITIALIZED or READ-WRITE state */
p_i93->max_ndef_length =
p_i93->t5t_area_last_offset - p_i93->ndef_tlv_start_offset + 1;
if (p_i93->max_ndef_length >= 0xFF) {
/* 3 bytes length field can be used */
p_i93->max_ndef_length -= 4;
} else {
/* 1 byte length field can be used */
p_i93->max_ndef_length -= 2;
}
rw_data.ndef.max_size = p_i93->max_ndef_length;
}
/* Complete the greedy collection */
p_i93->gre_ndef_tlv_pos = p_i93->ndef_tlv_start_offset;
p_i93->gre_ndef_tlv_length = p_i93->ndef_length;
p_i93->gre_validity = 1;
p_i93->state = RW_I93_STATE_IDLE;
p_i93->sent_cmd = 0;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s - NDEF cur_size (%d), max_size (%d), flags (0x%x)", __func__,
rw_data.ndef.cur_size, rw_data.ndef.max_size, rw_data.ndef.flags);
(*(rw_cb.p_cback))(RW_I93_NDEF_DETECT_EVT, &rw_data);
break;
} else {
/* read more data */
p_i93->rw_offset += length;
if (p_i93->rw_offset > p_i93->t5t_area_last_offset) {
rw_i93_handle_error(NFC_STATUS_FAILED);
} else {
if (rw_i93_get_next_blocks(p_i93->rw_offset) == NFC_STATUS_OK) {
p_i93->sub_state = RW_I93_SUBSTATE_SEARCH_NDEF_TLV;
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
}
}
break;
default:
break;
}
}
/*******************************************************************************
**
** Function rw_t5t_sm_update_ndef
**
** Description Process NDEF update procedure
**
** 1. Set length field to zero
** 2. Write NDEF and Terminator TLV
** 3. Set length field to NDEF length
**
** Returns void
**
*******************************************************************************/
void rw_t5t_sm_update_ndef(NFC_HDR* p_resp) {
uint8_t* p = (uint8_t*)(p_resp + 1) + p_resp->offset;
uint8_t flags, xx, buff[I93_MAX_BLOCK_LENGH];
uint16_t length_offset;
uint16_t length = p_resp->len, block_number;
tRW_I93_CB* p_i93 = &rw_cb.tcb.i93;
tRW_DATA rw_data;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s - sub_state:%s (0x%x)", __func__,
rw_i93_get_sub_state_name(p_i93->sub_state).c_str(), p_i93->sub_state);
if (length == 0 || p_i93->block_size > I93_MAX_BLOCK_LENGH) {
android_errorWriteLog(0x534e4554, "122320256");
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
STREAM_TO_UINT8(flags, p);
length--;
if (flags & I93_FLAG_ERROR_DETECTED) {
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("%s - Got error flags (0x%02x)", __func__, flags);
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
switch (p_i93->sub_state) {
case RW_I93_SUBSTATE_RESET_LEN:
/* get offset of length field */
length_offset = (p_i93->ndef_tlv_start_offset + 1) % p_i93->block_size;
if (length < length_offset) {
android_errorWriteLog(0x534e4554, "122320256");
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
/* set length to zero */
*(p + length_offset) = 0x00;
if (p_i93->ndef_length > 0) {
/* if 3 bytes length field is needed */
if (p_i93->ndef_length >= 0xFF) {
xx = length_offset + 3;
} else {
xx = length_offset + 1;
}
if (p_i93->ndef_length >= 0xFF) {
p_i93->ndef_tlv_last_offset = p_i93->ndef_tlv_start_offset + 3;
} else {
p_i93->ndef_tlv_last_offset = p_i93->ndef_tlv_start_offset + 1;
}
/* write the first part of NDEF in the same block */
for (; xx < p_i93->block_size; xx++) {
if (xx > length || p_i93->rw_length > p_i93->ndef_length) {
android_errorWriteLog(0x534e4554, "122320256");
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
if (p_i93->rw_length < p_i93->ndef_length) {
*(p + xx) = *(p_i93->p_update_data + p_i93->rw_length++);
p_i93->ndef_tlv_last_offset++;
} else {
*(p + xx) = I93_ICODE_TLV_TYPE_NULL;
}
}
}
block_number = (p_i93->ndef_tlv_start_offset + 1) / p_i93->block_size;
if (rw_i93_send_cmd_write_single_block(block_number, p) ==
NFC_STATUS_OK) {
/* update next writing offset */
p_i93->rw_offset = (block_number + 1) * p_i93->block_size;
p_i93->sub_state = RW_I93_SUBSTATE_WRITE_NDEF;
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
break;
case RW_I93_SUBSTATE_WRITE_NDEF:
/* if it's not the end of tag memory */
if (p_i93->rw_offset <= p_i93->t5t_area_last_offset) {
// Note: Needed to write in the last block of the memory
block_number = p_i93->rw_offset / p_i93->block_size;
/* if we have more data to write */
if (p_i93->rw_length < p_i93->ndef_length) {
p = p_i93->p_update_data + p_i93->rw_length;
p_i93->rw_offset += p_i93->block_size;
p_i93->rw_length += p_i93->block_size;
/* if this is the last block of NDEF TLV */
if (p_i93->rw_length >= p_i93->ndef_length) {
/* length of NDEF TLV in the block */
xx = (uint8_t)(p_i93->block_size -
(p_i93->rw_length - p_i93->ndef_length));
/* set NULL TLV in the unused part of block */
memset(buff, I93_ICODE_TLV_TYPE_NULL, p_i93->block_size);
memcpy(buff, p, xx);
p = buff;
/* if it's the end of tag memory */
if (((p_i93->rw_offset - p_i93->block_size + xx) <=
p_i93->t5t_area_last_offset) &&
(xx < p_i93->block_size)) {
buff[xx] = I93_ICODE_TLV_TYPE_TERM;
}
// Note: Needed to write Terminator TLV in block following
// the NDEF Message ending in byte 3 of the previous block
p_i93->ndef_tlv_last_offset =
p_i93->rw_offset - p_i93->block_size + xx - 1;
} else {
p_i93->ndef_tlv_last_offset += p_i93->block_size;
}
if (rw_i93_send_cmd_write_single_block(block_number, p) !=
NFC_STATUS_OK) {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
} else {
/* if this is the very next block of NDEF TLV */
// Note: Needed to write Terminator TLV in block following
// the NDEF Message ending in byte 3 of the previous block
if ((block_number ==
(p_i93->ndef_tlv_last_offset / p_i93->block_size) + 1) &&
(((p_i93->ndef_tlv_last_offset + 1) % p_i93->block_size) == 0)) {
/* write Terminator TLV and NULL TLV */
memset(buff, I93_ICODE_TLV_TYPE_NULL, p_i93->block_size);
buff[0] = I93_ICODE_TLV_TYPE_TERM;
p = buff;
if (rw_i93_send_cmd_write_single_block(block_number, p) !=
NFC_STATUS_OK) {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
block_number =
(p_i93->ndef_tlv_start_offset + 1) / p_i93->block_size;
/* set offset to length field */
p_i93->rw_offset = p_i93->ndef_tlv_start_offset + 1;
/* get size of length field */
if (p_i93->ndef_length >= 0xFF) {
p_i93->rw_length = 3;
} else if (p_i93->ndef_length > 0) {
p_i93->rw_length = 1;
} else {
p_i93->rw_length = 0;
}
p_i93->sub_state = RW_I93_SUBSTATE_UPDATE_LEN;
} else {
/* finished writing NDEF and Terminator TLV */
/* read length field to update length */
block_number =
(p_i93->ndef_tlv_start_offset + 1) / p_i93->block_size;
if (rw_i93_send_cmd_read_single_block(block_number, false) ==
NFC_STATUS_OK) {
/* set offset to length field */
p_i93->rw_offset = p_i93->ndef_tlv_start_offset + 1;
/* get size of length field */
if (p_i93->ndef_length >= 0xFF) {
p_i93->rw_length = 3;
} else if (p_i93->ndef_length > 0) {
p_i93->rw_length = 1;
} else {
p_i93->rw_length = 0;
}
p_i93->sub_state = RW_I93_SUBSTATE_UPDATE_LEN;
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
}
}
} /* (p_i93->rw_offset <= p_i93->t5t_area_last_offset) */
else {
/* if we have no more data to write */
if (p_i93->rw_length >= p_i93->ndef_length) {
/* finished writing NDEF and Terminator TLV */
/* read length field to update length */
block_number = (p_i93->ndef_tlv_start_offset + 1) / p_i93->block_size;
if (rw_i93_send_cmd_read_single_block(block_number, false) ==
NFC_STATUS_OK) {
/* set offset to length field */
p_i93->rw_offset = p_i93->ndef_tlv_start_offset + 1;
/* get size of length field */
if (p_i93->ndef_length >= 0xFF) {
p_i93->rw_length = 3;
} else if (p_i93->ndef_length > 0) {
p_i93->rw_length = 1;
} else {
p_i93->rw_length = 0;
}
p_i93->sub_state = RW_I93_SUBSTATE_UPDATE_LEN;
break;
}
}
rw_i93_handle_error(NFC_STATUS_FAILED);
}
break;
case RW_I93_SUBSTATE_UPDATE_LEN:
/* if we have more length field to write */
if (p_i93->rw_length > 0) {
/* if we got ack for writing, read next block to update rest of length
* field */
if (length == 0) {
block_number = p_i93->rw_offset / p_i93->block_size;
if (rw_i93_send_cmd_read_single_block(block_number, false) !=
NFC_STATUS_OK) {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
} else {
length_offset = p_i93->rw_offset % p_i93->block_size;
/* update length field within the read block */
for (xx = length_offset; xx < p_i93->block_size; xx++) {
if (xx > length) {
android_errorWriteLog(0x534e4554, "122320256");
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
if (p_i93->rw_length == 3)
*(p + xx) = 0xFF;
else if (p_i93->rw_length == 2)
*(p + xx) = (uint8_t)((p_i93->ndef_length >> 8) & 0xFF);
else if (p_i93->rw_length == 1)
*(p + xx) = (uint8_t)(p_i93->ndef_length & 0xFF);
p_i93->rw_length--;
if (p_i93->rw_length == 0) break;
}
block_number = (p_i93->rw_offset / p_i93->block_size);
if (rw_i93_send_cmd_write_single_block(block_number, p) ==
NFC_STATUS_OK) {
/* set offset to the beginning of next block */
p_i93->rw_offset +=
p_i93->block_size - (p_i93->rw_offset % p_i93->block_size);
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
}
} else {
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s - NDEF update complete, %d bytes, (%d-%d)", __func__,
p_i93->ndef_length, p_i93->ndef_tlv_start_offset,
p_i93->ndef_tlv_last_offset);
p_i93->state = RW_I93_STATE_IDLE;
p_i93->sent_cmd = 0;
p_i93->p_update_data = nullptr;
rw_data.status = NFC_STATUS_OK;
(*(rw_cb.p_cback))(RW_I93_NDEF_UPDATE_CPLT_EVT, &rw_data);
}
break;
default:
break;
}
}
/*******************************************************************************
**
** Function rw_i93_sm_set_read_only
**
** Description Process read-only procedure
**
** 1. Update CC as read-only
** 2. Lock all block of NDEF TLV
** 3. Lock block of CC
**
** Returns void
**
*******************************************************************************/
void rw_t5t_sm_set_read_only(NFC_HDR* p_resp) {
uint8_t* p = (uint8_t*)(p_resp + 1) + p_resp->offset;
uint16_t block_number;
uint8_t flags;
uint16_t length = p_resp->len;
tRW_I93_CB* p_i93 = &rw_cb.tcb.i93;
tRW_DATA rw_data;
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s - sub_state:%s (0x%x)", __func__,
rw_i93_get_sub_state_name(p_i93->sub_state).c_str(), p_i93->sub_state);
if (length == 0) {
android_errorWriteLog(0x534e4554, "122322613");
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
STREAM_TO_UINT8(flags, p);
length--;
if (flags & I93_FLAG_ERROR_DETECTED) {
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("%s - Got error flags (0x%02x)", __func__, flags);
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
switch (p_i93->sub_state) {
case RW_I93_SUBSTATE_WAIT_CC:
if (length < RW_I93_CC_SIZE) {
android_errorWriteLog(0x534e4554, "139188579");
rw_i93_handle_error(NFC_STATUS_FAILED);
return;
}
/* mark CC as read-only */
*(p + 1) |= I93_ICODE_CC_READ_ONLY;
if (rw_i93_send_cmd_write_single_block(0, p) == NFC_STATUS_OK) {
p_i93->sub_state = RW_I93_SUBSTATE_WAIT_UPDATE_CC;
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
break;
case RW_I93_SUBSTATE_WAIT_UPDATE_CC:
/* successfully write CC then lock CC and all blocks of T5T Area */
p_i93->rw_offset = 0;
if (rw_i93_send_cmd_lock_block(0) == NFC_STATUS_OK) {
p_i93->rw_offset += p_i93->block_size;
p_i93->sub_state = RW_I93_SUBSTATE_LOCK_T5T_AREA;
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
break;
case RW_I93_SUBSTATE_LOCK_T5T_AREA:
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s - rw_offset:0x%02x, t5t_area_last_offset:0x%02x", __func__,
p_i93->rw_offset, p_i93->t5t_area_last_offset);
/* 2nd block to be locked can be the last 4 bytes of CC in case CC
* is 8byte long, then T5T_Area starts */
if (p_i93->rw_offset <= p_i93->t5t_area_last_offset) {
if (p_i93->block_size == 0) {
LOG(ERROR) << StringPrintf("%s - zero block_size error", __func__);
rw_i93_handle_error(NFC_STATUS_FAILED);
break;
}
/* get the next block of NDEF TLV */
block_number = (uint16_t)(p_i93->rw_offset / p_i93->block_size);
if (rw_i93_send_cmd_lock_block(block_number) == NFC_STATUS_OK) {
p_i93->rw_offset += p_i93->block_size;
} else {
rw_i93_handle_error(NFC_STATUS_FAILED);
}
} else {
p_i93->intl_flags |= RW_I93_FLAG_READ_ONLY;
p_i93->state = RW_I93_STATE_IDLE;
p_i93->sent_cmd = 0;
rw_data.status = NFC_STATUS_OK;
(*(rw_cb.p_cback))(RW_I93_SET_TAG_RO_EVT, &rw_data);
}
break;
default:
break;
}
}