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