1 /******************************************************************************
2 *
3 * Copyright 2018 NXP
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18 #define LOG_TAG "NfccPowerTracker"
19 #include "NfccPowerTracker.h"
20 #include "phNxpNciHal_ext.h"
21 #include <assert.h>
22 #include <fstream>
23 #include <iostream>
24 #include <log/log.h>
25 #include <sstream>
26 #include <stdio.h>
27 #include <sys/file.h>
28 #include <sys/time.h>
29 using namespace std;
30
31 extern bool nfc_debug_enabled;
32 extern phNxpNciHal_Control_t nxpncihal_ctrl;
33 static const uint64_t PWR_TRK_ERROR_MARGIN_IN_MILLISEC = 60000;
34 static const std::string POWER_TRACKER_LOG_FILE =
35 "/data/vendor/nfc/nfc_power_state.txt";
36 static const uint16_t TIMER_COUNT_MASK = 0x7FFF;
37
NfccPowerTracker()38 NfccPowerTracker::NfccPowerTracker() {
39 mIsLastUpdateScreenOn = false;
40 mIsFirstPwrTrkNtfRecvd = false;
41 mLastPowerTrackAborted = false;
42 /*Default standby time*/
43 mStandbyTimePerDiscLoopInMillisec = 1000;
44 }
~NfccPowerTracker()45 NfccPowerTracker::~NfccPowerTracker() {}
46
47 /*******************************************************************************
48 **
49 ** Function NfccPowerTracker::getInstance
50 **
51 ** Description access class singleton
52 **
53 ** Returns pointer to the singleton object
54 **
55 *******************************************************************************/
getInstance()56 NfccPowerTracker &NfccPowerTracker::getInstance() {
57 static NfccPowerTracker sPwrInstance;
58 return sPwrInstance;
59 }
60 /*******************************************************************************
61 **
62 ** Function Initialize
63 **
64 ** Description get all prerequisite information from NFCC needed for
65 ** Power tracker calculations.
66 **
67 ** Returns void
68 **
69 *******************************************************************************/
Initialize()70 void NfccPowerTracker::Initialize() {
71 /*get total duration of discovery loop from NFCC using GET CONFIG command*/
72 uint8_t cmdGetConfigDiscLoopDuration[] = {0x20, 0x03, 0x02, 0x01, 0x00};
73 int status = phNxpNciHal_send_ext_cmd(sizeof(cmdGetConfigDiscLoopDuration),
74 cmdGetConfigDiscLoopDuration);
75 if (status != 0) {
76 ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::Initialize: failed");
77 return;
78 }
79 /*Check for valid get config response and update stanby time*/
80 if (nxpncihal_ctrl.p_rx_data[0] == 0x40 &&
81 nxpncihal_ctrl.p_rx_data[1] == 0x03 &&
82 nxpncihal_ctrl.p_rx_data[2] == 0x06 &&
83 nxpncihal_ctrl.p_rx_data[3] == 0x00 &&
84 nxpncihal_ctrl.p_rx_data[4] == 0x01 &&
85 nxpncihal_ctrl.p_rx_data[5] == 0x00 &&
86 nxpncihal_ctrl.p_rx_data[6] == 0x02) {
87 mStandbyTimePerDiscLoopInMillisec = (uint32_t)(
88 (nxpncihal_ctrl.p_rx_data[8] << 8) | nxpncihal_ctrl.p_rx_data[7]);
89 ALOGD_IF(nfc_debug_enabled, "mStandbyTimePerDiscLoopInMillisec value : %d",
90 mStandbyTimePerDiscLoopInMillisec);
91 }
92 }
93
94 /*******************************************************************************
95 **
96 ** Function TimeDiff
97 **
98 ** Description Computes time difference in milliseconds.
99 **
100 ** Returns Time difference in milliseconds
101 **
102 *******************************************************************************/
TimeDiff(struct timespec start,struct timespec end)103 uint64_t NfccPowerTracker::TimeDiff(struct timespec start,
104 struct timespec end) {
105 uint64_t startTimeInMillisec =
106 start.tv_sec * 1000 + (start.tv_nsec / 1000000);
107 uint64_t endTimeInMillisec = end.tv_sec * 1000 + (end.tv_nsec / 1000000);
108
109 assert(startTimeInMillisec > endTimeInMillisec);
110 return (endTimeInMillisec - startTimeInMillisec);
111 }
112
113 /*******************************************************************************
114 **
115 ** Function NfccPowerTracker::ProcessCmd
116 **
117 ** Description Parse the commands going to NFCC,
118 ** get the time at which power relevant commands are sent
119 ** (ex:Screen state/OMAPI session)is sent and
120 ** log/cache the timestamp to file
121 **
122 ** Returns void
123 **
124 *******************************************************************************/
ProcessCmd(uint8_t * cmd,uint16_t len)125 void NfccPowerTracker::ProcessCmd(uint8_t *cmd, uint16_t len) {
126 ALOGD_IF(nfc_debug_enabled,
127 "NfccPowerTracker::ProcessCmd: Enter,Recieved len :%d", len);
128 bool screenStateCommand;
129 if (cmd[0] == 0x20 && cmd[1] == 0x09) {
130 screenStateCommand = true;
131 } else {
132 screenStateCommand = false;
133 }
134
135 if (screenStateCommand && (cmd[3] == 0x00 || cmd[3] == 0x02)) {
136 /* Command for Screen State On-Locked or Unlocked */
137 clock_gettime(CLOCK_BOOTTIME, &mLastScreenOnTimeStamp);
138 mIsLastUpdateScreenOn = true;
139 } else if (screenStateCommand && (cmd[3] == 0x01 || cmd[3] == 0x03)) {
140 /* Command for Screen State OFF-locked or Unlocked */
141 clock_gettime(CLOCK_BOOTTIME, &mLastScreenOffTimeStamp);
142 mIsLastUpdateScreenOn = false;
143 } else if (cmd[0] == 0x20 && cmd[1] == 0x02 && cmd[2] == 0x05 &&
144 cmd[3] == 0x01 && cmd[4] == 0x00 && cmd[5] == 0x02) {
145 /* Command to update duration of discovery loop */
146 mStandbyTimePerDiscLoopInMillisec = (cmd[7] << 8 | cmd[6]);
147 ALOGD_IF(nfc_debug_enabled, "mStandbyTimePerDiscLoopInMillisec value : %d",
148 mStandbyTimePerDiscLoopInMillisec);
149 }
150 }
151
152 /*******************************************************************************
153 **
154 ** Function NfccPowerTracker::ProcessNtf
155 **
156 ** Description Parse the Notifications coming from NFCC,
157 ** get the time at which power relevant notifications are
158 ** received
159 ** (ex:RF ON-OFF/ACTIVATE-DEACTIVATE NTF/PROP_PWR_TRACKINFO)
160 ** calculate error in standby time by comparing the
161 ** expectated value from NFC HAL and received value from NFCC.
162 ** Cache relevant info (timestamps) to file
163 **
164 ** Returns void
165 **
166 *******************************************************************************/
ProcessNtf(uint8_t * rsp,uint16_t rsp_len)167 void NfccPowerTracker::ProcessNtf(uint8_t *rsp, uint16_t rsp_len) {
168 ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::ProcessNtf: Enter");
169
170 /* Screen State Notification recieved */
171 if ((rsp[0] == 0x6F && rsp[1] == 0x05)) {
172 ProcessPowerTrackNtf(rsp, rsp_len);
173 } else if (rsp[0] == 0x61 && rsp[1] == 0x05) {
174 /*Activation notification received. Calculate the time NFCC is
175 active in Reader/P2P/CE duration */
176 clock_gettime(CLOCK_BOOTTIME, &mActiveTimeStart);
177 if (!mIsLastUpdateScreenOn) {
178 mActiveInfo.totalTransitions++;
179 }
180 } else if (rsp[0] == 0x61 && rsp[1] == 0x06) {
181 /* Deactivation notification received Calculate the time NFCC is
182 active in Reader/P2P/CE duration.Time between Activation and
183 Deacivation gives the active time*/
184 clock_gettime(CLOCK_BOOTTIME, &mActiveTimeEnd);
185 mActiveDurationFromLastScreenUpdate +=
186 TimeDiff(mActiveTimeStart, mActiveTimeEnd);
187 if (!mIsLastUpdateScreenOn) {
188 mStandbyInfo.totalTransitions++;
189 }
190 ALOGD_IF(nfc_debug_enabled, "mActiveDurationFromLastScreenUpdate: %llu",
191 (unsigned long long)mActiveDurationFromLastScreenUpdate);
192 }
193 }
194
195 /*******************************************************************************
196 **
197 ** Function ProcessPowerTrackNtf
198 **
199 ** Description Process Power Tracker notification and update timingInfo to
200 ** Log File.
201 **
202 ** Returns void
203 **
204 *******************************************************************************/
ProcessPowerTrackNtf(uint8_t * rsp,uint16_t rsp_len)205 void NfccPowerTracker::ProcessPowerTrackNtf(uint8_t *rsp, uint16_t rsp_len) {
206 /* Enable Power Tracking computations after 1st Power tracker notification
207 * is received. */
208 if (!mIsFirstPwrTrkNtfRecvd) {
209 mIsFirstPwrTrkNtfRecvd = true;
210 ifstream ifile(POWER_TRACKER_LOG_FILE.c_str());
211 if ((bool)ifile == true) {
212 mLastPowerTrackAborted = true;
213 }
214 return;
215 }
216
217 /*Duration between screen state change is taken as reference for calculating
218 active and standby time*/
219 uint64_t totalDuration = 0;
220 totalDuration =
221 mIsLastUpdateScreenOn
222 ? TimeDiff(mLastScreenOffTimeStamp, mLastScreenOnTimeStamp)
223 : TimeDiff(mLastScreenOnTimeStamp, mLastScreenOffTimeStamp);
224 if (totalDuration == 0)
225 return;
226
227 /*Calculate Active and Standby time based on the pollCount provided in the
228 Power tracker Notification from NFCC*/
229 uint16_t sPollCount = (TIMER_COUNT_MASK & ((rsp[5] << 8) | rsp[4]));
230 ALOGD_IF(nfc_debug_enabled,
231 "Poll/Timer count recived from FW is %d and rsp_len :%d", sPollCount,
232 rsp_len);
233 uint64_t standbyTime = 0, activeTime = 0;
234 if (mIsLastUpdateScreenOn) {
235 activeTime = sPollCount * ACTIVE_TIME_PER_TIMER_COUNT_IN_MILLISEC;
236 /*Check for errors in count provided by NFCC*/
237 uint64_t error = (activeTime > mActiveDurationFromLastScreenUpdate)
238 ? (activeTime - mActiveDurationFromLastScreenUpdate)
239 : (mActiveDurationFromLastScreenUpdate - activeTime);
240 if (error > PWR_TRK_ERROR_MARGIN_IN_MILLISEC) {
241 ALOGD_IF(nfc_debug_enabled,
242 "Active Time Error observed with value is %llu",
243 (unsigned long long)error);
244 mErrorInStandbyInfo.residencyInMsecSinceBoot += error;
245 }
246 standbyTime = (totalDuration > activeTime) ? (totalDuration - activeTime)
247 : (activeTime - totalDuration);
248 if (rsp[3]) {
249 /*If notification trigger is counter overflow, update the screen on
250 timestamp as there is no screen state change*/
251 clock_gettime(CLOCK_BOOTTIME, &mLastScreenOnTimeStamp);
252 }
253 mActiveInfo.totalTransitions++;
254 } else {
255 standbyTime = (sPollCount * ((uint64_t)mStandbyTimePerDiscLoopInMillisec));
256 activeTime = totalDuration > standbyTime ? (totalDuration - standbyTime)
257 : (standbyTime - totalDuration);
258 if (rsp[3]) {
259 /*If notification trigger is counter overflow, update the screen off
260 timestamp as there is no screen state change*/
261 clock_gettime(CLOCK_BOOTTIME, &mLastScreenOffTimeStamp);
262 }
263 /*Total transitions in screen on -> Screen Off window is same as poll count
264 provided by NFCC, as, there is transition in each discovery loop*/
265 mActiveInfo.totalTransitions += sPollCount;
266 /*1 additional transition for screen state update*/
267 mStandbyInfo.totalTransitions += (sPollCount + 1);
268 }
269
270 ALOGD_IF(nfc_debug_enabled,
271 "activeTime: %llu, standbyTime: %llu, totalDuration :%llu",
272 (unsigned long long)activeTime, (unsigned long long)standbyTime,
273 (unsigned long long)totalDuration);
274 if (mLastPowerTrackAborted) {
275 ALOGD_IF(nfc_debug_enabled,
276 "Last Hal service aborted,so retrive the power info data and "
277 "continue\n");
278 /*Read the file content and store in mActiveInfo.residencyInMsecSinceBoot
279 and mStandbyInfo.residencyInMsecSinceBoot*/
280 if (ReadPowerStateLog()) {
281 mLastPowerTrackAborted = false;
282 }
283 }
284 mStandbyInfo.residencyInMsecSinceBoot += standbyTime;
285 mActiveInfo.residencyInMsecSinceBoot += activeTime;
286 UpdatePowerStateLog(mStandbyInfo, mActiveInfo);
287 mActiveDurationFromLastScreenUpdate = 0;
288 }
289 /*******************************************************************************
290 **
291 ** Function NfccPowerTracker::UpdatePowerStateLog
292 **
293 ** Description update the powerstate related information in log file
294 **
295 ** Returns void
296 **
297 *******************************************************************************/
UpdatePowerStateLog(NfccPowerStateInfo_t mStandbyInfo,NfccPowerStateInfo_t mActiveInfo)298 void NfccPowerTracker::UpdatePowerStateLog(NfccPowerStateInfo_t mStandbyInfo,
299 NfccPowerStateInfo_t mActiveInfo) {
300 FILE *fp;
301 const string PWR_TRK_LOG_FILE_VERSION = "1.0";
302 /*Write the Active and standby timestamp into the file*/
303 fp = fopen(POWER_TRACKER_LOG_FILE.c_str(), "w");
304 if (fp == NULL) {
305 ALOGD_IF(nfc_debug_enabled, "Failed to Open Pwr Tracker Info File\n");
306 return;
307 }
308 ostringstream PwrTrackerInfo;
309 PwrTrackerInfo << "Version: " << PWR_TRK_LOG_FILE_VERSION.c_str() << endl;
310 PwrTrackerInfo << "NFC {" << endl;
311 PwrTrackerInfo << " { " << STR_ACTIVE
312 << std::to_string(mActiveInfo.residencyInMsecSinceBoot) << " }"
313 << endl;
314 PwrTrackerInfo << " { " << STR_STANDBY
315 << std::to_string(mStandbyInfo.residencyInMsecSinceBoot)
316 << " }" << endl;
317 PwrTrackerInfo << "}";
318 ALOGD_IF(nfc_debug_enabled,
319 "mActiveInfo.residencyInMsecSinceBoot: %llu, "
320 "mActiveInfo.totalTransitions: %llu,"
321 "mStandbyInfo.residencyInMsecSinceBoot "
322 ":%llu,mStandbyInfo.totalTransitions: %llu"
323 "mErrorInStandbyInfo.residencyInMsecSinceBoot: %llu",
324 (unsigned long long)mActiveInfo.residencyInMsecSinceBoot,
325 (unsigned long long)mActiveInfo.totalTransitions,
326 (unsigned long long)mStandbyInfo.residencyInMsecSinceBoot,
327 (unsigned long long)mStandbyInfo.totalTransitions,
328 (unsigned long long)mErrorInStandbyInfo.residencyInMsecSinceBoot);
329 string PwrInfo = PwrTrackerInfo.str();
330 if (!TryLockFile(fp)) {
331 ALOGD_IF(nfc_debug_enabled,
332 "Failed to Lock PwrTracker File.Skipping update\n");
333 fclose(fp);
334 return;
335 }
336 fwrite(PwrInfo.c_str(), sizeof(char), PwrInfo.length(), fp);
337 fflush(fp);
338 UnlockFile(fp);
339 fclose(fp);
340 }
341 /*******************************************************************************
342 **
343 ** Function ReadPowerStateLog
344 **
345 ** Description Retrieve powerstate related information from log file.
346 **
347 ** Returns true if read successful, false otherwise.
348 **
349 *******************************************************************************/
ReadPowerStateLog()350 bool NfccPowerTracker::ReadPowerStateLog() {
351 ifstream pwrStateFileStream;
352 string itemName;
353 ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::ReadPowerStateLog: Enter \n");
354 pwrStateFileStream.open(POWER_TRACKER_LOG_FILE.c_str());
355 if (pwrStateFileStream.fail()) {
356 ALOGE("Error: %s", strerror(errno));
357 return false;
358 }
359
360 /*Check for required string(time in millisec) in the log file and convert it
361 to integer*/
362 while (pwrStateFileStream >> itemName) {
363 if (STR_ACTIVE.compare(itemName) == 0) {
364 pwrStateFileStream >> itemName;
365 mActiveInfo.residencyInMsecSinceBoot = stoull(itemName.c_str(), nullptr);
366 } else if (STR_STANDBY.compare(itemName) == 0) {
367 pwrStateFileStream >> itemName;
368 mStandbyInfo.residencyInMsecSinceBoot = stoull(itemName.c_str(), nullptr);
369 }
370 }
371
372 ALOGD_IF(nfc_debug_enabled,
373 "Value retrieved from Powertracker file is"
374 "activeTime: %llu and standbyTime: %llu\n",
375 (unsigned long long)mActiveInfo.residencyInMsecSinceBoot,
376 (unsigned long long)mStandbyInfo.residencyInMsecSinceBoot);
377 pwrStateFileStream.close();
378 return true;
379 }
380 /*******************************************************************************
381 **
382 ** Function Pause
383 **
384 ** Description Pause Power state Information Tracking,Tracking will resume
385 ** once next power tracker notification is recieved as part of
386 ** ProcessNtf.
387 **
388 ** Returns void
389 **
390 *******************************************************************************/
Pause()391 void NfccPowerTracker::Pause() { mIsFirstPwrTrkNtfRecvd = false; }
392
393 /*******************************************************************************
394 **
395 ** Function Reset
396 **
397 ** Description Stop power track information processing and delete
398 ** power tracker log file.
399 **
400 ** Returns void
401 **
402 *******************************************************************************/
Reset()403 void NfccPowerTracker::Reset() {
404 ALOGD_IF(nfc_debug_enabled, "NfccPowerTracker::Reset enter");
405 if (remove(POWER_TRACKER_LOG_FILE.c_str()) != 0) {
406 ALOGD_IF(nfc_debug_enabled, "Error deleting Power tracker file");
407 }
408 }
409 /*******************************************************************************
410 **
411 ** Function TryLockFile
412 **
413 ** Description Lock PowerTracker log file. Any application trying to read
414 ** from PowerTracker log file shall acquire lock before reading
415 ** to avoid inconsistent data.
416 **
417 ** Returns true if locking was successful
418 ** false if there was a failure to lock PowerTracker log file.
419 *******************************************************************************/
TryLockFile(FILE * fp)420 bool NfccPowerTracker::TryLockFile(FILE *fp) {
421 uint8_t retryCount = 5;
422 do {
423 if (!flock(fileno(fp), LOCK_EX | LOCK_NB))
424 return true;
425 usleep(10000); /*10 millisec*/
426 } while (retryCount--);
427
428 return false;
429 }
430 /*******************************************************************************
431 **
432 ** Function UnlockFile
433 **
434 ** Description Unlock previously locked PowerTracker log file.
435 **
436 ** Returns void
437 **
438 *******************************************************************************/
UnlockFile(FILE * fp)439 void NfccPowerTracker::UnlockFile(FILE *fp) { flock(fileno(fp), LOCK_UN); }
440