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