blob: cae7301110a83f8e25a6db174cb2576d77ac8184 [file] [log] [blame]
/*
* 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 <inttypes.h>
#include <phNxpNciHal_PowerStats.h>
#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, &currentTime) == -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;
}