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