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