• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <bit>
2 #include <mutex>
3 #include <random>
4 #include <thread>
5 #include <unordered_map>
6 #include <vector>
7 
8 #include "phNxpConfig.h"
9 #include "phNxpUciHal.h"
10 #include "phNxpUciHal_ext.h"
11 #include "phNxpUciHal_utils.h"
12 
13 extern phNxpUciHal_Control_t nxpucihal_ctrl;
14 
15 //
16 // SessionTrack
17 //
18 // Keeps track of device/session state.
19 //
20 // 1. Per-country calibrations
21 //
22 // When the country code is switching from A to B,
23 // a. Active Sessions restricted by B country should be stopped.
24 // b. Per-country device calibrations should be delayed to where
25 //    device stays in IDLE.
26 //
27 // 2. Issue URSK_DELETE_CMD on SESSION_DEINIT_RSP (optional/experimental)
28 //
29 // Calls URSK_DELETE_CMD for every CCC session closing,
30 // for the cases where CCC session ID was created but not started.
31 // (This is only activated when DELETE_URSK_FOR_CCC_SESSION=1 is set
32 // from config)
33 //
34 // 3. Call suspend to kernel driver on idle (optional/experimental)
35 //
36 // (This is only activated when AUTO_SUSPEND_ENABLED=1 is set from config)
37 // Tracks the each session's status and automatically requests suspend
38 // to kernel driver when it's idle for a given duration,
39 // and resumes the device before sending any commands.
40 // SessionTracks detects UWBS is in idle when there's no session created.
41 //
42 //
43 // For CCC Session, in case `OVERRIDE_STS_INDEX_FOR_CCC_SESSION` is set,
44 // 1) Set STS Index as as non Zero in case new ranging session
45 // 2) Set STS Index to Last CCC STS Index + 1 in case of restarting a session.
46 //
47 
48 class SessionTrack {
49 private:
50   // Session
51   struct SessionInfo {
52     uint32_t  session_id_;
53     uint8_t   session_type_;
54     uint8_t   session_state_;
55     uint8_t   channel_;
56     bool      ranging_started_;
SessionInfoSessionTrack::SessionInfo57     SessionInfo(uint32_t session_id, uint8_t session_type) :
58       session_id_(session_id),
59       session_type_(session_type),
60       session_state_(UCI_MSG_SESSION_STATE_UNDEFINED),
61       channel_(0),
62       ranging_started_(false) {
63     }
64   };
65   enum class SessionTrackWorkType {
66     IDLE = 0,
67     REFRESH_IDLE,
68     ACTIVATE,
69     IDLE_TIMER_FIRED,
70     DELETE_URSK,
71     STOP,
72   };
73   enum class PowerState {
74     SUSPEND = 0,
75     IDLE,
76     ACTIVE,
77   };
78   struct SessionTrackMsg {
79     SessionTrackWorkType type_;
80     std::shared_ptr<SessionInfo> session_info_;
81     bool sync_;
82     std::condition_variable cond_;
83 
SessionTrackMsgSessionTrack::SessionTrackMsg84     SessionTrackMsg(SessionTrackWorkType type, bool sync) : type_(type), sync_(sync) { }
85 
86     // Per-session work item
SessionTrackMsgSessionTrack::SessionTrackMsg87     SessionTrackMsg(SessionTrackWorkType type, std::shared_ptr<SessionInfo> session_info, bool sync) :
88       type_(type), session_info_(session_info), sync_(sync) { }
89   };
90   static constexpr unsigned long kAutoSuspendTimeoutDefaultMs_ = (30 * 1000);
91   static constexpr long kQueueTimeoutMs = 500;
92   static constexpr long kUrskDeleteNtfTimeoutMs = 500;
93 
94 private:
95   std::shared_ptr<phNxpUciHal_RxHandler> rx_handler_session_status_ntf_;
96   std::unordered_map<uint32_t, std::shared_ptr<SessionInfo>> sessions_;
97   std::mutex sessions_lock_;
98 
99   bool auto_suspend_enabled_;
100   bool delete_ursk_ccc_enabled_;
101   bool calibration_delayed_;
102   std::atomic<PowerState> power_state_;
103   bool idle_timer_started_;
104   unsigned long idle_timeout_ms_;
105   bool override_sts_index_for_ccc_;
106 
107   std::thread worker_thread_;
108   std::mutex sync_mutex_;
109   uint32_t idle_timer_;
110   std::unique_ptr<MessageQueue<SessionTrackMsg>> msgq_;
111 
112 public:
SessionTrack()113   SessionTrack() :
114     auto_suspend_enabled_(false),
115     delete_ursk_ccc_enabled_(false),
116     calibration_delayed_(false),
117     power_state_(PowerState::IDLE),
118     idle_timer_started_(false),
119     idle_timeout_ms_(kAutoSuspendTimeoutDefaultMs_),
120     override_sts_index_for_ccc_(true)
121   {
122     sessions_.clear();
123 
124     msgq_ = std::make_unique<MessageQueue<SessionTrackMsg>>("SessionTrack");
125     worker_thread_ = std::thread(&SessionTrack::PowerManagerWorker, this);
126 
127     unsigned long numval = 0;
128 
129     if (NxpConfig_GetNum(NAME_DELETE_URSK_FOR_CCC_SESSION, &numval, sizeof(numval)) && numval) {
130       delete_ursk_ccc_enabled_ = true;
131     }
132 
133     // Default on
134     if (NxpConfig_GetNum(NAME_OVERRIDE_STS_INDEX_FOR_CCC_SESSION, &numval, sizeof(numval)) && !numval) {
135       override_sts_index_for_ccc_ = false;
136     }
137 
138     if (NxpConfig_GetNum(NAME_AUTO_SUSPEND_ENABLE, &numval, sizeof(numval)) && numval) {
139       auto_suspend_enabled_ = true;
140 
141       NxpConfig_GetNum(NAME_AUTO_SUSPEND_TIMEOUT_MS, &idle_timeout_ms_, sizeof(idle_timeout_ms_));
142 
143       // Idle timer is only activated when AUTO_SUSPEND_ENABLED=1
144       // device suspend won't be triggered when it's not activated.
145       idle_timer_ = phOsalUwb_Timer_Create();
146       RefreshIdle();
147     }
148 
149     // register SESSION_STATUS_NTF rx handler
150     rx_handler_session_status_ntf_ = phNxpUciHal_rx_handler_add(
151       UCI_MT_NTF, UCI_GID_SESSION_MANAGE, UCI_MSG_SESSION_STATUS_NTF,
152       false, false,
153       std::bind(&SessionTrack::OnSessionStatusNtf, this, std::placeholders::_1, std::placeholders::_2));
154   }
155 
~SessionTrack()156   virtual ~SessionTrack() {
157     phNxpUciHal_rx_handler_del(rx_handler_session_status_ntf_);
158 
159     if (auto_suspend_enabled_) {
160       phOsalUwb_Timer_Delete(idle_timer_);
161     }
162     auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::STOP, true);
163     QueueSessionTrackWork(msg);
164     worker_thread_.join();
165   }
166 
167   // Called upon upper-layer's SESSION_INIT_CMD
OnSessionInit(size_t packet_len,const uint8_t * packet)168   void OnSessionInit(size_t packet_len, const uint8_t *packet)
169   {
170     if (packet_len != UCI_MSG_SESSION_STATE_INIT_CMD_LEN)
171       return;
172 
173     uint32_t session_id = le_bytes_to_cpu<uint32_t>(&packet[UCI_MSG_SESSION_STATE_INIT_CMD_ID_OFFSET]);
174     uint8_t session_type = packet[UCI_MSG_SESSION_STATE_INIT_CMD_TYPE_OFFSET];
175 
176     // Check SESSION_INIT_RSP for SessionID - Handle matching
177     auto session_init_rsp_cb =
178       [this, session_id, session_type](size_t packet_len, const uint8_t *packet)
179     {
180       if (packet_len != UCI_MSG_SESSION_STATE_INIT_RSP_LEN )
181         return;
182 
183       uint8_t status = packet[UCI_MSG_SESSION_STATE_INIT_RSP_STATUS_OFFSET];
184       uint32_t handle = le_bytes_to_cpu<uint32_t>(&packet[UCI_MSG_SESSION_STATE_INIT_RSP_HANDLE_OFFSET]);
185       if (status != UWBSTATUS_SUCCESS)
186         return;
187 
188       bool was_idle;
189       {
190         std::lock_guard<std::mutex> lock(sessions_lock_);
191 
192         was_idle = IsDeviceIdle();
193 
194         sessions_.emplace(std::make_pair(handle,
195                                          std::make_shared<SessionInfo>(session_id, session_type)));
196       }
197       if (was_idle) {
198         NXPLOG_UCIHAL_D("Queue Active");
199         auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::ACTIVATE, false);
200         QueueSessionTrackWork(msg);
201       }
202     };
203 
204     // XXX: This rx handler can be called multiple times on
205     // UCI_STATUS_COMMAND_RETRY(0xA) from SESSION_INIT_CMD
206     phNxpUciHal_rx_handler_add(UCI_MT_RSP, UCI_GID_SESSION_MANAGE,
207       UCI_MSG_SESSION_STATE_INIT, false, true, session_init_rsp_cb);
208   }
209 
210   // Called by upper-layer's SetAppConfig command handler
OnChannelConfig(uint32_t session_handle,uint8_t channel)211   void OnChannelConfig(uint32_t session_handle, uint8_t channel) {
212     // Update channel info
213     std::lock_guard<std::mutex> lock(sessions_lock_);
214     auto pSessionInfo = GetSessionInfo(session_handle);
215     if (!pSessionInfo)
216       return;
217     pSessionInfo->channel_ = channel;
218   }
219 
220   // Called by upper-layer's SetCountryCode command handler,
221   // Check whether per-country calibration can be executed.
222   // phNxpUciHal_Runtime_Settings_t should've been updated.
OnCountryCodeChanged()223   void OnCountryCodeChanged() {
224     phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
225     NXPLOG_UCIHAL_D("SessionTrack: OnCountryCodeChanged");
226 
227     calibration_delayed_ = false;
228     std::vector<uint32_t> blocked_session_handles;
229     {
230       std::lock_guard<std::mutex> lock(sessions_lock_);
231       for (const auto elem : sessions_) {
232         auto session_handle = elem.first;
233         auto pSessionInfo = elem.second;
234 
235         if(pSessionInfo->session_state_ != UCI_MSG_SESSION_STATE_ACTIVE)
236           continue;
237         // there's active sessions existed, delay per-country calibrations
238         calibration_delayed_ = true;
239         if (!rt_set->uwb_enable || rt_set->restricted_channel_mask & (1 << pSessionInfo->channel_)) {
240           blocked_session_handles.push_back(session_handle);
241         }
242       }
243     }
244 
245     if (rt_set->uwb_enable && !calibration_delayed_) {
246       NXPLOG_UCIHAL_D("SessionTrack: no active sessions, execute per-country cal now.")
247       apply_per_country_calibrations();
248     } else {
249       NXPLOG_UCIHAL_D("SessionTrack: device is in active state, delay per-country cal.")
250       // stop all sessions affected by new country code's restrictions
251       for (auto session_handle : blocked_session_handles) {
252         NXPLOG_UCIHAL_D("SessionTrack: stop session (handle=0x%08x) due to country code restrictions", session_handle);
253         // Can issue an UCI command. This function is only called from upper-layer thread.
254         StopRanging(session_handle);
255       }
256     }
257   }
258 
RefreshIdle()259   void RefreshIdle() {
260     auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::REFRESH_IDLE, true);
261     QueueSessionTrackWork(msg);
262   }
263 
OnSessionStart(size_t packet_len,const uint8_t * packet)264   void OnSessionStart(size_t packet_len, const uint8_t *packet) {
265     if (packet_len != UCI_MSG_SESSION_START_CMD_LENGTH)
266       return;
267 
268     if (!override_sts_index_for_ccc_) {
269       return; /* Not needed / used... */
270     }
271 
272     uint32_t session_handle = le_bytes_to_cpu<uint32_t>(&packet[UCI_MSG_SESSION_START_HANDLE_OFFSET]);
273 
274     std::shared_ptr<SessionInfo> pSessionInfo;
275     {
276       std::lock_guard<std::mutex> lock(sessions_lock_);
277       pSessionInfo = GetSessionInfo(session_handle);
278     }
279 
280     // Check STS_INDEX and fetch if it was not set by upper-layer
281     if (!pSessionInfo || pSessionInfo->session_type_ != kSessionType_CCCRanging)
282       return;
283 
284     auto result = QueryStsIndex(session_handle);
285     if (!result.first) {
286       NXPLOG_UCIHAL_E("SessionTrack: failed to query sts index, session_handle=0x%x", session_handle);
287       return;
288     }
289 
290     // When it's resuming session, FW gives 0xFFFFFFFF when STS_INDEX was not set (SR100 D50.21)
291     if (result.second != 0 && result.second != 0xFFFFFFFF) {
292       NXPLOG_UCIHAL_D("SessionTrack: sts_index0 already set, skip fetching it");
293       return;
294     }
295 
296     if (!pSessionInfo->ranging_started_) {
297       // first ranging
298       NXPLOG_UCIHAL_D("SessionTrack: session handle 0x%x has zero sts_index0, fetch it", session_handle);
299       uint32_t new_sts_index = PickRandomStsIndex();
300       SetStsIndex(session_handle, new_sts_index);
301     } else {
302       // resuming ranging, sts_index0 should be incremented
303       NXPLOG_UCIHAL_D("SessionTrack: session handle 0x%x doesn't have valid sts_index0, increment it", session_handle);
304 
305       result = QueryLastStsIndexUsed(session_handle);
306       if (!result.first) {
307         NXPLOG_UCIHAL_E("SessionTrack: failed to query last sts index used, session_handle=0x%x", session_handle);
308         return;
309       }
310       // increment it from last sts_index (just +1)
311       uint32_t new_sts_index = (result.second + 1) & ((1 << 30) - 1);
312       SetStsIndex(session_handle, new_sts_index);
313     }
314   }
315 
316 private:
317   // Send SESSION_STOP_CMD
StopRanging(uint32_t session_handle)318   void StopRanging(uint32_t session_handle) {
319     uint8_t session_handle_bytes[4];
320     cpu_to_le_bytes(session_handle_bytes, session_handle);
321 
322     std::vector<uint8_t> packet{(UCI_MT_CMD << UCI_MT_SHIFT) | UCI_GID_SESSION_CONTROL, UCI_MSG_SESSION_STOP, 0, 0};
323     packet.insert(packet.end(), std::begin(session_handle_bytes), std::end(session_handle_bytes));
324     packet[UCI_PAYLOAD_LENGTH_OFFSET] = packet.size() - UCI_MSG_HDR_SIZE;
325 
326     auto ret = phNxpUciHal_send_ext_cmd(packet.size(), packet.data());
327     if (ret != UWBSTATUS_SUCCESS) {
328       NXPLOG_UCIHAL_E("SessionTrack: Failed to stop session handle 0x%08x", session_handle);
329     }
330   }
331 
332   // Send URSK_DELETE_CMD
DeleteUrsk(std::shared_ptr<SessionInfo> session_info)333   void DeleteUrsk(std::shared_ptr<SessionInfo> session_info) {
334     if (!session_info)
335       return;
336 
337     phNxpUciHal_Sem_t urskDeleteNtfWait;
338     phNxpUciHal_init_cb_data(&urskDeleteNtfWait, NULL);
339 
340     phNxpUciHal_rx_handler_add(UCI_MT_RSP, UCI_GID_PROPRIETARY_0X0F,
341       UCI_MSG_URSK_DELETE, true, true,
342       [](size_t packet_len, const uint8_t *packet) {
343         if (packet_len < 5)
344           return;
345         if (packet[4] != UWBSTATUS_SUCCESS) {
346           NXPLOG_UCIHAL_E("SessionTrack: URSR_DELETE failed, rsp status=0x%x", packet[4]);
347         }
348       }
349     );
350     phNxpUciHal_rx_handler_add(UCI_MT_NTF, UCI_GID_PROPRIETARY_0X0F,
351       UCI_MSG_URSK_DELETE, true, true,
352       [&urskDeleteNtfWait](size_t packet_len, const uint8_t *packet) {
353         if (packet_len < 6)
354           return;
355         uint8_t status = packet[4];
356         uint8_t nr = packet[5];
357 
358         // We always issue URSK_DELETE_CMD with one Session ID and wait for it,
359         // Number of entries should be 1.
360         if (nr != 1 || packet_len != (6 + 5 * nr)) {
361           NXPLOG_UCIHAL_E("SessionTrack: unrecognized packet type of URSK_DELETE_NTF");
362           urskDeleteNtfWait.status = UWB_DEVICE_ERROR;
363         } else {
364           if (status != UWBSTATUS_SUCCESS) {
365             NXPLOG_UCIHAL_E("SessionTrack: URSK_DELETE failed, ntf status=0x%x", status);
366             urskDeleteNtfWait.status = status;
367           } else {
368             uint32_t session_id = le_bytes_to_cpu<uint32_t>(&packet[6]);
369             uint8_t del_status = packet[10];
370             NXPLOG_UCIHAL_D("SessionTrack: URSK_DELETE done, deletion_status=%u", del_status);
371 
372             // 0: RDS removed successfully
373             // 1: RDS not found
374             // 2: Interface error encountered
375             if (del_status == 0 || del_status == 1) {
376               urskDeleteNtfWait.status = status;
377             } else {
378               urskDeleteNtfWait.status = UWB_DEVICE_ERROR;
379             }
380           }
381         }
382         SEM_POST(&urskDeleteNtfWait);
383       }
384     );
385 
386     NXPLOG_UCIHAL_D("SessionTrack: URSK_DELETE for session ID 0x%x", session_info->session_id_);
387     uint8_t session_id_bytes[4];
388     cpu_to_le_bytes(session_id_bytes, session_info->session_id_);
389 
390     std::vector<uint8_t> packet{(UCI_MT_CMD << UCI_MT_SHIFT) | UCI_GID_PROPRIETARY_0X0F,
391       UCI_MSG_URSK_DELETE, 0, 0};
392 
393     packet.push_back(1);  // Num of Session IDs = 1
394     packet.insert(packet.end(), std::begin(session_id_bytes), std::end(session_id_bytes));
395     packet[UCI_PAYLOAD_LENGTH_OFFSET] = packet.size() - UCI_MSG_HDR_SIZE;
396 
397     auto ret = phNxpUciHal_send_ext_cmd(packet.size(), packet.data());
398     if (ret != UWBSTATUS_SUCCESS) {
399       NXPLOG_UCIHAL_E("SessionTrack: Failed to delete URSK for session id 0x%08x",
400         session_info->session_id_);
401     }
402 
403     if (phNxpUciHal_sem_timed_wait_msec(&urskDeleteNtfWait, kUrskDeleteNtfTimeoutMs)) {
404       NXPLOG_UCIHAL_E("SessionTrack: Failed(timeout) to delete URSK for session id 0x%08x",
405         session_info->session_id_);
406     }
407 
408     phNxpUciHal_cleanup_cb_data(&urskDeleteNtfWait);
409   }
410 
GetAppConfLe32(uint32_t session_handle,uint8_t tag)411   std::pair<bool, uint32_t> GetAppConfLe32(uint32_t session_handle, uint8_t tag)
412   {
413     uint32_t val = 0;
414     bool result = false;
415 
416     phNxpUciHal_rx_handler_add(UCI_MT_RSP, UCI_GID_SESSION_MANAGE,
417       UCI_MSG_SESSION_GET_APP_CONFIG, true, true,
418       [&val, &result, tag](size_t packet_len, const uint8_t *packet) {
419         if (packet_len != 12)
420           return;
421 
422         if (packet[4] != UWBSTATUS_SUCCESS) {
423           NXPLOG_UCIHAL_E("SessionTrack: GetAppConfig failed, status=0x%02x", packet[4]);
424           return;
425         }
426         if (packet[5] != 1) {
427           NXPLOG_UCIHAL_E("SessionTrack: GetAppConfig failed, nr=%u", packet[5]);
428           return;
429         }
430         if (packet[6] != tag) {
431           NXPLOG_UCIHAL_E("SessionTrack: GetAppConfig failed, tag=0x%02x, expected=0x%02x", packet[6], tag);
432           return;
433         }
434         if (packet[7] != 4) {
435           NXPLOG_UCIHAL_E("SessionTrack: GetAppConfig failed, len=%u", packet[7]);
436           return;
437         }
438         val = le_bytes_to_cpu<uint32_t>(&packet[8]);
439         result = true;
440       }
441     );
442 
443     std::vector<uint8_t> packet{(UCI_MT_CMD << UCI_MT_SHIFT) | UCI_GID_SESSION_MANAGE,
444       UCI_MSG_SESSION_GET_APP_CONFIG, 0,  0};
445 
446     uint8_t session_handle_bytes[4];
447     cpu_to_le_bytes(session_handle_bytes, session_handle);
448 
449     packet.insert(packet.end(), std::begin(session_handle_bytes), std::end(session_handle_bytes));
450     packet.push_back(1);  // Num of Prameters
451     packet.push_back(tag);  // The parameter. STS Index
452     packet[UCI_PAYLOAD_LENGTH_OFFSET] = packet.size() - UCI_MSG_HDR_SIZE;
453 
454     auto ret = phNxpUciHal_send_ext_cmd(packet.size(), packet.data());
455     if (ret != UWBSTATUS_SUCCESS) {
456       return std::pair(false, 0);
457     } else {
458       return std::pair(result, val);
459     }
460   }
461 
QueryStsIndex(uint32_t session_handle)462   std::pair<bool, uint32_t> QueryStsIndex(uint32_t session_handle)
463   {
464     return GetAppConfLe32(session_handle, UCI_APP_CONFIG_FIRA_STS_INDEX);
465   }
466 
QueryLastStsIndexUsed(uint32_t session_handle)467   std::pair<bool, uint32_t> QueryLastStsIndexUsed(uint32_t session_handle)
468   {
469     return GetAppConfLe32(session_handle, UCI_APP_CONFIG_CCC_LAST_STS_INDEX_USED);
470   }
471 
SetStsIndex(uint32_t session_handle,uint32_t sts_index)472   bool SetStsIndex(uint32_t session_handle, uint32_t sts_index)
473   {
474     NXPLOG_UCIHAL_D("SessionTrack: SetStsIndex 0x%x for session handle 0x%x", sts_index, session_handle);
475 
476     uint8_t session_handle_bytes[4];
477     uint8_t sts_index_bytes[4];
478 
479     cpu_to_le_bytes(session_handle_bytes, session_handle);
480     cpu_to_le_bytes(sts_index_bytes, sts_index);
481 
482     std::vector<uint8_t> packet{(UCI_MT_CMD << UCI_MT_SHIFT) | UCI_GID_SESSION_MANAGE,
483       UCI_MSG_SESSION_SET_APP_CONFIG, 0,  0};
484 
485     packet.insert(packet.end(), std::begin(session_handle_bytes), std::end(session_handle_bytes));
486     packet.push_back(1);  // Num of Prameters
487     packet.push_back(UCI_APP_CONFIG_FIRA_STS_INDEX);  // The parameter. STS Index
488     packet.push_back(4);  // Sts Index Size...
489     packet.insert(packet.end(), std::begin(sts_index_bytes), std::end(sts_index_bytes));
490     packet[UCI_PAYLOAD_LENGTH_OFFSET] = packet.size() - UCI_MSG_HDR_SIZE;
491 
492     auto ret = phNxpUciHal_send_ext_cmd(packet.size(), packet.data());
493     if (ret != UWBSTATUS_SUCCESS) {
494       NXPLOG_UCIHAL_E("SessionTrack: Failed to SetStsIndex");
495       return false;
496     }
497     return true;
498   }
499 
PickRandomStsIndex()500   uint32_t PickRandomStsIndex()
501   {
502     std::random_device rdev;
503     std::mt19937 rng(rdev());
504 
505     // valid range is [0, 2~30), but use half of it to prevent roll over
506     std::uniform_int_distribution<std::mt19937::result_type> sts_index(0, (1 << 16) - 1);
507     return sts_index(rng);
508   }
509 
510   // UCI_MSG_SESSION_STATUS_NTF rx handler
OnSessionStatusNtf(size_t packet_len,const uint8_t * packet)511   void OnSessionStatusNtf(size_t packet_len, const uint8_t* packet) {
512     if (packet_len != UCI_MSG_SESSION_STATUS_NTF_LENGTH) {
513       NXPLOG_UCIHAL_E("SessionTrack: SESSION_STATUS_NTF packet parse error");
514       return;
515     }
516 
517     uint32_t session_handle = le_bytes_to_cpu<uint32_t>(&packet[UCI_MSG_SESSION_STATUS_NTF_HANDLE_OFFSET]);
518     uint8_t session_state = packet[UCI_MSG_SESSION_STATUS_NTF_STATE_OFFSET];
519 
520     bool is_idle = false;
521     {
522       std::lock_guard<std::mutex> lock(sessions_lock_);
523 
524       auto pSessionInfo = GetSessionInfo(session_handle);
525       if (pSessionInfo) {
526         NXPLOG_UCIHAL_D("SessionTrack: update session handle 0x%08x state %u", session_handle, session_state);
527         pSessionInfo->session_state_ = session_state;
528       }
529 
530       if (session_state == UCI_MSG_SESSION_STATE_DEINIT) {
531         NXPLOG_UCIHAL_D("SessionTrack: remove session handle 0x%08x", session_handle);
532 
533         if (delete_ursk_ccc_enabled_ && pSessionInfo &&
534             pSessionInfo->session_type_ == kSessionType_CCCRanging) {
535           // If this CCC ranging session, issue DELETE_URSK_CMD for this session.
536           auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::DELETE_URSK, pSessionInfo, true);
537           QueueSessionTrackWork(msg);
538         }
539         sessions_.erase(session_handle);
540         is_idle = IsDeviceIdle();
541       } else if (session_state == UCI_MSG_SESSION_STATE_ACTIVE) {
542         // mark this session has been started at
543         pSessionInfo->ranging_started_ = true;
544       }
545     }
546 
547     if (is_idle) { // transition to IDLE
548       NXPLOG_UCIHAL_D("Queue Idle");
549       auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::IDLE, false);
550       QueueSessionTrackWork(msg);
551     }
552   }
553 
IdleTimerCallback(uint32_t TimerId,void * pContext)554   static void IdleTimerCallback(uint32_t TimerId, void* pContext) {
555     SessionTrack *mgr = static_cast<SessionTrack*>(pContext);
556     auto msg = std::make_shared<SessionTrackMsg>(SessionTrackWorkType::IDLE_TIMER_FIRED, false);
557     mgr->QueueSessionTrackWork(msg);
558   }
559 
PowerIdleTimerStop()560   void PowerIdleTimerStop() {
561     if (!auto_suspend_enabled_)
562       return;
563 
564     NXPLOG_UCIHAL_D("SessionTrack: stop idle timer");
565     if (idle_timer_started_) {
566       if (phOsalUwb_Timer_Stop(idle_timer_) != UWBSTATUS_SUCCESS) {
567         NXPLOG_UCIHAL_E("SessionTrack: idle timer stop failed");
568       }
569       idle_timer_started_ = false;
570     }
571   }
PowerIdleTimerRefresh()572   void PowerIdleTimerRefresh() {
573     if (!auto_suspend_enabled_)
574       return;
575 
576     NXPLOG_UCIHAL_D("SessionTrack: refresh idle timer, %lums", idle_timeout_ms_);
577     if (idle_timer_started_) {
578       if (phOsalUwb_Timer_Stop(idle_timer_) != UWBSTATUS_SUCCESS) {
579         NXPLOG_UCIHAL_E("SessionTrack: idle timer stop failed");
580       }
581     }
582     if (phOsalUwb_Timer_Start(idle_timer_, idle_timeout_ms_, IdleTimerCallback, this) != UWBSTATUS_SUCCESS) {
583       NXPLOG_UCIHAL_E("SessionTrack: idle timer start failed");
584     }
585     idle_timer_started_ = true;
586   }
587 
588   // Worker thread for auto suspend
PowerManagerWorker()589   void PowerManagerWorker() {
590     NXPLOG_UCIHAL_D("SessionTrack: worker thread started.")
591 
592     bool stop_thread = false;
593     while (!stop_thread) {
594       auto msg = msgq_->recv();
595       if (!msg) {
596         NXPLOG_UCIHAL_E("Power State: CRITICAL: worker thread received a bad message!, stop the queue");
597         break;
598       }
599       NXPLOG_UCIHAL_D("SessionTrack: work %d state %d",
600         static_cast<int>(msg->type_), static_cast<int>(power_state_.load()));
601 
602       switch (msg->type_) {
603       case SessionTrackWorkType::IDLE:
604         if (calibration_delayed_) {
605           NXPLOG_UCIHAL_D("SessionTrack: No active session, execute per-country calibrations");
606           CONCURRENCY_LOCK();
607           apply_per_country_calibrations();
608           CONCURRENCY_UNLOCK();
609           calibration_delayed_ = false;
610         }
611         power_state_ = PowerState::IDLE;
612         PowerIdleTimerRefresh();
613         break;
614       case SessionTrackWorkType::REFRESH_IDLE:
615         if (power_state_ == PowerState::SUSPEND) {
616           NXPLOG_UCIHAL_D("SessionTrack: resume");
617           phTmlUwb_Resume();
618           power_state_ = PowerState::IDLE;
619         }
620         if (power_state_ == PowerState::IDLE) {
621           PowerIdleTimerRefresh();
622         }
623         break;
624       case SessionTrackWorkType::ACTIVATE:
625         if (power_state_ == PowerState::SUSPEND) {
626           NXPLOG_UCIHAL_E("SessionTrack: activated while in suspend!");
627           phTmlUwb_Resume();
628         }
629         PowerIdleTimerStop();
630         power_state_ = PowerState::ACTIVE;
631         break;
632       case SessionTrackWorkType::IDLE_TIMER_FIRED:
633         if (power_state_ == PowerState::IDLE) {
634           NXPLOG_UCIHAL_D("SessionTrack: idle timer expired, go suspend");
635           power_state_ = PowerState::SUSPEND;
636           phTmlUwb_Suspend();
637         } else {
638           NXPLOG_UCIHAL_E("SessionTrack: idle timer expired while in %d",
639             static_cast<int>(power_state_.load()));
640         }
641         break;
642       case SessionTrackWorkType::DELETE_URSK:
643         DeleteUrsk(msg->session_info_);
644         break;
645       case SessionTrackWorkType::STOP:
646         stop_thread = true;
647         break;
648       default:
649         NXPLOG_UCIHAL_E("SessionTrack: worker thread received a bad message!");
650         break;
651       }
652       if (msg->sync_)
653         msg->cond_.notify_one();
654     }
655     if (idle_timer_started_) {
656       PowerIdleTimerStop();
657     }
658 
659     NXPLOG_UCIHAL_D("SessionTrack: worker thread exit.");
660   }
661 
QueueSessionTrackWork(std::shared_ptr<SessionTrackMsg> msg)662   void QueueSessionTrackWork(std::shared_ptr<SessionTrackMsg> msg) {
663     msgq_->send(msg);
664 
665     if (msg->sync_) {
666       std::unique_lock<std::mutex> lock(sync_mutex_);
667       if (msg->cond_.wait_for(lock, std::chrono::milliseconds(kQueueTimeoutMs)) == std::cv_status::timeout) {
668         NXPLOG_UCIHAL_E("SessionTrack: timeout to process %d", static_cast<int>(msg->type_));
669       }
670     }
671   }
672 
GetSessionInfo(uint32_t session_handle)673   std::shared_ptr<SessionInfo> GetSessionInfo(uint32_t session_handle) {
674     auto it = sessions_.find(session_handle);
675     if (it == sessions_.end()) {
676       NXPLOG_UCIHAL_E("SessionTrack: Session 0x%08x not registered", session_handle);
677       return NULL;
678     }
679     return it->second;
680   }
681 
IsDeviceIdle()682   bool IsDeviceIdle() {
683     return sessions_.size() == 0;
684   }
685 };
686 
687 static std::unique_ptr<SessionTrack> gSessionTrack;
688 
SessionTrack_init()689 void SessionTrack_init()
690 {
691   gSessionTrack = std::make_unique<SessionTrack>();
692 }
693 
SessionTrack_deinit()694 void SessionTrack_deinit()
695 {
696   gSessionTrack.reset();
697 }
698 
SessionTrack_onCountryCodeChanged()699 void SessionTrack_onCountryCodeChanged()
700 {
701   if (gSessionTrack)
702     gSessionTrack->OnCountryCodeChanged();
703 }
704 
SessionTrack_onAppConfig(uint32_t session_handle,uint8_t channel)705 void SessionTrack_onAppConfig(uint32_t session_handle, uint8_t channel)
706 {
707   if (gSessionTrack)
708     gSessionTrack->OnChannelConfig(session_handle, channel);
709 }
710 
SessionTrack_keepAlive()711 void SessionTrack_keepAlive()
712 {
713   if (gSessionTrack)
714     gSessionTrack->RefreshIdle();
715 }
716 
SessionTrack_onSessionInit(size_t packet_len,const uint8_t * packet)717 void SessionTrack_onSessionInit(size_t packet_len, const uint8_t *packet)
718 {
719   if (gSessionTrack)
720     gSessionTrack->OnSessionInit(packet_len, packet);
721 }
722 
SessionTrack_onSessionStart(size_t packet_len,const uint8_t * packet)723 void SessionTrack_onSessionStart(size_t packet_len, const uint8_t *packet)
724 {
725   if (gSessionTrack)
726     gSessionTrack->OnSessionStart(packet_len, packet);
727 }
728