/* * Copyright (C) 2012-2019 NXP Semiconductors * * 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. */ #include #include #include /* Timeout value to wait for NFC-DEP detection.*/ #define CUSTOM_POLL_TIMEOUT 160 #define CLEAN_UP_TIMEOUT 250 #define MAX_WRITE_RETRY 5 #define MAX_POLL_CMD_LEN 64 #define NCI_HEADER_SIZE 3 /******************* Global variables *****************************************/ extern phNxpNciHal_Control_t nxpncihal_ctrl; extern NFCSTATUS phNxpNciHal_send_ext_cmd(uint16_t cmd_len, uint8_t* p_cmd); static uint8_t cmd_stop_rf_discovery[] = {0x21, 0x06, 0x01, 0x00}; /* IDLE */ static uint8_t cmd_resume_rf_discovery[] = {0x21, 0x06, 0x01, 0x03}; /* RF_DISCOVER */ /*RF_DISCOVER_SELECT_CMD*/ static uint8_t cmd_select_rf_discovery[] = {0x21, 0x04, 0x03, 0x01, 0x04, 0x02}; static uint8_t cmd_poll[MAX_POLL_CMD_LEN]; static uint8_t cmd_poll_len = 0; int discover_type = 0xFF; uint32_t cleanup_timer; /*PRIO LOGIC related dead functions undefined*/ #ifdef P2P_PRIO_LOGIC_HAL_IMP static int iso_dep_detected = 0x00; static int poll_timer_fired = 0x00; static uint8_t bIgnorep2plogic = 0; static uint8_t* p_iso_ntf_buff = NULL; /* buffer to store second notification */ static uint8_t bIgnoreIsoDep = 0; static uint32_t custom_poll_timer; /************** NFC-DEP SW PRIO functions *************************************/ static NFCSTATUS phNxpNciHal_start_polling_loop(void); static NFCSTATUS phNxpNciHal_stop_polling_loop(void); static NFCSTATUS phNxpNciHal_resume_polling_loop(void); static void phNxpNciHal_NfcDep_store_ntf(uint8_t* p_cmd_data, uint16_t cmd_len); /******************************************************************************* ** ** Function cleanup_timer_handler ** ** Description Callback function for cleanup timer. ** ** Returns None ** *******************************************************************************/ static void cleanup_timer_handler(uint32_t timerId, void* pContext) { NXPLOG_NCIHAL_D(">> cleanup_timer_handler."); NXPLOG_NCIHAL_D( ">> cleanup_timer_handler. ISO_DEP not detected second time."); phOsalNfc_Timer_Delete(cleanup_timer); cleanup_timer = 0; iso_dep_detected = 0x00; EnableP2P_PrioLogic = false; return; } /******************************************************************************* ** ** Function custom_poll_timer_handler ** ** Description Callback function for custom poll timer. ** ** Returns None ** *******************************************************************************/ static void custom_poll_timer_handler(uint32_t timerId, void* pContext) { NXPLOG_NCIHAL_D(">> custom_poll_timer_handler."); NXPLOG_NCIHAL_D( ">> custom_poll_timer_handler. NFC_DEP not detected. so giving early " "chance to ISO_DEP."); phOsalNfc_Timer_Delete(custom_poll_timer); if (iso_dep_detected == 0x01) { poll_timer_fired = 0x01; /* * Restart polling loop. * When the polling loop is stopped, polling will be restarted. */ NXPLOG_NCIHAL_D(">> custom_poll_timer_handler - restart polling loop."); phNxpNciHal_stop_polling_loop(); } else { NXPLOG_NCIHAL_D( ">> custom_poll_timer_handler - invalid flag state (iso_dep_detected)"); } return; } /******************************************************************************* ** ** Function phNxpNciHal_stop_polling_loop ** ** Description Sends stop polling cmd to NFCC ** ** Returns NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED ** *******************************************************************************/ static NFCSTATUS phNxpNciHal_stop_polling_loop() { NFCSTATUS status = NFCSTATUS_SUCCESS; phNxpNciHal_Sem_t cb_data; pthread_t pthread; discover_type = STOP_POLLING; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (pthread_create(&pthread, &attr, tmp_thread, (void*)&discover_type) != 0) { NXPLOG_NCIHAL_E("fail to create pthread"); } pthread_attr_destroy(&attr); return status; } /******************************************************************************* ** ** Function phNxpNciHal_resume_polling_loop ** ** Description Sends resume polling cmd to NFCC ** ** Returns NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED ** *******************************************************************************/ static NFCSTATUS phNxpNciHal_resume_polling_loop() { NFCSTATUS status = NFCSTATUS_SUCCESS; phNxpNciHal_Sem_t cb_data; pthread_t pthread; discover_type = RESUME_POLLING; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (pthread_create(&pthread, &attr, tmp_thread, (void*)&discover_type) != 0) { NXPLOG_NCIHAL_E("fail to create pthread"); } pthread_attr_destroy(&attr); return status; } /******************************************************************************* ** ** Function phNxpNciHal_start_polling_loop ** ** Description Sends start polling cmd to NFCC ** ** Returns NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED ** *******************************************************************************/ NFCSTATUS phNxpNciHal_start_polling_loop() { NFCSTATUS status = NFCSTATUS_FAILED; phNxpNciHal_Sem_t cb_data; pthread_t pthread; discover_type = START_POLLING; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (pthread_create(&pthread, &attr, tmp_thread, (void*)&discover_type) != 0) { NXPLOG_NCIHAL_E("fail to create pthread"); } pthread_attr_destroy(&attr); return status; } /******************************************************************************* ** ** Function phNxpNciHal_NfcDep_rsp_ext ** ** Description Implements algorithm for NFC-DEP protocol priority over ** ISO-DEP protocol. ** Following the algorithm: ** IF ISO-DEP detected first time,set the ISO-DEP detected flag ** and resume polling loop with 60ms timeout value. ** a) if than NFC-DEP detected than send the response to ** libnfc-nci stack and stop the timer. ** b) if NFC-DEP not detected with in 60ms, than restart ** the polling loop to give early chance to ISO-DEP with ** a cleanup timer. ** c) if ISO-DEP detected second time send the response to ** libnfc-nci stack and stop the cleanup timer. ** d) if ISO-DEP not detected with in cleanup timeout, than ** clear the ISO-DEP detection flag. ** ** Returns NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED ** *******************************************************************************/ NFCSTATUS phNxpNciHal_NfcDep_rsp_ext(uint8_t* p_ntf, uint16_t* p_len) { NFCSTATUS status = NFCSTATUS_INVALID_PARAMETER; NXPLOG_NCIHAL_D(">> p_ntf[0]=%02x , p_ntf[1]=%02x", p_ntf[0], p_ntf[1]); if (p_ntf[0] == 0x41 && p_ntf[1] == 0x04) { // Tag selected, Disable P2P Prio logic. bIgnoreIsoDep = 1; NXPLOG_NCIHAL_D(">> Tag selected, Disable P2P Prio logic."); } else if (((p_ntf[0] == 0x61 && p_ntf[1] == 0x06) || (p_ntf[0] == 0x41 && p_ntf[1] == 0x06)) && bIgnoreIsoDep == 1) { // Tag deselected, enable P2P Prio logic. bIgnoreIsoDep = 0x00; NXPLOG_NCIHAL_D(">> Tag deselected, enable P2P Prio logic."); } if (bIgnoreIsoDep == 0x00 && p_ntf[0] == 0x61 && p_ntf[1] == 0x05 && *p_len > 5) { if (p_ntf[5] == 0x04 && p_ntf[6] < 0x80) { NXPLOG_NCIHAL_D(">> ISO DEP detected."); if (iso_dep_detected == 0x00) { NXPLOG_NCIHAL_D(">> ISO DEP detected first time. Resume polling loop"); iso_dep_detected = 0x01; status = phNxpNciHal_resume_polling_loop(); custom_poll_timer = phOsalNfc_Timer_Create(); NXPLOG_NCIHAL_D("custom poll timer started - %d", custom_poll_timer); status = phOsalNfc_Timer_Start(custom_poll_timer, CUSTOM_POLL_TIMEOUT, &custom_poll_timer_handler, NULL); if (NFCSTATUS_SUCCESS == status) { NXPLOG_NCIHAL_D("custom poll timer started"); } else { NXPLOG_NCIHAL_E("custom poll timer not started!!!"); status = NFCSTATUS_FAILED; } status = NFCSTATUS_FAILED; } else { NXPLOG_NCIHAL_D(">> ISO DEP detected second time."); /* Store notification */ phNxpNciHal_NfcDep_store_ntf(p_ntf, *p_len); /* Stop Cleanup_timer */ phOsalNfc_Timer_Stop(cleanup_timer); phOsalNfc_Timer_Delete(cleanup_timer); cleanup_timer = 0; EnableP2P_PrioLogic = false; iso_dep_detected = 0; status = NFCSTATUS_SUCCESS; } } else if (p_ntf[5] == 0x05) { NXPLOG_NCIHAL_D(">> NFC-DEP Detected - stopping the custom poll timer"); phOsalNfc_Timer_Stop(custom_poll_timer); phOsalNfc_Timer_Delete(custom_poll_timer); EnableP2P_PrioLogic = false; iso_dep_detected = 0; status = NFCSTATUS_SUCCESS; } else { NXPLOG_NCIHAL_D( ">> detected other technology- stopping the custom poll timer"); phOsalNfc_Timer_Stop(custom_poll_timer); phOsalNfc_Timer_Delete(custom_poll_timer); EnableP2P_PrioLogic = false; iso_dep_detected = 0; status = NFCSTATUS_INVALID_PARAMETER; } } else if (bIgnoreIsoDep == 0x00 && ((p_ntf[0] == 0x41 && p_ntf[1] == 0x06) || (p_ntf[0] == 0x61 && p_ntf[1] == 0x06))) { NXPLOG_NCIHAL_D(">> RF disabled"); if (poll_timer_fired == 0x01) { poll_timer_fired = 0x00; NXPLOG_NCIHAL_D(">>restarting polling loop."); /* start polling loop */ phNxpNciHal_start_polling_loop(); EnableP2P_PrioLogic = false; NXPLOG_NCIHAL_D( ">> NFC DEP NOT detected - custom poll timer expired - RF disabled"); cleanup_timer = phOsalNfc_Timer_Create(); /* Start cleanup_timer */ NFCSTATUS status = phOsalNfc_Timer_Start(cleanup_timer, CLEAN_UP_TIMEOUT, &cleanup_timer_handler, NULL); if (NFCSTATUS_SUCCESS == status) { NXPLOG_NCIHAL_D("cleanup timer started"); } else { NXPLOG_NCIHAL_E("cleanup timer not started!!!"); status = NFCSTATUS_FAILED; } status = NFCSTATUS_FAILED; } else { status = NFCSTATUS_SUCCESS; } } if (bIgnoreIsoDep == 0x00 && iso_dep_detected == 1) { if ((p_ntf[0] == 0x41 && p_ntf[1] == 0x06) || (p_ntf[0] == 0x61 && p_ntf[1] == 0x06)) { NXPLOG_NCIHAL_D(">>iso_dep_detected Disconnect related notification"); status = NFCSTATUS_FAILED; } else { NXPLOG_NCIHAL_W("Never come here"); } } return status; } /******************************************************************************* ** ** Function phNxpNciHal_NfcDep_store_ntf ** ** Description Stores the iso dep notification locally. ** ** Returns None ** *******************************************************************************/ static void phNxpNciHal_NfcDep_store_ntf(uint8_t* p_cmd_data, uint16_t cmd_len) { p_iso_ntf_buff = NULL; p_iso_ntf_buff = malloc(sizeof(uint8_t) * cmd_len); if (p_iso_ntf_buff == NULL) { NXPLOG_NCIHAL_E("Error allocating memory (p_iso_ntf_buff)"); return; } memcpy(p_iso_ntf_buff, p_cmd_data, cmd_len); bIgnorep2plogic = 1; } /******************************************************************************* ** ** Function phNxpNciHal_NfcDep_comapre_ntf ** ** Description Compare the notification with previous iso dep notification. ** ** Returns NFCSTATUS_SUCCESS if successful,otherwise NFCSTATUS_FAILED ** *******************************************************************************/ NFCSTATUS phNxpNciHal_NfcDep_comapre_ntf(uint8_t* p_cmd_data, uint16_t cmd_len) { NFCSTATUS status = NFCSTATUS_FAILED; int32_t ret_val = -1; if (bIgnorep2plogic == 1) { ret_val = memcmp(p_cmd_data, p_iso_ntf_buff, cmd_len); if (ret_val != 0) { NXPLOG_NCIHAL_E("Third notification is not equal to last"); } else { NXPLOG_NCIHAL_D( "Third notification is equal to last (disable p2p logic)"); status = NFCSTATUS_SUCCESS; } bIgnorep2plogic = 0; } if (p_iso_ntf_buff != NULL) { free(p_iso_ntf_buff); p_iso_ntf_buff = NULL; } return status; } extern NFCSTATUS phNxpNciHal_clean_P2P_Prio() { NFCSTATUS status = NFCSTATUS_SUCCESS; iso_dep_detected = 0x00; EnableP2P_PrioLogic = false; poll_timer_fired = 0x00; bIgnorep2plogic = 0x00; bIgnoreIsoDep = 0x00; status = phOsalNfc_Timer_Stop(cleanup_timer); status |= phOsalNfc_Timer_Delete(cleanup_timer); status |= phOsalNfc_Timer_Stop(custom_poll_timer); status |= phOsalNfc_Timer_Delete(custom_poll_timer); cleanup_timer = 0; return status; } #endif /******************************************************************************* ** ** Function tmp_thread ** ** Description Thread to execute custom poll commands . ** ** Returns None ** *******************************************************************************/ void* tmp_thread(void* tmp) { NFCSTATUS status = NFCSTATUS_SUCCESS; uint16_t data_len; NXPLOG_NCIHAL_W("tmp_thread: enter type=0x0%x", *((int*)tmp)); usleep(10 * 1000); switch (*((int*)tmp)) { case START_POLLING: { CONCURRENCY_LOCK(); data_len = phNxpNciHal_write_unlocked(cmd_poll_len, cmd_poll, ORIG_NXPHAL); CONCURRENCY_UNLOCK(); if (data_len != cmd_poll_len) { NXPLOG_NCIHAL_E("phNxpNciHal_start_polling_loop: data len mismatch"); status = NFCSTATUS_FAILED; } } break; case RESUME_POLLING: { CONCURRENCY_LOCK(); data_len = phNxpNciHal_write_unlocked(sizeof(cmd_resume_rf_discovery), cmd_resume_rf_discovery, ORIG_NXPHAL); CONCURRENCY_UNLOCK(); if (data_len != sizeof(cmd_resume_rf_discovery)) { NXPLOG_NCIHAL_E("phNxpNciHal_resume_polling_loop: data len mismatch"); status = NFCSTATUS_FAILED; } } break; case STOP_POLLING: { CONCURRENCY_LOCK(); data_len = phNxpNciHal_write_unlocked(sizeof(cmd_stop_rf_discovery), cmd_stop_rf_discovery, ORIG_NXPHAL); CONCURRENCY_UNLOCK(); if (data_len != sizeof(cmd_stop_rf_discovery)) { NXPLOG_NCIHAL_E("phNxpNciHal_stop_polling_loop: data len mismatch"); status = NFCSTATUS_FAILED; } } break; case DISCOVER_SELECT: { CONCURRENCY_LOCK(); data_len = phNxpNciHal_write_unlocked(sizeof(cmd_select_rf_discovery), cmd_select_rf_discovery, ORIG_NXPHAL); CONCURRENCY_UNLOCK(); if (data_len != sizeof(cmd_resume_rf_discovery)) { NXPLOG_NCIHAL_E("phNxpNciHal_resume_polling_loop: data len mismatch"); status = NFCSTATUS_FAILED; } } break; default: NXPLOG_NCIHAL_E("No Matching case"); status = NFCSTATUS_FAILED; break; } NXPLOG_NCIHAL_W("tmp_thread: exit"); pthread_exit(NULL); return NULL; } /******************************************************************************* ** ** Function phNxpNciHal_select_RF_Discovery ** ** Description Sends RF_DISCOVER_SELECT_CMD ** Parameters RfID , RfProtocolType ** Returns NFCSTATUS_PENDING if success ** *******************************************************************************/ NFCSTATUS phNxpNciHal_select_RF_Discovery(unsigned int RfID, unsigned int RfProtocolType) { NFCSTATUS status = NFCSTATUS_SUCCESS; pthread_t pthread; discover_type = DISCOVER_SELECT; cmd_select_rf_discovery[3] = RfID; cmd_select_rf_discovery[4] = RfProtocolType; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (pthread_create(&pthread, &attr, tmp_thread, (void*)&discover_type) != 0) { NXPLOG_NCIHAL_E("fail to create pthread"); } pthread_attr_destroy(&attr); return status; } /******************************************************************************* ** ** Function phNxpNciHal_NfcDep_cmd_ext ** ** Description Stores the polling loop configuration locally. ** ** Returns None ** *******************************************************************************/ void phNxpNciHal_NfcDep_cmd_ext(uint8_t* p_cmd_data, uint16_t* cmd_len) { if (*cmd_len < NCI_HEADER_SIZE) return; if (p_cmd_data[0] == 0x21 && p_cmd_data[1] == 0x03) { if (*cmd_len == 6 && p_cmd_data[3] == 0x01 && p_cmd_data[4] == 0x02 && p_cmd_data[5] == 0x01) { /* DO NOTHING */ } else { if (*cmd_len > MAX_POLL_CMD_LEN) { NXPLOG_NCIHAL_E("invalid cmd_len"); return; } /* Store the polling loop configuration */ cmd_poll_len = *cmd_len; memset(&cmd_poll, 0, cmd_poll_len); memcpy(&cmd_poll, p_cmd_data, cmd_poll_len); } } return; }