/* * Copyright 2022-2023 NXP * * 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 "phNxpNciHal_PowerTracker.h" #include #include #include "IntervalTimer.h" #include "NfcProperties.sysprop.h" #include "NxpNfcThreadMutex.h" #include "phNfcCommon.h" #include "phNxpConfig.h" #include "phNxpNciHal_ext.h" using namespace vendor::nfc::nxp; using std::vector; /******************* Macro definition *****************************************/ #define STATE_STANDBY_STR "STANDBY" #define STATE_ULPDET_STR "ULPDET" #define STATE_ACTIVE_STR "ACTIVE" #define TIME_MS(spec) \ ((long)spec.tv_sec * 1000 + (long)(spec.tv_nsec / 1000000)) /******************* Local functions *****************************************/ static void* phNxpNciHal_pollPowerTrackerData(void* pContext); static NFCSTATUS phNxpNciHal_syncPowerTrackerData(); /******************* Enums and Class declarations ***********************/ /** * Power data for each states. */ typedef struct { uint32_t stateEntryCount; uint32_t stateTickCount; } StateResidencyData; /** * Context object used internally by power tracker implementation. */ typedef struct { // Power data poll duration. long pollDurationMilliSec; // Power tracker thread id pthread_t thread; // To signal events. NfcHalThreadCondVar event; // To protect state data NfcHalThreadMutex dataMutex; // Poll for power tracker periodically if this is true. bool_t isRefreshNfccStateOngoing; // Timestamp of last power data sync. struct timespec lastSyncTime; // Power data for STANDBY, ULPDET, ACTIVE states StateResidencyData stateData[MAX_STATES]; // Timer used for tracking ULPDET power data. IntervalTimer ulpdetTimer; // Timestamp of ULPDET start. struct timespec ulpdetStartTime; // True if ULPDET is on. bool_t isUlpdetOn; } PowerTrackerContext; /******************* External variables ***************************************/ extern phNxpNciHal_Control_t nxpncihal_ctrl; /*********************** Global Variables *************************************/ static PowerTrackerContext gContext = { .pollDurationMilliSec = 0, .thread = 0, .isRefreshNfccStateOngoing = false, .lastSyncTime.tv_sec = 0, .lastSyncTime.tv_nsec = 0, .stateData[STANDBY].stateEntryCount = 0, .stateData[STANDBY].stateTickCount = 0, .stateData[ULPDET].stateEntryCount = 0, .stateData[ULPDET].stateTickCount = 0, .stateData[ACTIVE].stateEntryCount = 0, .stateData[ACTIVE].stateTickCount = 0, }; /******************************************************************************* ** ** Function phNxpNciHal_startPowerTracker() ** ** Description Function to start power tracker feature. ** ** Parameters pollDuration - Poll duration in MS for fetching power data ** from NFCC. ** Returns NFCSTATUS_FAILED or NFCSTATUS_SUCCESS *******************************************************************************/ NFCSTATUS phNxpNciHal_startPowerTracker(unsigned long pollDuration) { NFCSTATUS status = NFCSTATUS_SUCCESS; phNxpNci_EEPROM_info_t mEEPROM_info = {.request_mode = 0}; uint8_t power_tracker_enable = 0x01; NXPLOG_NCIHAL_I("%s: Starting PowerTracker with poll duration %ld", __func__, pollDuration); mEEPROM_info.request_mode = SET_EEPROM_DATA; mEEPROM_info.buffer = (uint8_t*)&power_tracker_enable; mEEPROM_info.bufflen = sizeof(power_tracker_enable); mEEPROM_info.request_type = EEPROM_POWER_TRACKER_ENABLE; status = request_EEPROM(&mEEPROM_info); if (status != NFCSTATUS_SUCCESS) { NXPLOG_NCIHAL_E("Failed to Start PowerTracker, status - %d", status); return status; } if (!gContext.isRefreshNfccStateOngoing) { // Initialize last sync time to the start time. if (clock_gettime(CLOCK_MONOTONIC, &gContext.lastSyncTime) == -1) { NXPLOG_NCIHAL_E("%s Fail get time; errno=0x%X", __func__, errno); } // Sync Initial values of variables from sys properties. // so that previous values are used in case of Nfc HAL crash. gContext.stateData[ACTIVE].stateEntryCount = NfcProps::activeStateEntryCount().value_or(0); gContext.stateData[ACTIVE].stateTickCount = NfcProps::activeStateTick().value_or(0); gContext.stateData[STANDBY].stateEntryCount = NfcProps::standbyStateEntryCount().value_or(0); gContext.stateData[STANDBY].stateTickCount = NfcProps::standbyStateTick().value_or(0); gContext.stateData[ULPDET].stateEntryCount = NfcProps::ulpdetStateEntryCount().value_or(0); gContext.stateData[ULPDET].stateTickCount = NfcProps::ulpdetStateTick().value_or(0); // Start polling Thread gContext.pollDurationMilliSec = pollDuration; gContext.isRefreshNfccStateOngoing = true; if (pthread_create(&gContext.thread, NULL, phNxpNciHal_pollPowerTrackerData, &gContext) != 0) { NXPLOG_NCIHAL_E("%s: Failed to create PowerTracker, thread - %d", __func__, errno); return NFCSTATUS_FAILED; } phNxpNciHal_registerToPowerStats(); } else { NXPLOG_NCIHAL_E("PowerTracker already enabled"); } return status; } /******************************************************************************* ** ** Function phNxpNciHal_pollPowerTrackerData ** ** Description Thread function which tracks power data in a loop with ** configured duration until power tracker feature is stopped. ** ** Parameters pContext - Power tracker thread context if any. ** Returns None *******************************************************************************/ static void* phNxpNciHal_pollPowerTrackerData(void* pCtx) { NFCSTATUS status = NFCSTATUS_SUCCESS; PowerTrackerContext* pContext = (PowerTrackerContext*)pCtx; NXPLOG_NCIHAL_D("Starting to poll for PowerTracker data"); // Poll till power tracker is cancelled. while (pContext->isRefreshNfccStateOngoing) { struct timespec absoluteTime; if (clock_gettime(CLOCK_MONOTONIC, &absoluteTime) == -1) { NXPLOG_NCIHAL_E("%s Fail get time; errno=0x%X", __func__, errno); } else { absoluteTime.tv_sec += pContext->pollDurationMilliSec / 1000; long ns = absoluteTime.tv_nsec + ((pContext->pollDurationMilliSec % 1000) * 1000000); if (ns > 1000000000) { absoluteTime.tv_sec++; absoluteTime.tv_nsec = ns - 1000000000; } else absoluteTime.tv_nsec = ns; } pContext->event.lock(); // Wait for poll duration pContext->event.timedWait(&absoluteTime); pContext->event.unlock(); // Sync and cache power tracker data. if (pContext->isRefreshNfccStateOngoing) { status = phNxpNciHal_syncPowerTrackerData(); if (NFCSTATUS_SUCCESS != status) { NXPLOG_NCIHAL_E("Failed to fetch PowerTracker data. error = %d", status); // break; } } } NXPLOG_NCIHAL_D("Stopped polling for PowerTracker data"); return NULL; } /******************************************************************************* ** ** Function phNxpNciHal_syncPowerTrackerData() ** ** Description Function to sync power tracker data from NFCC chip. ** ** Parameters None. ** Returns NFCSTATUS_FAILED or NFCSTATUS_SUCCESS *******************************************************************************/ static NFCSTATUS phNxpNciHal_syncPowerTrackerData() { NFCSTATUS status = NFCSTATUS_SUCCESS; struct timespec currentTime = {.tv_sec = 0, .tv_nsec = 0}; uint8_t cmd_getPowerTrackerData[] = {0x2F, 0x2E, // NCI_PROP_GET_PWR_TRACK_DATA_CMD 0x00}; // LENGTH uint64_t activeTick = 0, activeCounter = 0; CONCURRENCY_LOCK(); // This lock is to protect origin field. status = phNxpNciHal_send_ext_cmd(sizeof(cmd_getPowerTrackerData), cmd_getPowerTrackerData); CONCURRENCY_UNLOCK(); if (status != NFCSTATUS_SUCCESS) { return status; } if (nxpncihal_ctrl.p_rx_data[3] != NFCSTATUS_SUCCESS) { return (NFCSTATUS)nxpncihal_ctrl.p_rx_data[3]; } if (clock_gettime(CLOCK_MONOTONIC, ¤tTime) == -1) { NXPLOG_NCIHAL_E("%s Fail get time; errno=0x%X", __func__, errno); } uint8_t* rsp = nxpncihal_ctrl.p_rx_data; activeCounter = ((uint64_t)rsp[4] << 0) | ((uint64_t)rsp[5] << 8) | ((uint64_t)rsp[6] << 16) | ((uint64_t)rsp[7] << 24); activeTick = ((uint64_t)rsp[8] << 0) | ((uint64_t)rsp[9] << 8) | ((uint64_t)rsp[10] << 16) | ((uint64_t)rsp[11] << 24); // Calculate time difference between two sync uint64_t totalTimeMs = TIME_MS(currentTime) - TIME_MS(gContext.lastSyncTime); NfcHalAutoThreadMutex(gContext.dataMutex); gContext.stateData[ACTIVE].stateEntryCount += activeCounter; gContext.stateData[ACTIVE].stateTickCount += activeTick; // Standby counter is same as active counter less one as current // data sync will move NFCC to active resulting in one value // higher than standby gContext.stateData[STANDBY].stateEntryCount = (gContext.stateData[ACTIVE].stateEntryCount - 1); if ((totalTimeMs / STEP_TIME_MS) > activeTick) { gContext.stateData[STANDBY].stateTickCount += ((totalTimeMs / STEP_TIME_MS) - activeTick); } else { NXPLOG_NCIHAL_E("ActiveTick(%" PRIu64 ") > totalTick(%" PRIu64 "), Shouldn't happen !!!", activeTick, (totalTimeMs / STEP_TIME_MS)); } gContext.lastSyncTime = currentTime; // Sync values of variables to sys properties so that // previous values can be synced in case of Nfc HAL crash. NfcProps::activeStateEntryCount(gContext.stateData[ACTIVE].stateEntryCount); NfcProps::activeStateTick(gContext.stateData[ACTIVE].stateTickCount); NfcProps::standbyStateEntryCount(gContext.stateData[STANDBY].stateEntryCount); NfcProps::standbyStateTick(gContext.stateData[STANDBY].stateTickCount); NXPLOG_NCIHAL_D( "Successfully fetched PowerTracker data " "Active counter = %u, Active Tick = %u " "Standby Counter = %u, Standby Tick = %u " "ULPDET Counter = %u, ULPDET Tick = %u", gContext.stateData[ACTIVE].stateEntryCount, gContext.stateData[ACTIVE].stateTickCount, gContext.stateData[STANDBY].stateEntryCount, gContext.stateData[STANDBY].stateTickCount, gContext.stateData[ULPDET].stateEntryCount, gContext.stateData[ULPDET].stateTickCount); return status; } /******************************************************************************* ** ** Function onUlpdetTimerExpired() ** ** Description Callback invoked by Ulpdet timer when timeout happens. ** Currently ulpdet power data is tracked with same frequency ** as poll duration to be in sync with ACTIVE, STANDBY data. ** Once ULPDET timer expires after poll duration data are ** updated and timer is re created until ULPDET is off. ** ** Parameters val - Timer context passed while starting timer. ** Returns None *******************************************************************************/ static void onUlpdetTimerExpired(union sigval val) { (void)val; NXPLOG_NCIHAL_D("%s Ulpdet Timer Expired,", __func__); struct timespec ulpdetEndTime = {.tv_sec = 0, .tv_nsec = 0}; // End ulpdet Tick. if (clock_gettime(CLOCK_MONOTONIC, &ulpdetEndTime) == -1) { NXPLOG_NCIHAL_E("%s fail get time; errno=0x%X", __func__, errno); } long ulpdetTimeMs = TIME_MS(ulpdetEndTime) - TIME_MS(gContext.ulpdetStartTime); NfcHalAutoThreadMutex(gContext.dataMutex); // Convert to Tick with 100ms step gContext.stateData[ULPDET].stateTickCount += (ulpdetTimeMs / STEP_TIME_MS); // Sync values of variables to sys properties so that // previous values can be synced in case of Nfc HAL crash. NfcProps::ulpdetStateEntryCount(gContext.stateData[ULPDET].stateEntryCount); NfcProps::ulpdetStateTick(gContext.stateData[ULPDET].stateTickCount); gContext.ulpdetStartTime = ulpdetEndTime; if (gContext.isUlpdetOn) { // Start ULPDET Timer NXPLOG_NCIHAL_D("%s Refreshing Ulpdet Timer", __func__); gContext.ulpdetTimer.set(gContext.pollDurationMilliSec, NULL, onUlpdetTimerExpired); } } /******************************************************************************* ** ** Function phNxpNciHal_onRefreshNfccPowerState() ** ** Description Callback invoked internally by HAL whenever there is system ** state change and power data needs to be refreshed. ** ** Parameters state - Can be SCREEN_OFF, SCREEN_ON, ULPDET_OFF, ULPDET_ON ** Returns NFCSTATUS_FAILED or NFCSTATUS_SUCCESS *******************************************************************************/ NFCSTATUS phNxpNciHal_onRefreshNfccPowerState(RefreshNfccPowerState state) { NFCSTATUS status = NFCSTATUS_SUCCESS; NXPLOG_NCIHAL_D("%s Enter, RefreshNfccPowerState = %u", __func__, state); union sigval val; switch (state) { case SCREEN_ON: // Signal power tracker thread to sync data from NFCC gContext.event.signal(); break; case ULPDET_ON: if (phNxpNciHal_syncPowerTrackerData() != NFCSTATUS_SUCCESS) { NXPLOG_NCIHAL_E("Failed to fetch PowerTracker data. error = %d", status); } gContext.dataMutex.lock(); gContext.stateData[ULPDET].stateEntryCount++; if (clock_gettime(CLOCK_MONOTONIC, &gContext.ulpdetStartTime) == -1) { NXPLOG_NCIHAL_E("%s fail get time; errno=0x%X", __func__, errno); } gContext.isUlpdetOn = true; gContext.dataMutex.unlock(); // Start ULPDET Timer gContext.ulpdetTimer.set(gContext.pollDurationMilliSec, NULL, onUlpdetTimerExpired); break; case ULPDET_OFF: if (gContext.isUlpdetOn) { gContext.ulpdetTimer.kill(); gContext.isUlpdetOn = false; onUlpdetTimerExpired(val); gContext.ulpdetStartTime = {.tv_sec = 0, .tv_nsec = 0}; } break; default: status = NFCSTATUS_FAILED; break; } NXPLOG_NCIHAL_D("%s Exit", __func__); return status; } /******************************************************************************* ** ** Function phNxpNciHal_stopPowerTracker() ** ** Description Function to stop power tracker feature. ** ** Parameters None ** Returns NFCSTATUS_FAILED or NFCSTATUS_SUCCESS *******************************************************************************/ NFCSTATUS phNxpNciHal_stopPowerTracker() { NFCSTATUS status = NFCSTATUS_SUCCESS; phNxpNci_EEPROM_info_t mEEPROM_info = {.request_mode = 0}; uint8_t power_tracker_disable = 0x00; mEEPROM_info.request_mode = SET_EEPROM_DATA; mEEPROM_info.buffer = (uint8_t*)&power_tracker_disable; mEEPROM_info.bufflen = sizeof(power_tracker_disable); mEEPROM_info.request_type = EEPROM_POWER_TRACKER_ENABLE; status = request_EEPROM(&mEEPROM_info); if (status != NFCSTATUS_SUCCESS) { NXPLOG_NCIHAL_E("%s Failed to disable PowerTracker, error = %d", __func__, status); } if (gContext.isRefreshNfccStateOngoing) { // Stop Polling Thread gContext.isRefreshNfccStateOngoing = false; gContext.event.signal(); if (pthread_join(gContext.thread, NULL) != 0) { NXPLOG_NCIHAL_E("Failed to join with PowerTracker thread %d", errno); } } else { NXPLOG_NCIHAL_E("PowerTracker is already disabled"); } if (!gContext.isUlpdetOn) { NXPLOG_NCIHAL_I("%s: Stopped PowerTracker", __func__); phNxpNciHal_unregisterPowerStats(); } else { NXPLOG_NCIHAL_D("%s: Skipping unregister for ULPDET case", __func__); } return status; }