1 #include "NxpUwbChip.h"
2 #include "phNxpConfig.h"
3 #include "phNxpUciHal.h"
4 #include "phNxpUciHal_ext.h"
5 #include "phNxpUciHal_utils.h"
6 #include "phUwbStatus.h"
7 #include "phUwbTypes.h"
8 #include "phNxpUwbCalib.h"
9 #include "uci_defs.h"
10
11 #define UCI_MSG_UWB_ESE_BINDING_LEN 11
12 #define UCI_MSG_UWB_ESE_BINDING_OFFSET_COUNT 5
13 #define UCI_MSG_UWB_ESE_BINDING_OFFSET_BINDING_STATE 6
14
15 extern phNxpUciHal_Control_t nxpucihal_ctrl;
16
report_binding_status(uint8_t binding_status)17 static void report_binding_status(uint8_t binding_status)
18 {
19 // BINDING_STATUS_NTF
20 uint8_t data_len = 5;
21 uint8_t buffer[5];
22 buffer[0] = 0x6E;
23 buffer[1] = 0x06;
24 buffer[2] = 0x00;
25 buffer[3] = 0x01;
26 buffer[4] = binding_status;
27 nxpucihal_ctrl.rx_data_len = 5;
28 if (nxpucihal_ctrl.p_uwb_stack_data_cback != NULL) {
29 (*nxpucihal_ctrl.p_uwb_stack_data_cback)(data_len, buffer);
30 }
31 }
32
33 /******************************************************************************
34 * Function otp_read_data
35 *
36 * Description Read OTP calibration data
37 *
38 * Returns true on success
39 *
40 ******************************************************************************/
otp_read_data(const uint8_t channel,const uint8_t param_id,uint8_t * buffer,size_t len)41 static bool otp_read_data(const uint8_t channel, const uint8_t param_id, uint8_t *buffer, size_t len)
42 {
43 phNxpUciHal_Sem_t calib_data_ntf_wait;
44 phNxpUciHal_init_cb_data(&calib_data_ntf_wait, NULL);
45
46 // NXP_READ_CALIB_DATA_NTF
47 bool received = false;
48 auto read_calib_ntf_cb =
49 [&] (size_t packet_len, const uint8_t *packet) mutable
50 {
51 // READ_CALIB_DATA_NTF: status(1), length-of-payload(1), payload(N)
52 const uint8_t plen = packet[3]; // payload-length
53 const uint8_t *p = &packet[4]; // payload
54
55 if (plen < 2) {
56 NXPLOG_UCIHAL_E("Otp read: bad payload length %u", plen);
57 } else if (p[0] != UCI_STATUS_OK) {
58 NXPLOG_UCIHAL_E("Otp read: bad status=0x%x", nxpucihal_ctrl.p_rx_data[4]);
59 } else if (p[1] != len) {
60 NXPLOG_UCIHAL_E("Otp read: size mismatch %u (expected %zu for param 0x%x)",
61 p[1], len, param_id);
62 } else {
63 memcpy(buffer, &p[2], len);
64 received = true;
65 SEM_POST(&calib_data_ntf_wait);
66 }
67 };
68 auto handler = phNxpUciHal_rx_handler_add(
69 UCI_MT_NTF, UCI_GID_PROPRIETARY_0X0A, UCI_MSG_READ_CALIB_DATA,
70 true, true, read_calib_ntf_cb);
71
72
73 // READ_CALIB_DATA_CMD
74 std::vector<uint8_t> packet{(UCI_MT_CMD << UCI_MT_SHIFT) | UCI_GID_PROPRIETARY_0X0A, UCI_MSG_READ_CALIB_DATA, 0x00, 0x03};
75 packet.push_back(channel);
76 packet.push_back(0x01); // OTP read option
77 packet.push_back(param_id);
78
79 tHAL_UWB_STATUS status = phNxpUciHal_send_ext_cmd(packet.size(), packet.data());
80 if (status != UWBSTATUS_SUCCESS) {
81 goto fail_otp_read_data;
82 }
83
84 phNxpUciHal_sem_timed_wait_sec(&calib_data_ntf_wait, 3);
85 if (!received) {
86 goto fail_otp_read_data;
87 }
88
89 phNxpUciHal_cleanup_cb_data(&calib_data_ntf_wait);
90 return true;
91
92 fail_otp_read_data:
93 phNxpUciHal_cleanup_cb_data(&calib_data_ntf_wait);
94 NXPLOG_UCIHAL_E("Failed to read OTP data id=%u", param_id);
95 return false;
96 }
97
sr1xx_read_otp(extcal_param_id_t id,uint8_t * data,size_t data_len,size_t * retlen)98 static tHAL_UWB_STATUS sr1xx_read_otp(extcal_param_id_t id, uint8_t *data, size_t data_len, size_t *retlen)
99 {
100 switch(id) {
101 case EXTCAL_PARAM_CLK_ACCURACY:
102 {
103 const size_t param_len = 6;
104 uint8_t otp_xtal_data[3];
105
106 if (data_len < param_len) {
107 NXPLOG_UCIHAL_E("Requested RF_CLK_ACCURACY_CALIB with %zu bytes (expected >= %zu)", data_len, param_len);
108 return UWBSTATUS_FAILED;
109 }
110 if (!otp_read_data(0x09, OTP_ID_XTAL_CAP_GM_CTRL, otp_xtal_data, sizeof(otp_xtal_data))) {
111 NXPLOG_UCIHAL_E("Failed to read OTP XTAL_CAP_GM_CTRL");
112 return UWBSTATUS_FAILED;
113 }
114 memset(data, 0, param_len);
115 // convert OTP_ID_XTAL_CAP_GM_CTRL to EXTCAL_PARAM_RX_ANT_DELAY
116 data[0] = otp_xtal_data[0]; // cap1
117 data[2] = otp_xtal_data[1]; // cap2
118 data[4] = otp_xtal_data[2]; // gm_current_control (default: 0x30)
119 *retlen = param_len;
120 return UWBSTATUS_SUCCESS;
121 }
122 break;
123 default:
124 NXPLOG_UCIHAL_E("Unsupported otp parameter %d", id);
125 return UWBSTATUS_FAILED;
126 }
127 }
128
129 //
130 // SR1XX Error handlers (Thermal Runaway, LOW VBATT)
131 //
132
sr1xx_handle_device_error()133 static void sr1xx_handle_device_error()
134 {
135 /* Send FW crash NTF to upper layer for triggering MW recovery */
136 phNxpUciHal_send_dev_error_status_ntf();
137 }
138
sr1xx_clear_device_error()139 static void sr1xx_clear_device_error()
140 {
141 }
142
143 //
144 // SE binding
145 //
146
147 // Temporarily disable DPD for binding, vendor config should re-enable it
sr1xx_disable_dpd()148 static tHAL_UWB_STATUS sr1xx_disable_dpd()
149 {
150 uint8_t buffer[] = {0x20, 0x04, 0x00, 0x04, 0x01, 0x01, 0x01, 0x00};
151 return phNxpUciHal_send_ext_cmd(sizeof(buffer), buffer);
152 }
153
154 /******************************************************************************
155 * Function sr1xx_do_bind
156 *
157 * Description Sends UWB_ESE_BINDING_CMD and returns
158 * updated binding status and remaining UWBS binding count
159 *
160 * Returns status
161 *
162 ******************************************************************************/
sr1xx_do_bind(uint8_t * binding_status,uint8_t * remain_count)163 static tHAL_UWB_STATUS sr1xx_do_bind(uint8_t *binding_status, uint8_t *remain_count)
164 {
165 tHAL_UWB_STATUS status;
166
167 // register rx handler for UWB_ESE_BINDING_NTF
168 phNxpUciHal_Sem_t binding_ntf_wait;
169 phNxpUciHal_init_cb_data(&binding_ntf_wait, NULL);
170
171 auto binding_ntf_cb =
172 [&](size_t packet_len, const uint8_t *packet) mutable
173 {
174 if (packet_len == UCI_MSG_UWB_ESE_BINDING_LEN) {
175 uint8_t status = packet[UCI_RESPONSE_STATUS_OFFSET];
176 if (status != UWBSTATUS_SUCCESS) {
177 NXPLOG_UCIHAL_E("UWB_ESE_BINDING_NTF: Binding failed, status=0x%x", status);
178 }
179 *binding_status = packet[UCI_MSG_UWB_ESE_BINDING_OFFSET_BINDING_STATE];
180 *remain_count = packet[UCI_MSG_UWB_ESE_BINDING_OFFSET_COUNT];
181 NXPLOG_UCIHAL_D("Received UWB_ESE_BINDING_NTF, status=0x%x, binding_state=0x%x, count=%u",
182 status, *binding_status, *remain_count);
183 SEM_POST(&binding_ntf_wait);
184 } else {
185 NXPLOG_UCIHAL_E("UWB_ESE_BINDING_NTF: packet length mismatched %zu", packet_len);
186 }
187 };
188 auto handler = phNxpUciHal_rx_handler_add(
189 UCI_MT_NTF, UCI_GID_PROPRIETARY_0X0F, UCI_MSG_UWB_ESE_BINDING,
190 true, true, binding_ntf_cb);
191
192 // UWB_ESE_BINDING_CMD
193 uint8_t buffer[] = {0x2F, 0x31, 0x00, 0x00};
194 status = phNxpUciHal_send_ext_cmd(sizeof(buffer), buffer);
195 if (status != UWBSTATUS_SUCCESS) {
196 NXPLOG_UCIHAL_E("Failed to send UWB_ESE_BINDING_CMD");
197 goto exit_do_bind;
198 }
199
200 if (phNxpUciHal_sem_timed_wait(&binding_ntf_wait) ||
201 binding_ntf_wait.status != UWBSTATUS_SUCCESS) {
202 NXPLOG_UCIHAL_E("Failed to retrieve UWB_ESE_BINDING_NTF");
203 goto exit_do_bind;
204 }
205
206 status = UWBSTATUS_SUCCESS;
207
208 exit_do_bind:
209 phNxpUciHal_rx_handler_del(handler);
210 phNxpUciHal_cleanup_cb_data(&binding_ntf_wait);
211 return status;
212 }
213
214 /******************************************************************************
215 * Function sr1xx_check_binding_status
216 *
217 * Description Send UWB_ESE_BINDING_CHECK_CMD and returns updated binding status
218 *
219 * Returns status
220 *
221 ******************************************************************************/
sr1xx_check_binding_status(uint8_t * binding_status)222 static tHAL_UWB_STATUS sr1xx_check_binding_status(uint8_t *binding_status)
223 {
224 tHAL_UWB_STATUS status;
225 *binding_status = UWB_DEVICE_UNKNOWN;
226
227 // register rx handler for UWB_ESE_BINDING_CHECK_NTF
228 uint8_t binding_status_got = UWB_DEVICE_UNKNOWN;
229 phNxpUciHal_Sem_t binding_check_ntf_wait;
230 phNxpUciHal_init_cb_data(&binding_check_ntf_wait, NULL);
231 auto binding_check_ntf_cb = [&](size_t packet_len, const uint8_t *packet) mutable {
232 if (packet_len >= UCI_RESPONSE_STATUS_OFFSET) {
233 binding_status_got = packet[UCI_RESPONSE_STATUS_OFFSET];
234 NXPLOG_UCIHAL_D("Received UWB_ESE_BINDING_CHECK_NTF, binding_status=0x%x", binding_status_got);
235 SEM_POST(&binding_check_ntf_wait);
236 }
237 };
238 auto handler = phNxpUciHal_rx_handler_add(
239 UCI_MT_NTF, UCI_GID_PROPRIETARY_0X0F, UCI_MSG_UWB_ESE_BINDING_CHECK,
240 true, true, binding_check_ntf_cb);
241
242 // UWB_ESE_BINDING_CHECK_CMD
243 uint8_t lock_cmd[] = {0x2F, 0x32, 0x00, 0x00};
244 status = phNxpUciHal_send_ext_cmd(sizeof(lock_cmd), lock_cmd);
245 if (status != UWBSTATUS_SUCCESS) {
246 goto exit_check_binding_status;
247 }
248
249 if (phNxpUciHal_sem_timed_wait(&binding_check_ntf_wait) ||
250 binding_check_ntf_wait.status != UWBSTATUS_SUCCESS) {
251 NXPLOG_UCIHAL_E("Failed to retrieve UWB_ESE_BINDING_CHECK_NTF");
252 goto exit_check_binding_status;
253 }
254
255 *binding_status = binding_status_got;
256 status = UWBSTATUS_SUCCESS;
257
258 exit_check_binding_status:
259 phNxpUciHal_rx_handler_del(handler);
260 phNxpUciHal_cleanup_cb_data(&binding_check_ntf_wait);
261 return status;
262 }
263
264 class NxpUwbChipSr1xx final : public NxpUwbChip {
265 public:
266 NxpUwbChipSr1xx();
267 virtual ~NxpUwbChipSr1xx();
268
269 tHAL_UWB_STATUS chip_init();
270 tHAL_UWB_STATUS core_init();
271 device_type_t get_device_type(const uint8_t *param, size_t param_len);
272 tHAL_UWB_STATUS read_otp(extcal_param_id_t id, uint8_t *data, size_t data_len, size_t *retlen);
273 tHAL_UWB_STATUS apply_calibration(extcal_param_id_t id, const uint8_t ch, const uint8_t *data, size_t data_len);
274 int16_t extra_group_delay(void);
275
276 private:
277 tHAL_UWB_STATUS check_binding();
278 void onDeviceStatusNtf(size_t packet_len, const uint8_t* packet);
279 void onGenericErrorNtf(size_t packet_len, const uint8_t* packet);
280 void onBindingStatusNtf(size_t packet_len, const uint8_t* packet);
281
282 private:
283 UciHalRxHandler deviceStatusNtfHandler_;
284 UciHalRxHandler genericErrorNtfHandler_;
285 UciHalRxHandler bindingStatusNtfHandler_;
286 UciHalSemaphore bindingStatusNtfWait_;
287 uint8_t bindingStatus_;
288 };
289
NxpUwbChipSr1xx()290 NxpUwbChipSr1xx::NxpUwbChipSr1xx() :
291 bindingStatus_(UWB_DEVICE_UNKNOWN)
292 {
293 }
294
~NxpUwbChipSr1xx()295 NxpUwbChipSr1xx::~NxpUwbChipSr1xx()
296 {
297 }
298
onDeviceStatusNtf(size_t packet_len,const uint8_t * packet)299 void NxpUwbChipSr1xx::onDeviceStatusNtf(size_t packet_len, const uint8_t* packet)
300 {
301 if(packet_len > UCI_RESPONSE_STATUS_OFFSET) {
302 uint8_t status = packet[UCI_RESPONSE_STATUS_OFFSET];
303 if (status == UCI_STATUS_HW_RESET) {
304 sr1xx_clear_device_error();
305 }
306 }
307 }
308
onGenericErrorNtf(size_t packet_len,const uint8_t * packet)309 void NxpUwbChipSr1xx::onGenericErrorNtf(size_t packet_len, const uint8_t* packet)
310 {
311 if(packet_len > UCI_RESPONSE_STATUS_OFFSET) {
312 uint8_t status = packet[UCI_RESPONSE_STATUS_OFFSET];
313 if ( status == UCI_STATUS_THERMAL_RUNAWAY || status == UCI_STATUS_LOW_VBAT) {
314 nxpucihal_ctrl.isSkipPacket = 1;
315 sr1xx_handle_device_error();
316 }
317 }
318 }
319
onBindingStatusNtf(size_t packet_len,const uint8_t * packet)320 void NxpUwbChipSr1xx::onBindingStatusNtf(size_t packet_len, const uint8_t* packet)
321 {
322 if (packet_len > UCI_RESPONSE_STATUS_OFFSET) {
323 bindingStatus_ = packet[UCI_RESPONSE_STATUS_OFFSET];
324 NXPLOG_UCIHAL_D("BINDING_STATUS_NTF: 0x%x", bindingStatus_);
325 bindingStatusNtfWait_.post(UWBSTATUS_SUCCESS);
326 }
327 }
328
check_binding()329 tHAL_UWB_STATUS NxpUwbChipSr1xx::check_binding()
330 {
331 // Wait for Binding status notification
332 if (bindingStatusNtfWait_.getStatus() != UWBSTATUS_SUCCESS) {
333 bindingStatusNtfWait_.wait_timeout_msec(3000);
334 }
335 if (bindingStatusNtfWait_.getStatus() != UWBSTATUS_SUCCESS) {
336 NXPLOG_UCIHAL_E("Binding status notification timeout");
337
338 // Stop HAL init when it didn't receive the binding notification.
339 // or if it's not user mode fw, just continue
340 if (nxpucihal_ctrl.fw_boot_mode == USER_FW_BOOT_MODE)
341 return UWBSTATUS_FAILED;
342 else
343 return UWBSTATUS_SUCCESS;
344 }
345
346 uint32_t val = 0;
347 NxpConfig_GetNum(NAME_UWB_BINDING_LOCKING_ALLOWED, &val, sizeof(val));
348 bool isBindingLockingAllowed = !!val;
349 if (!isBindingLockingAllowed) {
350 return UWBSTATUS_SUCCESS;
351 }
352
353 NXPLOG_UCIHAL_E("Current binding status: 0x%x", bindingStatus_);
354
355 switch (bindingStatus_) {
356 case UWB_DEVICE_UNKNOWN:
357 // Treat 'UNKNOWN' state as 'NOT_BOUND'
358 NXPLOG_UCIHAL_E("Unknown binding status, proceed binding.");
359 [[fallthrough]];
360 case UWB_DEVICE_NOT_BOUND:
361 {
362 sr1xx_disable_dpd();
363
364 // perform bind
365 uint8_t remaining_count = 0;
366 tHAL_UWB_STATUS status = sr1xx_do_bind(&bindingStatus_, &remaining_count);
367 if (status != UWBSTATUS_SUCCESS) {
368 return status;
369 }
370
371 // perform lock
372 if (bindingStatus_ == UWB_DEVICE_BOUND_UNLOCKED && remaining_count < 3) {
373 status = sr1xx_check_binding_status(&bindingStatus_);
374 if (status != UWBSTATUS_SUCCESS) {
375 return status;
376 }
377 }
378 }
379 break;
380 case UWB_DEVICE_BOUND_UNLOCKED:
381 {
382 sr1xx_disable_dpd();
383
384 // perform lock
385 tHAL_UWB_STATUS status = sr1xx_check_binding_status(&bindingStatus_);
386 if (status != UWBSTATUS_SUCCESS) {
387 // Sending originial binding status notification to upper layer
388 // XXX: Why?
389 report_binding_status(bindingStatus_);
390 }
391 }
392 break;
393
394 case UWB_DEVICE_BOUND_LOCKED:
395 // do nothing
396 break;
397
398 default:
399 NXPLOG_UCIHAL_E("Unknown binding status: 0x%x", bindingStatus_);
400 return UWBSTATUS_FAILED;
401 }
402
403 return UWBSTATUS_SUCCESS;
404 }
405
406 extern int phNxpUciHal_fw_download();
407
chip_init()408 tHAL_UWB_STATUS NxpUwbChipSr1xx::chip_init()
409 {
410 tHAL_UWB_STATUS status;
411
412 // system in FW download mode
413 // This will be cleared on first Device Status NTF
414 nxpucihal_ctrl.fw_dwnld_mode = true;
415
416 NXPLOG_UCIHAL_D("Start SR1XX FW download");
417
418 for (int i = 0; i < 5; i++) {
419 phTmlUwb_Chip_Reset();
420
421 status = phNxpUciHal_fw_download();
422
423 if (status == UWBSTATUS_SUCCESS) {
424 NXPLOG_UCIHAL_D("Complete SR1XX FW download");
425 break;
426 } else if(status == UWBSTATUS_FILE_NOT_FOUND) {
427 NXPLOG_UCIHAL_E("FW file Not found.");
428 break;
429 } else {
430 NXPLOG_UCIHAL_E("FW download failed, status= 0x%x, retry.", status);
431 }
432 }
433
434 // register device status ntf handler
435 deviceStatusNtfHandler_ = UciHalRxHandler(
436 UCI_MT_NTF, UCI_GID_CORE, UCI_MSG_CORE_DEVICE_STATUS_NTF, false,
437 std::bind(&NxpUwbChipSr1xx::onDeviceStatusNtf, this, std::placeholders::_1, std::placeholders::_2)
438 );
439
440 // register device error ntf handler
441 genericErrorNtfHandler_ = UciHalRxHandler(
442 UCI_MT_NTF, UCI_GID_CORE, UCI_MSG_CORE_GENERIC_ERROR_NTF, false,
443 std::bind(&NxpUwbChipSr1xx::onGenericErrorNtf, this, std::placeholders::_1, std::placeholders::_2)
444 );
445
446 // register binding status ntf handler
447 bindingStatusNtfHandler_ = UciHalRxHandler(
448 UCI_MT_NTF, UCI_GID_PROPRIETARY, UCI_MSG_BINDING_STATUS_NTF, true,
449 std::bind(&NxpUwbChipSr1xx::onBindingStatusNtf, this, std::placeholders::_1, std::placeholders::_2)
450 );
451
452 return status;
453 }
454
core_init()455 tHAL_UWB_STATUS NxpUwbChipSr1xx::core_init()
456 {
457 return check_binding();
458 }
459
get_device_type(const uint8_t * param,size_t param_len)460 device_type_t NxpUwbChipSr1xx::get_device_type(const uint8_t *param, size_t param_len)
461 {
462 // 'SR100S' or 'SR1..T'
463 if (param_len >= 6) {
464 const uint8_t marker = param[5];
465 if (marker == 'S')
466 return DEVICE_TYPE_SR1xxS;
467 else if (marker == 'T')
468 return DEVICE_TYPE_SR1xxT;
469 }
470 return DEVICE_TYPE_UNKNOWN;
471 }
472
read_otp(extcal_param_id_t id,uint8_t * data,size_t data_len,size_t * retlen)473 tHAL_UWB_STATUS NxpUwbChipSr1xx::read_otp(extcal_param_id_t id, uint8_t *data, size_t data_len, size_t *retlen)
474 {
475 return sr1xx_read_otp(id, data, data_len, retlen);
476 }
477
apply_calibration(extcal_param_id_t id,const uint8_t ch,const uint8_t * data,size_t data_len)478 tHAL_UWB_STATUS NxpUwbChipSr1xx::apply_calibration(extcal_param_id_t id, const uint8_t ch, const uint8_t *data, size_t data_len)
479 {
480 return phNxpUwbCalib_apply_calibration(id, ch, data, data_len);
481 }
482
extra_group_delay(void)483 int16_t NxpUwbChipSr1xx::extra_group_delay(void) {
484 bool need_7cm_offset = FALSE;
485 // + Compensation for D48/D49 calibration
486 // If calibration was done with D48 / D49
487 char calibrated_with_fw[15] = {0};
488
489 int has_calibrated_with_fw_config = NxpConfig_GetStr(
490 "cal.fw_version", calibrated_with_fw, sizeof(calibrated_with_fw) - 1);
491
492 if ( has_calibrated_with_fw_config ) {
493 // Conf file has entry of `cal.fw_version`
494 if (
495 ( 0 == memcmp("48.", calibrated_with_fw, 3)) ||
496 ( 0 == memcmp("49.", calibrated_with_fw, 3))) {
497 // Calibrated with D48 / D49.
498 if (nxpucihal_ctrl.fw_version.major_version == 0xFF) {
499 // Current FW seems to be Test FW
500 NXPLOG_UCIHAL_W("For Test FW, D49 -> D50+ 7cm Compensation is applied");
501 need_7cm_offset = TRUE;
502 }
503 else if (nxpucihal_ctrl.fw_version.major_version >= 0x50) {
504 // D50 and later fix is needed.
505 need_7cm_offset = TRUE;
506 }
507 }
508 else
509 {
510 // Not calibrated with D48/D49
511 }
512 }
513 else
514 {
515 // Missing Entry cal.fw_version
516 NXPLOG_UCIHAL_W("Could not get cal.fw_version. Assuming D48 used for calibration.");
517 need_7cm_offset = TRUE;
518 }
519 if (need_7cm_offset) {
520 /* Its Q14.2 format, hence << 2 */
521 return (7 << 2);
522 }
523 else
524 {
525 return 0;
526 }
527 }
528
GetUwbChip()529 std::unique_ptr<NxpUwbChip> GetUwbChip()
530 {
531 return std::make_unique<NxpUwbChipSr1xx>();
532 }
533