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 
145     // Start polling Thread
146     gContext.pollDurationMilliSec = pollDuration;
147     gContext.isRefreshNfccStateOngoing = true;
148     if (pthread_create(&gContext.thread, NULL, phNxpNciHal_pollPowerTrackerData,
149                        &gContext) != 0) {
150       NXPLOG_NCIHAL_E("%s: Failed to create PowerTracker, thread - %d",
151                       __func__, errno);
152       return NFCSTATUS_FAILED;
153     }
154     phNxpNciHal_registerToPowerStats();
155   } else {
156     NXPLOG_NCIHAL_E("PowerTracker already enabled");
157   }
158   return status;
159 }
160 
161 /*******************************************************************************
162 **
163 ** Function         phNxpNciHal_pollPowerTrackerData
164 **
165 ** Description      Thread function which tracks power data in a loop with
166 **                  configured duration until power tracker feature is stopped.
167 **
168 ** Parameters       pContext - Power tracker thread context if any.
169 ** Returns          None
170 *******************************************************************************/
171 
phNxpNciHal_pollPowerTrackerData(void * pCtx)172 static void* phNxpNciHal_pollPowerTrackerData(void* pCtx) {
173   NFCSTATUS status = NFCSTATUS_SUCCESS;
174   PowerTrackerContext* pContext = (PowerTrackerContext*)pCtx;
175 
176   NXPLOG_NCIHAL_D("Starting to poll for PowerTracker data");
177   // Poll till power tracker is cancelled.
178   while (pContext->isRefreshNfccStateOngoing) {
179     struct timespec absoluteTime;
180 
181     if (clock_gettime(CLOCK_MONOTONIC, &absoluteTime) == -1) {
182       NXPLOG_NCIHAL_E("%s Fail get time; errno=0x%X", __func__, errno);
183     } else {
184       absoluteTime.tv_sec += pContext->pollDurationMilliSec / 1000;
185       long ns = absoluteTime.tv_nsec +
186                 ((pContext->pollDurationMilliSec % 1000) * 1000000);
187       if (ns > 1000000000) {
188         absoluteTime.tv_sec++;
189         absoluteTime.tv_nsec = ns - 1000000000;
190       } else
191         absoluteTime.tv_nsec = ns;
192     }
193     pContext->event.lock();
194     // Wait for poll duration
195     pContext->event.timedWait(&absoluteTime);
196     pContext->event.unlock();
197 
198     // Sync and cache power tracker data.
199     if (pContext->isRefreshNfccStateOngoing) {
200       status = phNxpNciHal_syncPowerTrackerData();
201       if (NFCSTATUS_SUCCESS != status) {
202         NXPLOG_NCIHAL_E("Failed to fetch PowerTracker data. error = %d",
203                         status);
204         // break;
205       }
206     }
207   }
208   NXPLOG_NCIHAL_D("Stopped polling for PowerTracker data");
209   return NULL;
210 }
211 
212 /*******************************************************************************
213 **
214 ** Function         phNxpNciHal_syncPowerTrackerData()
215 **
216 ** Description      Function to sync power tracker data from NFCC chip.
217 **
218 ** Parameters       None.
219 ** Returns          NFCSTATUS_FAILED or NFCSTATUS_SUCCESS
220 *******************************************************************************/
221 
phNxpNciHal_syncPowerTrackerData()222 static NFCSTATUS phNxpNciHal_syncPowerTrackerData() {
223   NFCSTATUS status = NFCSTATUS_SUCCESS;
224   struct timespec currentTime = {.tv_sec = 0, .tv_nsec = 0};
225   uint8_t cmd_getPowerTrackerData[] = {0x2F,
226                                        0x2E,  // NCI_PROP_GET_PWR_TRACK_DATA_CMD
227                                        0x00};  // LENGTH
228   uint64_t activeTick = 0, activeCounter = 0;
229 
230   CONCURRENCY_LOCK();  // This lock is to protect origin field.
231   status = phNxpNciHal_send_ext_cmd(sizeof(cmd_getPowerTrackerData),
232                                     cmd_getPowerTrackerData);
233   CONCURRENCY_UNLOCK();
234   if (status != NFCSTATUS_SUCCESS) {
235     return status;
236   }
237   if (nxpncihal_ctrl.p_rx_data[3] != NFCSTATUS_SUCCESS) {
238     return (NFCSTATUS)nxpncihal_ctrl.p_rx_data[3];
239   }
240 
241   if (clock_gettime(CLOCK_MONOTONIC, &currentTime) == -1) {
242     NXPLOG_NCIHAL_E("%s Fail get time; errno=0x%X", __func__, errno);
243   }
244   uint8_t* rsp = nxpncihal_ctrl.p_rx_data;
245   activeCounter = ((uint64_t)rsp[4] << 0) | ((uint64_t)rsp[5] << 8) |
246                   ((uint64_t)rsp[6] << 16) | ((uint64_t)rsp[7] << 24);
247   activeTick = ((uint64_t)rsp[8] << 0) | ((uint64_t)rsp[9] << 8) |
248                ((uint64_t)rsp[10] << 16) | ((uint64_t)rsp[11] << 24);
249 
250   // Calculate time difference between two sync
251   uint64_t totalTimeMs = TIME_MS(currentTime) - TIME_MS(gContext.lastSyncTime);
252 
253   NfcHalAutoThreadMutex(gContext.dataMutex);
254   gContext.stateData[ACTIVE].stateEntryCount += activeCounter;
255   gContext.stateData[ACTIVE].stateTickCount += activeTick;
256   // Standby counter is same as active counter less one as current
257   // data sync will move NFCC to active resulting in one value
258   // higher than standby
259   gContext.stateData[STANDBY].stateEntryCount =
260       (gContext.stateData[ACTIVE].stateEntryCount - 1);
261   if ((totalTimeMs / STEP_TIME_MS) > activeTick) {
262     gContext.stateData[STANDBY].stateTickCount +=
263         ((totalTimeMs / STEP_TIME_MS) - activeTick);
264   } else {
265     NXPLOG_NCIHAL_E("ActiveTick(%" PRIu64 ") > totalTick(%" PRIu64
266                     "), Shouldn't happen !!!",
267                     activeTick, (totalTimeMs / STEP_TIME_MS));
268   }
269   gContext.lastSyncTime = currentTime;
270 
271   // Sync values of variables to sys properties so that
272   // previous values can be synced in case of Nfc HAL crash.
273   NfcProps::activeStateEntryCount(gContext.stateData[ACTIVE].stateEntryCount);
274   NfcProps::activeStateTick(gContext.stateData[ACTIVE].stateTickCount);
275   NfcProps::standbyStateEntryCount(gContext.stateData[STANDBY].stateEntryCount);
276   NfcProps::standbyStateTick(gContext.stateData[STANDBY].stateTickCount);
277 
278   NXPLOG_NCIHAL_D(
279       "Successfully fetched PowerTracker data "
280       "Active counter = %u, Active Tick = %u "
281       "Standby Counter = %u, Standby Tick = %u "
282       "ULPDET Counter = %u, ULPDET Tick = %u",
283       gContext.stateData[ACTIVE].stateEntryCount,
284       gContext.stateData[ACTIVE].stateTickCount,
285       gContext.stateData[STANDBY].stateEntryCount,
286       gContext.stateData[STANDBY].stateTickCount,
287       gContext.stateData[ULPDET].stateEntryCount,
288       gContext.stateData[ULPDET].stateTickCount);
289   return status;
290 }
291 
292 /*******************************************************************************
293 **
294 ** Function         onUlpdetTimerExpired()
295 **
296 ** Description      Callback invoked by Ulpdet timer when timeout happens.
297 **                  Currently ulpdet power data is tracked with same frequency
298 **                  as poll duration to be in sync with ACTIVE, STANDBY data.
299 **                  Once ULPDET timer expires after poll duration data are
300 **                  updated and timer is re created until ULPDET is off.
301 **
302 ** Parameters       val - Timer context passed while starting timer.
303 ** Returns          None
304 *******************************************************************************/
305 
onUlpdetTimerExpired(union sigval val)306 static void onUlpdetTimerExpired(union sigval val) {
307   (void)val;
308   NXPLOG_NCIHAL_D("%s Ulpdet Timer Expired,", __func__);
309   struct timespec ulpdetEndTime = {.tv_sec = 0, .tv_nsec = 0};
310   // End ulpdet Tick.
311   if (clock_gettime(CLOCK_MONOTONIC, &ulpdetEndTime) == -1) {
312     NXPLOG_NCIHAL_E("%s fail get time; errno=0x%X", __func__, errno);
313   }
314   long ulpdetTimeMs =
315       TIME_MS(ulpdetEndTime) - TIME_MS(gContext.ulpdetStartTime);
316 
317   NfcHalAutoThreadMutex(gContext.dataMutex);
318   // Convert to Tick with 100ms step
319   gContext.stateData[ULPDET].stateTickCount += (ulpdetTimeMs / STEP_TIME_MS);
320   // Sync values of variables to sys properties so that
321   // previous values can be synced in case of Nfc HAL crash.
322   NfcProps::ulpdetStateEntryCount(gContext.stateData[ULPDET].stateEntryCount);
323   NfcProps::ulpdetStateTick(gContext.stateData[ULPDET].stateTickCount);
324   gContext.ulpdetStartTime = ulpdetEndTime;
325   if (gContext.isUlpdetOn) {
326     // Start ULPDET Timer
327     NXPLOG_NCIHAL_D("%s Refreshing Ulpdet Timer", __func__);
328     gContext.ulpdetTimer.set(gContext.pollDurationMilliSec, NULL,
329                              onUlpdetTimerExpired);
330   }
331 }
332 
333 /*******************************************************************************
334 **
335 ** Function         phNxpNciHal_onRefreshNfccPowerState()
336 **
337 ** Description      Callback invoked internally by HAL whenever there is system
338 **                  state change and power data needs to be refreshed.
339 **
340 ** Parameters       state - Can be SCREEN_OFF, SCREEN_ON, ULPDET_OFF, ULPDET_ON
341 ** Returns          NFCSTATUS_FAILED or NFCSTATUS_SUCCESS
342 *******************************************************************************/
343 
phNxpNciHal_onRefreshNfccPowerState(RefreshNfccPowerState state)344 NFCSTATUS phNxpNciHal_onRefreshNfccPowerState(RefreshNfccPowerState state) {
345   NFCSTATUS status = NFCSTATUS_SUCCESS;
346   NXPLOG_NCIHAL_D("%s Enter, RefreshNfccPowerState = %u", __func__, state);
347   union sigval val;
348   switch (state) {
349     case SCREEN_ON:
350       // Signal power tracker thread to sync data from NFCC
351       gContext.event.signal();
352       break;
353     case ULPDET_ON:
354       if (phNxpNciHal_syncPowerTrackerData() != NFCSTATUS_SUCCESS) {
355         NXPLOG_NCIHAL_E("Failed to fetch PowerTracker data. error = %d",
356                         status);
357       }
358       gContext.dataMutex.lock();
359       gContext.stateData[ULPDET].stateEntryCount++;
360       if (clock_gettime(CLOCK_MONOTONIC, &gContext.ulpdetStartTime) == -1) {
361         NXPLOG_NCIHAL_E("%s fail get time; errno=0x%X", __func__, errno);
362       }
363       gContext.isUlpdetOn = true;
364       gContext.dataMutex.unlock();
365       // Start ULPDET Timer
366       gContext.ulpdetTimer.set(gContext.pollDurationMilliSec, NULL,
367                                onUlpdetTimerExpired);
368       break;
369     case ULPDET_OFF:
370       if (gContext.isUlpdetOn) {
371         gContext.ulpdetTimer.kill();
372         gContext.isUlpdetOn = false;
373         onUlpdetTimerExpired(val);
374         gContext.ulpdetStartTime = {.tv_sec = 0, .tv_nsec = 0};
375       }
376       break;
377     default:
378       status = NFCSTATUS_FAILED;
379       break;
380   }
381   NXPLOG_NCIHAL_D("%s Exit", __func__);
382   return status;
383 }
384 
385 /*******************************************************************************
386 **
387 ** Function         phNxpNciHal_stopPowerTracker()
388 **
389 ** Description      Function to stop power tracker feature.
390 **
391 ** Parameters       None
392 ** Returns          NFCSTATUS_FAILED or NFCSTATUS_SUCCESS
393 *******************************************************************************/
394 
phNxpNciHal_stopPowerTracker()395 NFCSTATUS phNxpNciHal_stopPowerTracker() {
396   NFCSTATUS status = NFCSTATUS_SUCCESS;
397   phNxpNci_EEPROM_info_t mEEPROM_info = {.request_mode = 0};
398   uint8_t power_tracker_disable = 0x00;
399 
400   mEEPROM_info.request_mode = SET_EEPROM_DATA;
401   mEEPROM_info.buffer = (uint8_t*)&power_tracker_disable;
402   mEEPROM_info.bufflen = sizeof(power_tracker_disable);
403   mEEPROM_info.request_type = EEPROM_POWER_TRACKER_ENABLE;
404 
405   status = request_EEPROM(&mEEPROM_info);
406   if (status != NFCSTATUS_SUCCESS) {
407     NXPLOG_NCIHAL_E("%s Failed to disable PowerTracker, error = %d", __func__,
408                     status);
409   }
410   if (gContext.isRefreshNfccStateOngoing) {
411     // Stop Polling Thread
412     gContext.isRefreshNfccStateOngoing = false;
413     gContext.event.signal();
414     if (pthread_join(gContext.thread, NULL) != 0) {
415       NXPLOG_NCIHAL_E("Failed to join with PowerTracker thread %d", errno);
416     }
417   } else {
418     NXPLOG_NCIHAL_E("PowerTracker is already disabled");
419   }
420   if (!gContext.isUlpdetOn) {
421     NXPLOG_NCIHAL_I("%s: Stopped PowerTracker", __func__);
422     phNxpNciHal_unregisterPowerStats();
423   } else {
424     NXPLOG_NCIHAL_D("%s: Skipping unregister for ULPDET case", __func__);
425   }
426   return status;
427 }
428