| /****************************************************************************** |
| * |
| * Copyright 2018 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. |
| * |
| ******************************************************************************/ |
| #define LOG_TAG "NfccPowerTracker" |
| #include "NfccPowerTracker.h" |
| #include "phNxpNciHal_ext.h" |
| #include <assert.h> |
| #include <fstream> |
| #include <iostream> |
| #include <log/log.h> |
| #include <sstream> |
| #include <stdio.h> |
| #include <sys/file.h> |
| #include <sys/time.h> |
| using namespace std; |
| |
| extern bool nfc_debug_enabled; |
| extern phNxpNciHal_Control_t nxpncihal_ctrl; |
| static const uint64_t PWR_TRK_ERROR_MARGIN_IN_MILLISEC = 60000; |
| static const std::string POWER_TRACKER_LOG_FILE = |
| "/data/vendor/nfc/nfc_power_state.txt"; |
| static const uint16_t TIMER_COUNT_MASK = 0x7FFF; |
| |
| NfccPowerTracker::NfccPowerTracker() { |
| mIsFirstPwrTrkNtfRecvd = false; |
| mLastPowerTrackAborted = false; |
| /*Default standby time*/ |
| mStandbyTimePerDiscLoopInMillisec = 1000; |
| } |
| NfccPowerTracker::~NfccPowerTracker() {} |
| |
| /******************************************************************************* |
| ** |
| ** Function NfccPowerTracker::getInstance |
| ** |
| ** Description access class singleton |
| ** |
| ** Returns pointer to the singleton object |
| ** |
| *******************************************************************************/ |
| NfccPowerTracker &NfccPowerTracker::getInstance() { |
| static NfccPowerTracker sPwrInstance; |
| return sPwrInstance; |
| } |
| /******************************************************************************* |
| ** |
| ** Function Initialize |
| ** |
| ** Description get all prerequisite information from NFCC needed for |
| ** Power tracker calculations. |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void NfccPowerTracker::Initialize() { |
| /*get total duration of discovery loop from NFCC using GET CONFIG command*/ |
| uint8_t cmdGetConfigDiscLoopDuration[] = {0x20, 0x03, 0x02, 0x01, 0x00}; |
| int status = phNxpNciHal_send_ext_cmd(sizeof(cmdGetConfigDiscLoopDuration), |
| cmdGetConfigDiscLoopDuration); |
| if (status != 0) { |
| ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::Initialize: failed"); |
| return; |
| } |
| /*Check for valid get config response and update stanby time*/ |
| if (nxpncihal_ctrl.p_rx_data[0] == 0x40 && |
| nxpncihal_ctrl.p_rx_data[1] == 0x03 && |
| nxpncihal_ctrl.p_rx_data[2] == 0x06 && |
| nxpncihal_ctrl.p_rx_data[3] == 0x00 && |
| nxpncihal_ctrl.p_rx_data[4] == 0x01 && |
| nxpncihal_ctrl.p_rx_data[5] == 0x00 && |
| nxpncihal_ctrl.p_rx_data[6] == 0x02) { |
| mStandbyTimePerDiscLoopInMillisec = (uint32_t)( |
| (nxpncihal_ctrl.p_rx_data[8] << 8) | nxpncihal_ctrl.p_rx_data[7]); |
| ALOGD_IF(nfc_debug_enabled, "mStandbyTimePerDiscLoopInMillisec value : %d", |
| mStandbyTimePerDiscLoopInMillisec); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function TimeDiff |
| ** |
| ** Description Computes time difference in milliseconds. |
| ** |
| ** Returns Time difference in milliseconds |
| ** |
| *******************************************************************************/ |
| uint64_t NfccPowerTracker::TimeDiff(struct timespec start, |
| struct timespec end) { |
| uint64_t startTimeInMillisec = |
| start.tv_sec * 1000 + (start.tv_nsec / 1000000); |
| uint64_t endTimeInMillisec = end.tv_sec * 1000 + (end.tv_nsec / 1000000); |
| |
| assert(startTimeInMillisec > endTimeInMillisec); |
| return (endTimeInMillisec - startTimeInMillisec); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function NfccPowerTracker::ProcessCmd |
| ** |
| ** Description Parse the commands going to NFCC, |
| ** get the time at which power relevant commands are sent |
| ** (ex:Screen state/OMAPI session)is sent and |
| ** log/cache the timestamp to file |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void NfccPowerTracker::ProcessCmd(uint8_t *cmd, uint16_t len) { |
| ALOGD_IF(nfc_debug_enabled, |
| "NfccPowerTracker::ProcessCmd: Enter,Recieved len :%d", len); |
| bool screenStateCommand; |
| if (cmd[0] == 0x20 && cmd[1] == 0x09) { |
| screenStateCommand = true; |
| } else { |
| screenStateCommand = false; |
| } |
| |
| if (screenStateCommand && (cmd[3] == 0x00 || cmd[3] == 0x02)) { |
| /* Command for Screen State On-Locked or Unlocked */ |
| clock_gettime(CLOCK_BOOTTIME, &mLastScreenOnTimeStamp); |
| mIsLastUpdateScreenOn = true; |
| } else if (screenStateCommand && (cmd[3] == 0x01 || cmd[3] == 0x03)) { |
| /* Command for Screen State OFF-locked or Unlocked */ |
| clock_gettime(CLOCK_BOOTTIME, &mLastScreenOffTimeStamp); |
| mIsLastUpdateScreenOn = false; |
| } else if (cmd[0] == 0x20 && cmd[1] == 0x02 && cmd[2] == 0x05 && |
| cmd[3] == 0x01 && cmd[4] == 0x00 && cmd[5] == 0x02) { |
| /* Command to update duration of discovery loop */ |
| mStandbyTimePerDiscLoopInMillisec = (cmd[7] << 8 | cmd[6]); |
| ALOGD_IF(nfc_debug_enabled, "mStandbyTimePerDiscLoopInMillisec value : %d", |
| mStandbyTimePerDiscLoopInMillisec); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function NfccPowerTracker::ProcessNtf |
| ** |
| ** Description Parse the Notifications coming from NFCC, |
| ** get the time at which power relevant notifications are |
| ** received |
| ** (ex:RF ON-OFF/ACTIVATE-DEACTIVATE NTF/PROP_PWR_TRACKINFO) |
| ** calculate error in standby time by comparing the |
| ** expectated value from NFC HAL and received value from NFCC. |
| ** Cache relevant info (timestamps) to file |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void NfccPowerTracker::ProcessNtf(uint8_t *rsp, uint16_t rsp_len) { |
| ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::ProcessNtf: Enter"); |
| |
| /* Screen State Notification recieved */ |
| if ((rsp[0] == 0x6F && rsp[1] == 0x05)) { |
| ProcessPowerTrackNtf(rsp, rsp_len); |
| } else if (rsp[0] == 0x61 && rsp[1] == 0x05) { |
| /*Activation notification received. Calculate the time NFCC is |
| active in Reader/P2P/CE duration */ |
| clock_gettime(CLOCK_BOOTTIME, &mActiveTimeStart); |
| if (!mIsLastUpdateScreenOn) { |
| mActiveInfo.totalTransitions++; |
| } |
| } else if (rsp[0] == 0x61 && rsp[1] == 0x06) { |
| /* Deactivation notification received Calculate the time NFCC is |
| active in Reader/P2P/CE duration.Time between Activation and |
| Deacivation gives the active time*/ |
| clock_gettime(CLOCK_BOOTTIME, &mActiveTimeEnd); |
| mActiveDurationFromLastScreenUpdate += |
| TimeDiff(mActiveTimeStart, mActiveTimeEnd); |
| if (!mIsLastUpdateScreenOn) { |
| mStandbyInfo.totalTransitions++; |
| } |
| ALOGD_IF(nfc_debug_enabled, "mActiveDurationFromLastScreenUpdate: %llu", |
| (unsigned long long)mActiveDurationFromLastScreenUpdate); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function ProcessPowerTrackNtf |
| ** |
| ** Description Process Power Tracker notification and update timingInfo to |
| ** Log File. |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void NfccPowerTracker::ProcessPowerTrackNtf(uint8_t *rsp, uint16_t rsp_len) { |
| /* Enable Power Tracking computations after 1st Power tracker notification |
| * is received. */ |
| if (!mIsFirstPwrTrkNtfRecvd) { |
| mIsFirstPwrTrkNtfRecvd = true; |
| ifstream ifile(POWER_TRACKER_LOG_FILE.c_str()); |
| if ((bool)ifile == true) { |
| mLastPowerTrackAborted = true; |
| } |
| return; |
| } |
| |
| /*Duration between screen state change is taken as reference for calculating |
| active and standby time*/ |
| uint64_t totalDuration = 0; |
| totalDuration = |
| mIsLastUpdateScreenOn |
| ? TimeDiff(mLastScreenOffTimeStamp, mLastScreenOnTimeStamp) |
| : TimeDiff(mLastScreenOnTimeStamp, mLastScreenOffTimeStamp); |
| if (totalDuration == 0) |
| return; |
| |
| /*Calculate Active and Standby time based on the pollCount provided in the |
| Power tracker Notification from NFCC*/ |
| uint16_t sPollCount = (TIMER_COUNT_MASK & ((rsp[5] << 8) | rsp[4])); |
| ALOGD_IF(nfc_debug_enabled, |
| "Poll/Timer count recived from FW is %d and rsp_len :%d", sPollCount, |
| rsp_len); |
| uint64_t standbyTime = 0, activeTime = 0; |
| if (mIsLastUpdateScreenOn) { |
| activeTime = sPollCount * ACTIVE_TIME_PER_TIMER_COUNT_IN_MILLISEC; |
| /*Check for errors in count provided by NFCC*/ |
| uint64_t error = (activeTime > mActiveDurationFromLastScreenUpdate) |
| ? (activeTime - mActiveDurationFromLastScreenUpdate) |
| : (mActiveDurationFromLastScreenUpdate - activeTime); |
| if (error > PWR_TRK_ERROR_MARGIN_IN_MILLISEC) { |
| ALOGD_IF(nfc_debug_enabled, |
| "Active Time Error observed with value is %llu", |
| (unsigned long long)error); |
| mErrorInStandbyInfo.residencyInMsecSinceBoot += error; |
| } |
| standbyTime = (totalDuration > activeTime) ? (totalDuration - activeTime) |
| : (activeTime - totalDuration); |
| if (rsp[3]) { |
| /*If notification trigger is counter overflow, update the screen on |
| timestamp as there is no screen state change*/ |
| clock_gettime(CLOCK_BOOTTIME, &mLastScreenOnTimeStamp); |
| } |
| mActiveInfo.totalTransitions++; |
| } else { |
| standbyTime = (sPollCount * mStandbyTimePerDiscLoopInMillisec); |
| activeTime = totalDuration > standbyTime ? (totalDuration - standbyTime) |
| : (standbyTime - totalDuration); |
| if (rsp[3]) { |
| /*If notification trigger is counter overflow, update the screen off |
| timestamp as there is no screen state change*/ |
| clock_gettime(CLOCK_BOOTTIME, &mLastScreenOffTimeStamp); |
| } |
| /*Total transitions in screen on -> Screen Off window is same as poll count |
| provided by NFCC, as, there is transition in each discovery loop*/ |
| mActiveInfo.totalTransitions += sPollCount; |
| /*1 additional transition for screen state update*/ |
| mStandbyInfo.totalTransitions += (sPollCount + 1); |
| } |
| |
| ALOGD_IF(nfc_debug_enabled, |
| "activeTime: %llu, standbyTime: %llu, totalDuration :%llu", |
| (unsigned long long)activeTime, (unsigned long long)standbyTime, |
| (unsigned long long)totalDuration); |
| if (mLastPowerTrackAborted) { |
| ALOGD_IF(nfc_debug_enabled, |
| "Last Hal service aborted,so retrive the power info data and " |
| "continue\n"); |
| /*Read the file content and store in mActiveInfo.residencyInMsecSinceBoot |
| and mStandbyInfo.residencyInMsecSinceBoot*/ |
| if (ReadPowerStateLog()) { |
| mLastPowerTrackAborted = false; |
| } |
| } |
| mStandbyInfo.residencyInMsecSinceBoot += standbyTime; |
| mActiveInfo.residencyInMsecSinceBoot += activeTime; |
| UpdatePowerStateLog(mStandbyInfo, mActiveInfo); |
| mActiveDurationFromLastScreenUpdate = 0; |
| } |
| /******************************************************************************* |
| ** |
| ** Function NfccPowerTracker::UpdatePowerStateLog |
| ** |
| ** Description update the powerstate related information in log file |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void NfccPowerTracker::UpdatePowerStateLog(NfccPowerStateInfo_t mStandbyInfo, |
| NfccPowerStateInfo_t mActiveInfo) { |
| FILE *fp; |
| const string PWR_TRK_LOG_FILE_VERSION = "1.0"; |
| /*Write the Active and standby timestamp into the file*/ |
| fp = fopen(POWER_TRACKER_LOG_FILE.c_str(), "w"); |
| if (fp == NULL) { |
| ALOGD_IF(nfc_debug_enabled, "Failed to Open Pwr Tracker Info File\n"); |
| return; |
| } |
| ostringstream PwrTrackerInfo; |
| PwrTrackerInfo << "Version: " << PWR_TRK_LOG_FILE_VERSION.c_str() << endl; |
| PwrTrackerInfo << "NFC {" << endl; |
| PwrTrackerInfo << " { " << STR_ACTIVE |
| << std::to_string(mActiveInfo.residencyInMsecSinceBoot) << " }" |
| << endl; |
| PwrTrackerInfo << " { " << STR_STANDBY |
| << std::to_string(mStandbyInfo.residencyInMsecSinceBoot) |
| << " }" << endl; |
| PwrTrackerInfo << "}"; |
| ALOGD_IF(nfc_debug_enabled, |
| "mActiveInfo.residencyInMsecSinceBoot: %llu, " |
| "mActiveInfo.totalTransitions: %llu," |
| "mStandbyInfo.residencyInMsecSinceBoot " |
| ":%llu,mStandbyInfo.totalTransitions: %llu" |
| "mErrorInStandbyInfo.residencyInMsecSinceBoot: %llu", |
| (unsigned long long)mActiveInfo.residencyInMsecSinceBoot, |
| (unsigned long long)mActiveInfo.totalTransitions, |
| (unsigned long long)mStandbyInfo.residencyInMsecSinceBoot, |
| (unsigned long long)mStandbyInfo.totalTransitions, |
| (unsigned long long)mErrorInStandbyInfo.residencyInMsecSinceBoot); |
| string PwrInfo = PwrTrackerInfo.str(); |
| if (!TryLockFile(fp)) { |
| ALOGD_IF(nfc_debug_enabled, |
| "Failed to Lock PwrTracker File.Skipping update\n"); |
| fclose(fp); |
| return; |
| } |
| fwrite(PwrInfo.c_str(), sizeof(char), PwrInfo.length(), fp); |
| fflush(fp); |
| UnlockFile(fp); |
| fclose(fp); |
| } |
| /******************************************************************************* |
| ** |
| ** Function ReadPowerStateLog |
| ** |
| ** Description Retrieve powerstate related information from log file. |
| ** |
| ** Returns true if read successful, false otherwise. |
| ** |
| *******************************************************************************/ |
| bool NfccPowerTracker::ReadPowerStateLog() { |
| ifstream pwrStateFileStream; |
| string itemName; |
| ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::ReadPowerStateLog: Enter \n"); |
| pwrStateFileStream.open(POWER_TRACKER_LOG_FILE.c_str()); |
| if (pwrStateFileStream.fail()) { |
| ALOGE("Error: %s", strerror(errno)); |
| return false; |
| } |
| |
| /*Check for required string(time in millisec) in the log file and convert it |
| to integer*/ |
| while (pwrStateFileStream >> itemName) { |
| if (STR_ACTIVE.compare(itemName) == 0) { |
| pwrStateFileStream >> itemName; |
| mActiveInfo.residencyInMsecSinceBoot = stoull(itemName.c_str(), nullptr); |
| } else if (STR_STANDBY.compare(itemName) == 0) { |
| pwrStateFileStream >> itemName; |
| mStandbyInfo.residencyInMsecSinceBoot = stoull(itemName.c_str(), nullptr); |
| } |
| } |
| |
| ALOGD_IF(nfc_debug_enabled, |
| "Value retrieved from Powertracker file is" |
| "activeTime: %llu and standbyTime: %llu\n", |
| (unsigned long long)mActiveInfo.residencyInMsecSinceBoot, |
| (unsigned long long)mStandbyInfo.residencyInMsecSinceBoot); |
| pwrStateFileStream.close(); |
| return true; |
| } |
| /******************************************************************************* |
| ** |
| ** Function Pause |
| ** |
| ** Description Pause Power state Information Tracking,Tracking will resume |
| ** once next power tracker notification is recieved as part of |
| ** ProcessNtf. |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void NfccPowerTracker::Pause() { mIsFirstPwrTrkNtfRecvd = false; } |
| |
| /******************************************************************************* |
| ** |
| ** Function Reset |
| ** |
| ** Description Stop power track information processing and delete |
| ** power tracker log file. |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void NfccPowerTracker::Reset() { |
| ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::Reset enter"); |
| if (remove(POWER_TRACKER_LOG_FILE.c_str()) != 0) { |
| ALOGD_IF(nfc_debug_enabled, "Error deleting Power tracker file"); |
| } |
| } |
| /******************************************************************************* |
| ** |
| ** Function TryLockFile |
| ** |
| ** Description Lock PowerTracker log file. Any application trying to read |
| ** from PowerTracker log file shall acquire lock before reading |
| ** to avoid inconsistent data. |
| ** |
| ** Returns true if locking was successful |
| ** false if there was a failure to lock PowerTracker log file. |
| *******************************************************************************/ |
| bool NfccPowerTracker::TryLockFile(FILE *fp) { |
| uint8_t retryCount = 5; |
| do { |
| if (!flock(fileno(fp), LOCK_EX | LOCK_NB)) |
| return true; |
| usleep(10000); /*10 millisec*/ |
| } while (retryCount--); |
| |
| return false; |
| } |
| /******************************************************************************* |
| ** |
| ** Function UnlockFile |
| ** |
| ** Description Unlock previously locked PowerTracker log file. |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void NfccPowerTracker::UnlockFile(FILE *fp) { flock(fileno(fp), LOCK_UN); } |