1 /*
2 * Copyright 2022-2023 NXP
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "phNxpNciHal_PowerTracker.h"
17
18 #include <inttypes.h>
19 #include <phNxpNciHal_PowerStats.h>
20
21 #include "IntervalTimer.h"
22 #include "NfcProperties.sysprop.h"
23 #include "NxpNfcThreadMutex.h"
24 #include "phNfcCommon.h"
25 #include "phNxpConfig.h"
26 #include "phNxpNciHal_ext.h"
27
28 using namespace vendor::nfc::nxp;
29 using std::vector;
30
31 /******************* Macro definition *****************************************/
32 #define STATE_STANDBY_STR "STANDBY"
33 #define STATE_ULPDET_STR "ULPDET"
34 #define STATE_ACTIVE_STR "ACTIVE"
35
36 #define TIME_MS(spec) \
37 ((long)spec.tv_sec * 1000 + (long)(spec.tv_nsec / 1000000))
38
39 /******************* Local functions *****************************************/
40 static void* phNxpNciHal_pollPowerTrackerData(void* pContext);
41 static NFCSTATUS phNxpNciHal_syncPowerTrackerData();
42
43 /******************* Enums and Class declarations ***********************/
44 /**
45 * Power data for each states.
46 */
47 typedef struct {
48 uint32_t stateEntryCount;
49 uint32_t stateTickCount;
50 } StateResidencyData;
51
52 /**
53 * Context object used internally by power tracker implementation.
54 */
55 typedef struct {
56 // Power data poll duration.
57 long pollDurationMilliSec;
58 // Power tracker thread id
59 pthread_t thread;
60 // To signal events.
61 NfcHalThreadCondVar event;
62 // To protect state data
63 NfcHalThreadMutex dataMutex;
64 // Poll for power tracker periodically if this is true.
65 bool_t isRefreshNfccStateOngoing;
66 // Timestamp of last power data sync.
67 struct timespec lastSyncTime;
68 // Power data for STANDBY, ULPDET, ACTIVE states
69 StateResidencyData stateData[MAX_STATES];
70 // Timer used for tracking ULPDET power data.
71 IntervalTimer ulpdetTimer;
72 // Timestamp of ULPDET start.
73 struct timespec ulpdetStartTime;
74 // True if ULPDET is on.
75 bool_t isUlpdetOn;
76 } PowerTrackerContext;
77
78 /******************* External variables ***************************************/
79 extern phNxpNciHal_Control_t nxpncihal_ctrl;
80
81 /*********************** Global Variables *************************************/
82 static PowerTrackerContext gContext = {
83 .pollDurationMilliSec = 0,
84 .thread = 0,
85 .isRefreshNfccStateOngoing = false,
86 .lastSyncTime.tv_sec = 0,
87 .lastSyncTime.tv_nsec = 0,
88 .stateData[STANDBY].stateEntryCount = 0,
89 .stateData[STANDBY].stateTickCount = 0,
90 .stateData[ULPDET].stateEntryCount = 0,
91 .stateData[ULPDET].stateTickCount = 0,
92 .stateData[ACTIVE].stateEntryCount = 0,
93 .stateData[ACTIVE].stateTickCount = 0,
94 };
95
96 /*******************************************************************************
97 **
98 ** Function phNxpNciHal_startPowerTracker()
99 **
100 ** Description Function to start power tracker feature.
101 **
102 ** Parameters pollDuration - Poll duration in MS for fetching power data
103 ** from NFCC.
104 ** Returns NFCSTATUS_FAILED or NFCSTATUS_SUCCESS
105 *******************************************************************************/
106
phNxpNciHal_startPowerTracker(unsigned long pollDuration)107 NFCSTATUS phNxpNciHal_startPowerTracker(unsigned long pollDuration) {
108 NFCSTATUS status = NFCSTATUS_SUCCESS;
109
110 phNxpNci_EEPROM_info_t mEEPROM_info = {.request_mode = 0};
111 uint8_t power_tracker_enable = 0x01;
112
113 NXPLOG_NCIHAL_I("%s: Starting PowerTracker with poll duration %ld", __func__,
114 pollDuration);
115 mEEPROM_info.request_mode = SET_EEPROM_DATA;
116 mEEPROM_info.buffer = (uint8_t*)&power_tracker_enable;
117 mEEPROM_info.bufflen = sizeof(power_tracker_enable);
118 mEEPROM_info.request_type = EEPROM_POWER_TRACKER_ENABLE;
119
120 status = request_EEPROM(&mEEPROM_info);
121 if (status != NFCSTATUS_SUCCESS) {
122 NXPLOG_NCIHAL_E("Failed to Start PowerTracker, status - %d", status);
123 return status;
124 }
125 if (!gContext.isRefreshNfccStateOngoing) {
126 // Initialize last sync time to the start time.
127 if (clock_gettime(CLOCK_MONOTONIC, &gContext.lastSyncTime) == -1) {
128 NXPLOG_NCIHAL_E("%s Fail get time; errno=0x%X", __func__, errno);
129 }
130 // Sync Initial values of variables from sys properties.
131 // so that previous values are used in case of Nfc HAL crash.
132 gContext.stateData[ACTIVE].stateEntryCount =
133 NfcProps::activeStateEntryCount().value_or(0);
134 gContext.stateData[ACTIVE].stateTickCount =
135 NfcProps::activeStateTick().value_or(0);
136 gContext.stateData[STANDBY].stateEntryCount =
137 NfcProps::standbyStateEntryCount().value_or(0);
138 gContext.stateData[STANDBY].stateTickCount =
139 NfcProps::standbyStateTick().value_or(0);
140 gContext.stateData[ULPDET].stateEntryCount =
141 NfcProps::ulpdetStateEntryCount().value_or(0);
142 gContext.stateData[ULPDET].stateTickCount =
143 NfcProps::ulpdetStateTick().value_or(0);
144 NXPLOG_NCIHAL_D(
145 "Cached PowerTracker data "
146 "Active counter = %u, Active Tick = %u "
147 "Standby Counter = %u, Standby Tick = %u "
148 "ULPDET Counter = %u, ULPDET Tick = %u",
149 gContext.stateData[ACTIVE].stateEntryCount,
150 gContext.stateData[ACTIVE].stateTickCount,
151 gContext.stateData[STANDBY].stateEntryCount,
152 gContext.stateData[STANDBY].stateTickCount,
153 gContext.stateData[ULPDET].stateEntryCount,
154 gContext.stateData[ULPDET].stateTickCount);
155
156 // Start polling Thread
157 gContext.pollDurationMilliSec = pollDuration;
158 gContext.isRefreshNfccStateOngoing = true;
159 if (pthread_create(&gContext.thread, NULL, phNxpNciHal_pollPowerTrackerData,
160 &gContext) != 0) {
161 NXPLOG_NCIHAL_E("%s: Failed to create PowerTracker, thread - %d",
162 __func__, errno);
163 return NFCSTATUS_FAILED;
164 }
165 phNxpNciHal_registerToPowerStats();
166 } else {
167 NXPLOG_NCIHAL_E("PowerTracker already enabled");
168 }
169 return status;
170 }
171
172 /*******************************************************************************
173 **
174 ** Function phNxpNciHal_pollPowerTrackerData
175 **
176 ** Description Thread function which tracks power data in a loop with
177 ** configured duration until power tracker feature is stopped.
178 **
179 ** Parameters pContext - Power tracker thread context if any.
180 ** Returns None
181 *******************************************************************************/
182
phNxpNciHal_pollPowerTrackerData(void * pCtx)183 static void* phNxpNciHal_pollPowerTrackerData(void* pCtx) {
184 NFCSTATUS status = NFCSTATUS_SUCCESS;
185 PowerTrackerContext* pContext = (PowerTrackerContext*)pCtx;
186
187 NXPLOG_NCIHAL_D("Starting to poll for PowerTracker data");
188 // Poll till power tracker is cancelled.
189 while (pContext->isRefreshNfccStateOngoing) {
190 struct timespec absoluteTime;
191
192 if (clock_gettime(CLOCK_MONOTONIC, &absoluteTime) == -1) {
193 NXPLOG_NCIHAL_E("%s Fail get time; errno=0x%X", __func__, errno);
194 } else {
195 absoluteTime.tv_sec += pContext->pollDurationMilliSec / 1000;
196 long ns = absoluteTime.tv_nsec +
197 ((pContext->pollDurationMilliSec % 1000) * 1000000);
198 if (ns > 1000000000) {
199 absoluteTime.tv_sec++;
200 absoluteTime.tv_nsec = ns - 1000000000;
201 } else
202 absoluteTime.tv_nsec = ns;
203 }
204 pContext->event.lock();
205 // Wait for poll duration
206 pContext->event.timedWait(&absoluteTime);
207 pContext->event.unlock();
208
209 // Sync and cache power tracker data.
210 status = phNxpNciHal_syncPowerTrackerData();
211 if (NFCSTATUS_SUCCESS != status) {
212 NXPLOG_NCIHAL_E("Failed to fetch PowerTracker data. error = %d", status);
213 }
214 }
215 NXPLOG_NCIHAL_D("Stopped polling for PowerTracker data");
216 return NULL;
217 }
218
219 /*******************************************************************************
220 **
221 ** Function phNxpNciHal_syncPowerTrackerData()
222 **
223 ** Description Function to sync power tracker data from NFCC chip.
224 **
225 ** Parameters None.
226 ** Returns NFCSTATUS_FAILED or NFCSTATUS_SUCCESS
227 *******************************************************************************/
228
phNxpNciHal_syncPowerTrackerData()229 static NFCSTATUS phNxpNciHal_syncPowerTrackerData() {
230 NFCSTATUS status = NFCSTATUS_SUCCESS;
231 struct timespec currentTime = {.tv_sec = 0, .tv_nsec = 0};
232 uint8_t cmd_getPowerTrackerData[] = {0x2F,
233 0x2E, // NCI_PROP_GET_PWR_TRACK_DATA_CMD
234 0x00}; // LENGTH
235 uint64_t activeTick = 0, activeCounter = 0;
236
237 CONCURRENCY_LOCK(); // This lock is to protect origin field.
238 status = phNxpNciHal_send_ext_cmd(sizeof(cmd_getPowerTrackerData),
239 cmd_getPowerTrackerData);
240 CONCURRENCY_UNLOCK();
241 if (status != NFCSTATUS_SUCCESS) {
242 return status;
243 }
244 if (nxpncihal_ctrl.p_rx_data[3] != NFCSTATUS_SUCCESS) {
245 return (NFCSTATUS)nxpncihal_ctrl.p_rx_data[3];
246 }
247
248 if (clock_gettime(CLOCK_MONOTONIC, ¤tTime) == -1) {
249 NXPLOG_NCIHAL_E("%s Fail get time; errno=0x%X", __func__, errno);
250 }
251 uint8_t* rsp = nxpncihal_ctrl.p_rx_data;
252 activeCounter = ((uint64_t)rsp[4] << 0) | ((uint64_t)rsp[5] << 8) |
253 ((uint64_t)rsp[6] << 16) | ((uint64_t)rsp[7] << 24);
254 activeTick = ((uint64_t)rsp[8] << 0) | ((uint64_t)rsp[9] << 8) |
255 ((uint64_t)rsp[10] << 16) | ((uint64_t)rsp[11] << 24);
256
257 // Calculate time difference between two sync
258 uint64_t totalTimeMs = TIME_MS(currentTime) - TIME_MS(gContext.lastSyncTime);
259
260 NfcHalAutoThreadMutex(gContext.dataMutex);
261 gContext.stateData[ACTIVE].stateEntryCount += activeCounter;
262 gContext.stateData[ACTIVE].stateTickCount += activeTick;
263 // Standby counter is same as active counter less one as current
264 // data sync will move NFCC to active resulting in one value
265 // higher than standby
266 gContext.stateData[STANDBY].stateEntryCount =
267 (gContext.stateData[ACTIVE].stateEntryCount - 1);
268 if ((totalTimeMs / STEP_TIME_MS) > activeTick) {
269 gContext.stateData[STANDBY].stateTickCount +=
270 ((totalTimeMs / STEP_TIME_MS) - activeTick);
271 } else {
272 NXPLOG_NCIHAL_E("ActiveTick(%" PRIu64 ") > totalTick(%" PRIu64
273 "), Shouldn't happen !!!",
274 activeTick, (totalTimeMs / STEP_TIME_MS));
275 }
276 gContext.lastSyncTime = currentTime;
277
278 // Sync values of variables to sys properties so that
279 // previous values can be synced in case of Nfc HAL crash.
280 NfcProps::activeStateEntryCount(gContext.stateData[ACTIVE].stateEntryCount);
281 NfcProps::activeStateTick(gContext.stateData[ACTIVE].stateTickCount);
282 NfcProps::standbyStateEntryCount(gContext.stateData[STANDBY].stateEntryCount);
283 NfcProps::standbyStateTick(gContext.stateData[STANDBY].stateTickCount);
284
285 NXPLOG_NCIHAL_D(
286 "Successfully fetched PowerTracker data "
287 "Active counter = %u, Active Tick = %u "
288 "Standby Counter = %u, Standby Tick = %u "
289 "ULPDET Counter = %u, ULPDET Tick = %u",
290 gContext.stateData[ACTIVE].stateEntryCount,
291 gContext.stateData[ACTIVE].stateTickCount,
292 gContext.stateData[STANDBY].stateEntryCount,
293 gContext.stateData[STANDBY].stateTickCount,
294 gContext.stateData[ULPDET].stateEntryCount,
295 gContext.stateData[ULPDET].stateTickCount);
296 return status;
297 }
298
299 /*******************************************************************************
300 **
301 ** Function onUlpdetTimerExpired()
302 **
303 ** Description Callback invoked by Ulpdet timer when timeout happens.
304 ** Currently ulpdet power data is tracked with same frequency
305 ** as poll duration to be in sync with ACTIVE, STANDBY data.
306 ** Once ULPDET timer expires after poll duration data are
307 ** updated and timer is re created until ULPDET is off.
308 **
309 ** Parameters val - Timer context passed while starting timer.
310 ** Returns None
311 *******************************************************************************/
312
onUlpdetTimerExpired(union sigval val)313 static void onUlpdetTimerExpired(union sigval val) {
314 (void)val;
315 NXPLOG_NCIHAL_D("%s Ulpdet Timer Expired,", __func__);
316 struct timespec ulpdetEndTime = {.tv_sec = 0, .tv_nsec = 0};
317 // End ulpdet Tick.
318 if (clock_gettime(CLOCK_MONOTONIC, &ulpdetEndTime) == -1) {
319 NXPLOG_NCIHAL_E("%s fail get time; errno=0x%X", __func__, errno);
320 }
321 long ulpdetTimeMs =
322 TIME_MS(ulpdetEndTime) - TIME_MS(gContext.ulpdetStartTime);
323
324 NfcHalAutoThreadMutex(gContext.dataMutex);
325 // Convert to Tick with 100ms step
326 gContext.stateData[ULPDET].stateTickCount += (ulpdetTimeMs / STEP_TIME_MS);
327 // Sync values of variables to sys properties so that
328 // previous values can be synced in case of Nfc HAL crash.
329 NfcProps::ulpdetStateEntryCount(gContext.stateData[ULPDET].stateEntryCount);
330 NfcProps::ulpdetStateTick(gContext.stateData[ULPDET].stateTickCount);
331 gContext.ulpdetStartTime = ulpdetEndTime;
332 if (gContext.isUlpdetOn) {
333 // Start ULPDET Timer
334 NXPLOG_NCIHAL_D("%s Refreshing Ulpdet Timer", __func__);
335 gContext.ulpdetTimer.set(gContext.pollDurationMilliSec, NULL,
336 onUlpdetTimerExpired);
337 }
338 }
339
340 /*******************************************************************************
341 **
342 ** Function phNxpNciHal_onRefreshNfccPowerState()
343 **
344 ** Description Callback invoked internally by HAL whenever there is system
345 ** state change and power data needs to be refreshed.
346 **
347 ** Parameters state - Can be SCREEN_OFF, SCREEN_ON, ULPDET_OFF, ULPDET_ON
348 ** Returns NFCSTATUS_FAILED or NFCSTATUS_SUCCESS
349 *******************************************************************************/
350
phNxpNciHal_onRefreshNfccPowerState(RefreshNfccPowerState state)351 NFCSTATUS phNxpNciHal_onRefreshNfccPowerState(RefreshNfccPowerState state) {
352 NFCSTATUS status = NFCSTATUS_SUCCESS;
353 NXPLOG_NCIHAL_D("%s Enter, RefreshNfccPowerState = %u", __func__, state);
354 union sigval val;
355 switch (state) {
356 case SCREEN_ON:
357 // Signal power tracker thread to sync data from NFCC
358 gContext.event.signal();
359 break;
360 case ULPDET_ON:
361 if (phNxpNciHal_syncPowerTrackerData() != NFCSTATUS_SUCCESS) {
362 NXPLOG_NCIHAL_E("Failed to fetch PowerTracker data. error = %d",
363 status);
364 }
365 gContext.dataMutex.lock();
366 gContext.stateData[ULPDET].stateEntryCount++;
367 if (clock_gettime(CLOCK_MONOTONIC, &gContext.ulpdetStartTime) == -1) {
368 NXPLOG_NCIHAL_E("%s fail get time; errno=0x%X", __func__, errno);
369 }
370 gContext.isUlpdetOn = true;
371 gContext.dataMutex.unlock();
372 // Start ULPDET Timer
373 gContext.ulpdetTimer.set(gContext.pollDurationMilliSec, NULL,
374 onUlpdetTimerExpired);
375 break;
376 case ULPDET_OFF:
377 if (gContext.isUlpdetOn) {
378 gContext.ulpdetTimer.kill();
379 gContext.isUlpdetOn = false;
380 onUlpdetTimerExpired(val);
381 gContext.ulpdetStartTime = {.tv_sec = 0, .tv_nsec = 0};
382 }
383 break;
384 default:
385 status = NFCSTATUS_FAILED;
386 break;
387 }
388 NXPLOG_NCIHAL_D("%s Exit", __func__);
389 return status;
390 }
391
392 /*******************************************************************************
393 **
394 ** Function phNxpNciHal_stopPowerTracker()
395 **
396 ** Description Function to stop power tracker feature.
397 **
398 ** Parameters None
399 ** Returns NFCSTATUS_FAILED or NFCSTATUS_SUCCESS
400 *******************************************************************************/
401
phNxpNciHal_stopPowerTracker()402 NFCSTATUS phNxpNciHal_stopPowerTracker() {
403 NFCSTATUS status = NFCSTATUS_SUCCESS;
404 phNxpNci_EEPROM_info_t mEEPROM_info = {.request_mode = 0};
405 uint8_t power_tracker_disable = 0x00;
406
407 if (gContext.isRefreshNfccStateOngoing) {
408 // Stop Polling Thread
409 gContext.isRefreshNfccStateOngoing = false;
410 gContext.event.signal();
411 if (pthread_join(gContext.thread, NULL) != 0) {
412 NXPLOG_NCIHAL_E("Failed to join with PowerTracker thread %d", errno);
413 }
414 } else {
415 NXPLOG_NCIHAL_E("PowerTracker is already disabled");
416 }
417 mEEPROM_info.request_mode = SET_EEPROM_DATA;
418 mEEPROM_info.buffer = (uint8_t*)&power_tracker_disable;
419 mEEPROM_info.bufflen = sizeof(power_tracker_disable);
420 mEEPROM_info.request_type = EEPROM_POWER_TRACKER_ENABLE;
421
422 status = request_EEPROM(&mEEPROM_info);
423 if (status != NFCSTATUS_SUCCESS) {
424 NXPLOG_NCIHAL_E("%s Failed to disable PowerTracker, error = %d", __func__,
425 status);
426 }
427 if (!gContext.isUlpdetOn) {
428 NXPLOG_NCIHAL_I("%s: Stopped PowerTracker", __func__);
429 phNxpNciHal_unregisterPowerStats();
430 } else {
431 NXPLOG_NCIHAL_D("%s: Skipping unregister for ULPDET case", __func__);
432 }
433 return status;
434 }
435