C++程序  |  439行  |  17.18 KB

/******************************************************************************
 *
 *  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); }