| /****************************************************************************** |
| * |
| * 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 <log/log.h> |
| #include <string.h> |
| |
| #include <android-base/stringprintf.h> |
| #include <base/logging.h> |
| |
| #include "nfc_target.h" |
| |
| #include "bt_types.h" |
| #include "nfc_api.h" |
| #include "nfc_int.h" |
| #include "rw_api.h" |
| #include "rw_int.h" |
| |
| using android::base::StringPrintf; |
| |
| extern bool nfc_debug_enabled; |
| |
| /* main state */ |
| /* T4T is not activated */ |
| #define RW_T4T_STATE_NOT_ACTIVATED 0x00 |
| /* waiting for upper layer API */ |
| #define RW_T4T_STATE_IDLE 0x01 |
| /* performing NDEF detection precedure */ |
| #define RW_T4T_STATE_DETECT_NDEF 0x02 |
| /* performing read NDEF procedure */ |
| #define RW_T4T_STATE_READ_NDEF 0x03 |
| /* performing update NDEF procedure */ |
| #define RW_T4T_STATE_UPDATE_NDEF 0x04 |
| /* checking presence of tag */ |
| #define RW_T4T_STATE_PRESENCE_CHECK 0x05 |
| /* convert tag to read only */ |
| #define RW_T4T_STATE_SET_READ_ONLY 0x06 |
| |
| /* performing NDEF format */ |
| #define RW_T4T_STATE_NDEF_FORMAT 0x07 |
| |
| /* sub state */ |
| /* waiting for response of selecting AID */ |
| #define RW_T4T_SUBSTATE_WAIT_SELECT_APP 0x00 |
| /* waiting for response of selecting CC */ |
| #define RW_T4T_SUBSTATE_WAIT_SELECT_CC 0x01 |
| /* waiting for response of reading CC */ |
| #define RW_T4T_SUBSTATE_WAIT_CC_FILE 0x02 |
| /* waiting for response of selecting NDEF */ |
| #define RW_T4T_SUBSTATE_WAIT_SELECT_NDEF_FILE 0x03 |
| /* waiting for response of reading NLEN */ |
| #define RW_T4T_SUBSTATE_WAIT_READ_NLEN 0x04 |
| /* waiting for response of reading file */ |
| #define RW_T4T_SUBSTATE_WAIT_READ_RESP 0x05 |
| /* waiting for response of updating file */ |
| #define RW_T4T_SUBSTATE_WAIT_UPDATE_RESP 0x06 |
| /* waiting for response of updating NLEN */ |
| #define RW_T4T_SUBSTATE_WAIT_UPDATE_NLEN 0x07 |
| /* waiting for response of updating CC */ |
| #define RW_T4T_SUBSTATE_WAIT_UPDATE_CC 0x08 |
| |
| #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 |
| |
| static std::string rw_t4t_get_state_name(uint8_t state); |
| static std::string rw_t4t_get_sub_state_name(uint8_t sub_state); |
| |
| static bool rw_t4t_send_to_lower(NFC_HDR* p_c_apdu); |
| static bool rw_t4t_select_file(uint16_t file_id); |
| static bool rw_t4t_read_file(uint16_t offset, uint16_t length, |
| bool is_continue); |
| static bool rw_t4t_update_nlen(uint16_t ndef_len); |
| static bool rw_t4t_update_file(void); |
| static bool rw_t4t_update_cc_to_readonly(void); |
| static bool rw_t4t_select_application(uint8_t version); |
| static bool rw_t4t_validate_cc_file(void); |
| |
| static bool rw_t4t_get_hw_version(void); |
| static bool rw_t4t_get_sw_version(void); |
| static bool rw_t4t_create_app(void); |
| static bool rw_t4t_select_app(void); |
| static bool rw_t4t_create_ccfile(void); |
| static bool rw_t4t_create_ndef(void); |
| static bool rw_t4t_write_cc(void); |
| static bool rw_t4t_write_ndef(void); |
| static void rw_t4t_handle_error(tNFC_STATUS status, uint8_t sw1, uint8_t sw2); |
| static void rw_t4t_sm_detect_ndef(NFC_HDR* p_r_apdu); |
| static void rw_t4t_sm_read_ndef(NFC_HDR* p_r_apdu); |
| static void rw_t4t_sm_update_ndef(NFC_HDR* p_r_apdu); |
| static void rw_t4t_sm_set_readonly(NFC_HDR* p_r_apdu); |
| static void rw_t4t_data_cback(uint8_t conn_id, tNFC_CONN_EVT event, |
| tNFC_CONN* p_data); |
| static void rw_t4t_sm_ndef_format(NFC_HDR* p_r_apdu); |
| |
| /******************************************************************************* |
| ** |
| ** Function rw_t4t_send_to_lower |
| ** |
| ** Description Send C-APDU to lower layer |
| ** |
| ** Returns TRUE if success |
| ** |
| *******************************************************************************/ |
| static bool rw_t4t_send_to_lower(NFC_HDR* p_c_apdu) { |
| if (NFC_SendData(NFC_RF_CONN_ID, p_c_apdu) != NFC_STATUS_OK) { |
| LOG(ERROR) << StringPrintf("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 bool rw_t4t_get_hw_version(void) { |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_get_sw_version(void) { |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_update_version_details(NFC_HDR* p_r_apdu) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| uint8_t* p; |
| uint16_t major_version, minor_version; |
| |
| if (p_r_apdu->len < T4T_DES_GET_VERSION_LEN) { |
| LOG(ERROR) << StringPrintf("%s incorrect p_r_apdu length", __func__); |
| android_errorWriteLog(0x534e4554, "120865977"); |
| return false; |
| } |
| |
| p = (uint8_t*)(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 bool rw_t4t_get_uid_details(void) { |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_create_app(void) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| uint8_t df_name[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01}; |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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, (int)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 bool rw_t4t_select_app(void) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_create_ccfile(void) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_create_ndef(void) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_write_cc(void) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| uint8_t CCFileBytes[] = {0x00, 0x0F, 0x10, 0x00, 0x3B, 0x00, 0x34, 0x04, |
| 0x06, 0xE1, 0x04, 0x04, 0x00, 0x00, 0x00}; |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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_t)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, (int)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 bool rw_t4t_write_ndef(void) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_select_file(uint16_t file_id) { |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("File ID:0x%04X", file_id); |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_read_file(uint16_t offset, uint16_t length, |
| bool is_continue) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "offset:%d, length:%d, is_continue:%d, ", offset, length, is_continue); |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("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_t)(p_t4t->max_read_size); |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_update_nlen(uint16_t ndef_len) { |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("NLEN:%d", ndef_len); |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_update_file(void) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| uint16_t length; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "rw_offset:%d, rw_length:%d", p_t4t->rw_offset, p_t4t->rw_length); |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("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_t)(p_t4t->max_update_size); |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_update_cc_to_readonly(void) { |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("Remove Write access from CC"); |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_select_application(uint8_t version) { |
| NFC_HDR* p_c_apdu; |
| uint8_t* p; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("version:0x%X", version); |
| |
| p_c_apdu = (NFC_HDR*)GKI_getpoolbuf(NFC_RW_POOL_ID); |
| |
| if (!p_c_apdu) { |
| LOG(ERROR) << StringPrintf("Cannot allocate buffer"); |
| return false; |
| } |
| |
| p_c_apdu->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p = (uint8_t*)(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 bool rw_t4t_validate_cc_file(void) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << __func__; |
| |
| if (p_t4t->cc_file.cclen < T4T_CC_FILE_MIN_LEN) { |
| LOG(ERROR) << StringPrintf("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)) { |
| LOG(ERROR) << StringPrintf( |
| "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) { |
| LOG(ERROR) << StringPrintf("MaxLe (%d) is too small", |
| p_t4t->cc_file.max_le); |
| return false; |
| } |
| |
| if (p_t4t->cc_file.max_lc < 0x0001) { |
| LOG(ERROR) << StringPrintf("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)) { |
| LOG(ERROR) << StringPrintf("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)) { |
| LOG(ERROR) << StringPrintf("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) { |
| LOG(ERROR) << StringPrintf("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_WRITE_ACCESS_PROP_START)) { |
| LOG(ERROR) << StringPrintf("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_t sw1, uint8_t sw2) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| tRW_DATA rw_data; |
| tRW_EVENT event; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "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; |
| rw_data.ndef.cur_size = 0; |
| rw_data.ndef.max_size = 0; |
| |
| 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(NFC_HDR* p_r_apdu) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| uint8_t* p; |
| uint16_t status_words; |
| tRW_DATA rw_data; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "sub_state:%s (%d)", rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(), |
| p_t4t->sub_state); |
| |
| /* get status words */ |
| p = (uint8_t*)(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); |
| |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("Sent RW_T4T_NDEF_FORMAT_CPLT_EVT"); |
| } |
| } |
| break; |
| |
| default: |
| LOG(ERROR) << StringPrintf("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(NFC_HDR* p_r_apdu) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| uint8_t *p, type, length; |
| uint16_t status_words, nlen; |
| tRW_DATA rw_data; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "sub_state:%s (%d)", rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(), |
| p_t4t->sub_state); |
| |
| /* get status words */ |
| p = (uint8_t*)(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; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("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_t*)(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); |
| |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("Capability Container (CC) file"); |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf(" CCLEN: 0x%04X", p_t4t->cc_file.cclen); |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf(" Version:0x%02X", p_t4t->cc_file.version); |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf(" MaxLe: 0x%04X", p_t4t->cc_file.max_le); |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf(" MaxLc: 0x%04X", p_t4t->cc_file.max_lc); |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf(" NDEF File Control TLV"); |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| " FileID: 0x%04X", p_t4t->cc_file.ndef_fc.file_id); |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| " MaxFileSize: 0x%04X", p_t4t->cc_file.ndef_fc.max_file_size); |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| " ReadAccess: 0x%02X", p_t4t->cc_file.ndef_fc.read_access); |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| " WriteAccess: 0x%02X", p_t4t->cc_file.ndef_fc.write_access); |
| |
| 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_t*)(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_t)(p_t4t->cc_file.ndef_fc.max_file_size - |
| (uint16_t)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); |
| |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("Sent RW_T4T_NDEF_DETECT_EVT"); |
| } |
| } else { |
| /* NLEN should be less than max file size */ |
| LOG(ERROR) << StringPrintf( |
| "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 */ |
| LOG(ERROR) << StringPrintf( |
| "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: |
| LOG(ERROR) << StringPrintf("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(NFC_HDR* p_r_apdu) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| uint8_t* p; |
| uint16_t status_words; |
| tRW_DATA rw_data; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "sub_state:%s (%d)", rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(), |
| p_t4t->sub_state); |
| |
| /* get status words */ |
| p = (uint8_t*)(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); |
| |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("Sent RW_T4T_NDEF_READ_CPLT_EVT"); |
| } |
| |
| p_r_apdu = nullptr; |
| } else { |
| p_t4t->rw_length = 0; |
| p_t4t->state = RW_T4T_STATE_IDLE; |
| } |
| } else { |
| LOG(ERROR) << StringPrintf( |
| "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: |
| LOG(ERROR) << StringPrintf("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(NFC_HDR* p_r_apdu) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| uint8_t* p; |
| uint16_t status_words; |
| tRW_DATA rw_data; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "sub_state:%s (%d)", rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(), |
| p_t4t->sub_state); |
| |
| /* Get status words */ |
| p = (uint8_t*)(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 = nullptr; |
| } |
| } 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); |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("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 = nullptr; |
| } |
| } else { |
| p_t4t->p_update_data = nullptr; |
| |
| /* 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: |
| LOG(ERROR) << StringPrintf("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(NFC_HDR* p_r_apdu) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| uint8_t* p; |
| uint16_t status_words; |
| tRW_DATA rw_data; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "sub_state:%s (%d)", rw_t4t_get_sub_state_name(p_t4t->sub_state).c_str(), |
| p_t4t->sub_state); |
| |
| /* Get status words */ |
| p = (uint8_t*)(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; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("Sent RW_T4T_SET_TO_RO_EVT"); |
| (*(rw_cb.p_cback))(RW_T4T_SET_TO_RO_EVT, &rw_data); |
| } |
| break; |
| |
| default: |
| LOG(ERROR) << StringPrintf("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) { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("event=%d", p_tle->event); |
| |
| if (p_tle->event == NFC_TTYPE_RW_T4T_RESPONSE) { |
| rw_t4t_handle_error(NFC_STATUS_TIMEOUT, 0, 0); |
| } else { |
| LOG(ERROR) << StringPrintf("unknown event=%d", p_tle->event); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function rw_t4t_handle_isodep_nak_rsp |
| ** |
| ** Description This function handles the response and ntf . |
| ** |
| ** Returns none |
| ** |
| *******************************************************************************/ |
| void rw_t4t_handle_isodep_nak_rsp(uint8_t status, bool is_ntf) { |
| tRW_DATA rw_data; |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("rw_t4t_handle_isodep_nak_rsp %d", status); |
| if (is_ntf || (status != NFC_STATUS_OK)) { |
| rw_data.status = status; |
| nfc_stop_quick_timer(&p_t4t->timer); |
| p_t4t->state = RW_T4T_STATE_IDLE; |
| (*(rw_cb.p_cback))(RW_T4T_PRESENCE_CHECK_EVT, &rw_data); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function rw_t4t_data_cback |
| ** |
| ** Description This callback function receives the data from NFCC. |
| ** |
| ** Returns none |
| ** |
| *******************************************************************************/ |
| static void rw_t4t_data_cback(__attribute__((unused)) uint8_t conn_id, |
| tNFC_CONN_EVT event, tNFC_CONN* p_data) { |
| tRW_T4T_CB* p_t4t = &rw_cb.tcb.t4t; |
| NFC_HDR* p_r_apdu; |
| tRW_DATA rw_data; |
| |
| uint8_t begin_state = p_t4t->state; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("event = 0x%X", event); |
| nfc_stop_quick_timer(&p_t4t->timer); |
| |
| switch (event) { |
| case NFC_DEACTIVATE_CEVT: |
| NFC_SetStaticRfCback(nullptr); |
| 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_data.status = (tNFC_STATUS)(*(uint8_t*)p_data); |
| rw_t4t_handle_error(rw_data.status, 0, 0); |
| } else { |
| p_t4t->state = RW_T4T_STATE_IDLE; |
| rw_data.status = (tNFC_STATUS)(*(uint8_t*)p_data); |
| (*(rw_cb.p_cback))(RW_T4T_INTF_ERROR_EVT, &rw_data); |
| } |
| return; |
| |
| case NFC_DATA_CEVT: |
| p_r_apdu = (NFC_HDR*)p_data->data.p_data; |
| break; |
| |
| default: |
| return; |
| } |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "RW T4T state: <%s (%d)>", rw_t4t_get_state_name(p_t4t->state).c_str(), |
| p_t4t->state); |
| |
| if (p_t4t->state != RW_T4T_STATE_IDLE && |
| p_t4t->state != RW_T4T_STATE_PRESENCE_CHECK && |
| p_r_apdu->len < T4T_RSP_STATUS_WORDS_SIZE) { |
| LOG(ERROR) << StringPrintf("%s incorrect p_r_apdu length", __func__); |
| android_errorWriteLog(0x534e4554, "120865977"); |
| rw_t4t_handle_error(NFC_STATUS_FAILED, 0, 0); |
| GKI_freebuf(p_r_apdu); |
| return; |
| } |
| |
| switch (p_t4t->state) { |
| case RW_T4T_STATE_IDLE: |
| /* Unexpected R-APDU, it should be raw frame response */ |
| /* forward to upper layer without parsing */ |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "RW T4T Raw Frame: Len [0x%X] Status [%s]", p_r_apdu->len, |
| NFC_GetStatusName(p_data->data.status).c_str()); |
| 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 = nullptr; |
| } 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: |
| LOG(ERROR) << StringPrintf("invalid state=%d", p_t4t->state); |
| GKI_freebuf(p_r_apdu); |
| break; |
| } |
| |
| if (begin_state != p_t4t->state) { |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("RW T4T state changed:<%s> -> <%s>", |
| rw_t4t_get_state_name(begin_state).c_str(), |
| rw_t4t_get_state_name(p_t4t->state).c_str()); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function RW_T4tFormatNDef |
| ** |
| ** Description format T4T tag |
| ** |
| ** Returns NFC_STATUS_OK if success |
| ** |
| *******************************************************************************/ |
| tNFC_STATUS RW_T4tFormatNDef(void) { |
| DLOG_IF(INFO, nfc_debug_enabled) << __func__; |
| |
| if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE) { |
| LOG(ERROR) << StringPrintf("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; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << __func__; |
| |
| 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) { |
| DLOG_IF(INFO, nfc_debug_enabled) << __func__; |
| |
| if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE) { |
| LOG(ERROR) << StringPrintf("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) { |
| DLOG_IF(INFO, nfc_debug_enabled) << __func__; |
| |
| if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE) { |
| LOG(ERROR) << StringPrintf("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 { |
| LOG(ERROR) << StringPrintf("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_t length, uint8_t* p_data) { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("length:%d", length); |
| |
| if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE) { |
| LOG(ERROR) << StringPrintf("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) { |
| LOG(ERROR) << StringPrintf("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) { |
| LOG(ERROR) << StringPrintf( |
| "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 { |
| LOG(ERROR) << StringPrintf("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_t option) { |
| tNFC_STATUS retval = NFC_STATUS_OK; |
| tRW_DATA evt_data; |
| bool status; |
| NFC_HDR* p_data; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("%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 */ |
| p_data = (NFC_HDR*)GKI_getbuf(sizeof(NFC_HDR) + NCI_MSG_OFFSET_SIZE + |
| NCI_DATA_HDR_SIZE); |
| if (p_data != nullptr) { |
| p_data->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| p_data->len = 0; |
| if (NFC_SendData(NFC_RF_CONN_ID, (NFC_HDR*)p_data) == NFC_STATUS_OK) |
| status = true; |
| } |
| } else if (option == RW_T4T_CHK_ISO_DEP_NAK_PRES_CHK) { |
| if (NFC_ISODEPNakPresCheck() == NFC_STATUS_OK) status = true; |
| } |
| |
| 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; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << __func__; |
| |
| if (rw_cb.tcb.t4t.state != RW_T4T_STATE_IDLE) { |
| LOG(ERROR) << StringPrintf("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) { |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("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 { |
| LOG(ERROR) << StringPrintf("No NDEF detected"); |
| return NFC_STATUS_FAILED; |
| } |
| return (retval); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 std::string rw_t4t_get_state_name(uint8_t 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 std::string rw_t4t_get_sub_state_name(uint8_t 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"; |
| } |
| } |