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