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