1 /*
2 * Copyright 2012-2019, 2022-2023 NXP
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <string.h>
17 #include <sys/stat.h>
18
19 #include <atomic>
20 #include <bitset>
21 #include <map>
22 #include <vector>
23
24 #include <cutils/properties.h>
25
26 #include "phNxpConfig.h"
27 #include "phNxpLog.h"
28 #include "phNxpUciHal.h"
29 #include "phNxpUciHal_ext.h"
30 #include "phNxpUciHal_utils.h"
31 #include "phTmlUwb.h"
32 #include "phUwbCommon.h"
33 #include "sessionTrack.h"
34
35 /* Timeout value to wait for response from DEVICE_TYPE_SR1xx */
36 #define MAX_COMMAND_RETRY_COUNT 5
37 #define HAL_EXTNS_WRITE_RSP_TIMEOUT_MS 100
38 #define HAL_HW_RESET_NTF_TIMEOUT 10000 /* 10 sec wait */
39
40 /******************* Global variables *****************************************/
41 extern phNxpUciHal_Control_t nxpucihal_ctrl;
42
43 static std::vector<uint8_t> gtx_power;
44
45 /************** HAL extension functions ***************************************/
46 static bool phNxpUciHal_is_retry_not_required(uint8_t uci_octet0);
47 static void phNxpUciHal_clear_thermal_error_status();
48 static void phNxpUciHal_hw_reset_ntf_timeout_cb(uint32_t timerId,
49 void *pContext);
50
51 /******************************************************************************
52 * Function phNxpUciHal_process_ext_cmd_rsp
53 *
54 * Description This function process the extension command response. It
55 * also checks the received response to expected response.
56 *
57 * Returns returns UWBSTATUS_SUCCESS if response is as expected else
58 * returns failure.
59 *
60 ******************************************************************************/
phNxpUciHal_process_ext_cmd_rsp(uint16_t cmd_len,const uint8_t * p_cmd,uint16_t * data_written)61 tHAL_UWB_STATUS phNxpUciHal_process_ext_cmd_rsp(uint16_t cmd_len,
62 const uint8_t *p_cmd,
63 uint16_t *data_written) {
64 // PBF=1 or DATA packet: don't check RSP
65 bool isRetryNotRequired = phNxpUciHal_is_retry_not_required(p_cmd[0]) || (cmd_len < 4);
66
67 const uint8_t mt = (p_cmd[0] & UCI_MT_MASK) >> UCI_MT_SHIFT;
68 const uint8_t gid = p_cmd[0] & UCI_GID_MASK;
69 const uint8_t oid = p_cmd[1] & UCI_OID_MASK;
70
71 if (mt == UCI_MT_CMD && gid == UCI_GID_SESSION_CONTROL && oid == UCI_MSG_SESSION_START) {
72 SessionTrack_onSessionStart(cmd_len, p_cmd);
73 }
74
75 // upper-layer should handle the case of UWBSTATUS_COMMAND_RETRANSMIT && isRetryNotRequired
76 if (isRetryNotRequired) {
77 *data_written = phNxpUciHal_write_unlocked(cmd_len, p_cmd);
78
79 if (*data_written != cmd_len) {
80 NXPLOG_UCIHAL_D("phNxpUciHal_write failed for hal ext");
81 return UWBSTATUS_FAILED;
82 } else {
83 return UWBSTATUS_SUCCESS;
84 }
85 }
86
87 /* Create the local semaphore */
88 if (phNxpUciHal_init_cb_data(&nxpucihal_ctrl.ext_cb_data, NULL) != UWBSTATUS_SUCCESS) {
89 NXPLOG_UCIHAL_D("Create ext_cb_data failed");
90 return UWBSTATUS_FAILED;
91 }
92
93 tHAL_UWB_STATUS status = UWBSTATUS_FAILED;
94 int nr_retries = 0;
95 int nr_timedout = 0;
96 bool exit_loop = false;
97
98 while(!exit_loop) {
99 nxpucihal_ctrl.ext_cb_data.status = UWBSTATUS_FAILED;
100 nxpucihal_ctrl.ext_cb_waiting = true;
101
102 *data_written = phNxpUciHal_write_unlocked(cmd_len, p_cmd);
103
104 if (*data_written != cmd_len) {
105 status = UWBSTATUS_FAILED;
106 NXPLOG_UCIHAL_D("phNxpUciHal_write failed for hal ext");
107 goto clean_and_return;
108 }
109
110 if (nxpucihal_ctrl.hal_parse_enabled) {
111 status = UWBSTATUS_SUCCESS;
112 goto clean_and_return;
113 }
114
115 // Wait for rsp
116 phNxpUciHal_sem_timed_wait_msec(&nxpucihal_ctrl.ext_cb_data, HAL_EXTNS_WRITE_RSP_TIMEOUT_MS);
117
118 nxpucihal_ctrl.ext_cb_waiting = false;
119
120 switch (nxpucihal_ctrl.ext_cb_data.status) {
121 case UWBSTATUS_RESPONSE_TIMEOUT:
122 nr_timedout++;
123 [[fallthrough]];
124 case UWBSTATUS_COMMAND_RETRANSMIT:
125 // TODO: Do not retransmit CMD by here when !nxpucihal_ctrl.hal_ext_enabled,
126 // Upper layer should take care of it.
127 nr_retries++;
128 break;
129 default:
130 status = nxpucihal_ctrl.ext_cb_data.status;
131 exit_loop = true;
132 break;
133 }
134
135 if (nr_retries >= MAX_COMMAND_RETRY_COUNT) {
136 NXPLOG_UCIHAL_E("Failed to process cmd/rsp 0x%x", nxpucihal_ctrl.ext_cb_data.status);
137 status = UWBSTATUS_FAILED;
138 exit_loop = true;
139 phNxpUciHal_send_dev_error_status_ntf();
140 }
141 }
142
143 if (nr_timedout > 0) {
144 NXPLOG_UCIHAL_E("Warning: CMD/RSP retried %d times (timeout:%d)\n",
145 nr_retries, nr_timedout);
146 }
147
148 clean_and_return:
149 phNxpUciHal_cleanup_cb_data(&nxpucihal_ctrl.ext_cb_data);
150
151 return status;
152 }
153
154 /******************************************************************************
155 * Function phNxpUciHal_send_ext_cmd
156 *
157 * Description This function send the extension command to UWBC. No
158 * response is checked by this function but it waits for
159 * the response to come.
160 *
161 * Returns Returns UWBSTATUS_SUCCESS if sending cmd is successful and
162 * response is received.
163 *
164 ******************************************************************************/
phNxpUciHal_send_ext_cmd(uint16_t cmd_len,const uint8_t * p_cmd)165 tHAL_UWB_STATUS phNxpUciHal_send_ext_cmd(uint16_t cmd_len, const uint8_t* p_cmd) {
166 tHAL_UWB_STATUS status;
167
168 if (cmd_len >= UCI_MAX_DATA_LEN) {
169 status = UWBSTATUS_FAILED;
170 return status;
171 }
172 uint16_t data_written = 0;
173 HAL_ENABLE_EXT();
174 nxpucihal_ctrl.cmd_len = cmd_len;
175 memcpy(nxpucihal_ctrl.p_cmd_data, p_cmd, cmd_len);
176 status = phNxpUciHal_process_ext_cmd_rsp(
177 nxpucihal_ctrl.cmd_len, nxpucihal_ctrl.p_cmd_data, &data_written);
178 HAL_DISABLE_EXT();
179
180 return status;
181 }
182
183 /******************************************************************************
184 * Function phNxpUciHal_set_board_config
185 *
186 * Description This function is called to set the board varaint config
187 * Returns return 0 on success and -1 on fail, On success
188 * update the acutual state of operation in arg pointer
189 *
190 ******************************************************************************/
phNxpUciHal_set_board_config()191 tHAL_UWB_STATUS phNxpUciHal_set_board_config(){
192 tHAL_UWB_STATUS status;
193 uint8_t buffer[] = {0x2E,0x00,0x00,0x02,0x01,0x01};
194 /* Set the board variant configurations */
195 unsigned long num = 0;
196 NXPLOG_UCIHAL_D("%s: enter; ", __func__);
197 uint8_t boardConfig = 0, boardVersion = 0;
198
199 if(NxpConfig_GetNum(NAME_UWB_BOARD_VARIANT_CONFIG, &num, sizeof(num))){
200 boardConfig = (uint8_t)num;
201 NXPLOG_UCIHAL_D("%s: NAME_UWB_BOARD_VARIANT_CONFIG: %x", __func__,boardConfig);
202 } else {
203 NXPLOG_UCIHAL_D("%s: NAME_UWB_BOARD_VARIANT_CONFIG: failed %x", __func__,boardConfig);
204 }
205 if(NxpConfig_GetNum(NAME_UWB_BOARD_VARIANT_VERSION, &num, sizeof(num))){
206 boardVersion = (uint8_t)num;
207 NXPLOG_UCIHAL_D("%s: NAME_UWB_BOARD_VARIANT_VERSION: %x", __func__,boardVersion);
208 } else{
209 NXPLOG_UCIHAL_D("%s: NAME_UWB_BOARD_VARIANT_VERSION: failed %lx", __func__,num);
210 }
211 buffer[4] = boardConfig;
212 buffer[5] = boardVersion;
213
214 status = phNxpUciHal_send_ext_cmd(sizeof(buffer), buffer);
215
216 return status;
217 }
218
219 /*******************************************************************************
220 **
221 ** Function phNxpUciHal_process_ext_rsp
222 **
223 ** Description Process extension function response
224 **
225 ** Returns UWBSTATUS_SUCCESS if success
226 **
227 *******************************************************************************/
phNxpUciHal_process_ext_rsp(uint16_t rsp_len,uint8_t * p_buff)228 tHAL_UWB_STATUS phNxpUciHal_process_ext_rsp(uint16_t rsp_len, uint8_t* p_buff){
229 tHAL_UWB_STATUS status;
230 int NumOfTlv, index;
231 uint8_t paramId, extParamId, IdStatus;
232 index = UCI_NTF_PAYLOAD_OFFSET; // index for payload start
233 status = p_buff[index++];
234 if(status == UCI_STATUS_OK){
235 NXPLOG_UCIHAL_D("%s: status success %d", __func__, status);
236 return UWBSTATUS_SUCCESS;
237 }
238 NumOfTlv = p_buff[index++];
239 while (index < rsp_len) {
240 paramId = p_buff[index++];
241 if(paramId == EXTENDED_DEVICE_CONFIG_ID) {
242 extParamId = p_buff[index++];
243 IdStatus = p_buff[index++];
244
245 switch(extParamId) {
246 case UCI_EXT_PARAM_DELAY_CALIBRATION_VALUE:
247 case UCI_EXT_PARAM_AOA_CALIBRATION_CTRL:
248 case UCI_EXT_PARAM_DPD_WAKEUP_SRC:
249 case UCI_EXT_PARAM_WTX_COUNT_CONFIG:
250 case UCI_EXT_PARAM_DPD_ENTRY_TIMEOUT:
251 case UCI_EXT_PARAM_WIFI_COEX_FEATURE:
252 case UCI_EXT_PARAM_TX_BASE_BAND_CONFIG:
253 case UCI_EXT_PARAM_DDFS_TONE_CONFIG:
254 case UCI_EXT_PARAM_TX_PULSE_SHAPE_CONFIG:
255 case UCI_EXT_PARAM_CLK_CONFIG_CTRL:
256 if(IdStatus == UCI_STATUS_FEATURE_NOT_SUPPORTED){
257 NXPLOG_UCIHAL_E("%s: Vendor config param: %x %x is Not Supported", __func__, paramId, extParamId);
258 status = UWBSTATUS_SUCCESS;
259 } else {
260 status = UWBSTATUS_FAILED;
261 return status;
262 }
263 break;
264 default:
265 NXPLOG_UCIHAL_D("%s: Vendor param ID: %x", __func__, extParamId);
266 break;
267 }
268 } else {
269 IdStatus = p_buff[index++];
270 switch(paramId) {
271 case UCI_PARAM_ID_LOW_POWER_MODE:
272 if(IdStatus == UCI_STATUS_FEATURE_NOT_SUPPORTED){
273 NXPLOG_UCIHAL_E("%s: Generic config param: %x is Not Supported", __func__, paramId);
274 status = UWBSTATUS_SUCCESS;
275 } else {
276 status = UWBSTATUS_FAILED;
277 return status;
278 }
279 break;
280 default:
281 NXPLOG_UCIHAL_D("%s: Generic param ID: %x", __func__, paramId);
282 break;
283 }
284 }
285 }
286 NXPLOG_UCIHAL_D("%s: exit %d", __func__, status);
287 return status;
288 }
289
290 /*******************************************************************************
291 * Function phNxpUciHal_resetRuntimeSettings
292 *
293 * Description reset per-country code settigs to default
294 *
295 *******************************************************************************/
phNxpUciHal_resetRuntimeSettings(void)296 static void phNxpUciHal_resetRuntimeSettings(void)
297 {
298 phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
299 rt_set->uwb_enable = true;
300 rt_set->restricted_channel_mask = 0;
301 rt_set->tx_power_offset = 0;
302 }
303
304 /*******************************************************************************
305 * Function phNxpUciHal_applyCountryCaps
306 *
307 * Description Update runtime settings with given COUNTRY_CODE_CAPS byte array
308 *
309 * Returns void
310 *
311 *******************************************************************************/
phNxpUciHal_applyCountryCaps(const char country_code[2],const uint8_t * cc_resp,uint32_t cc_resp_len)312 static void phNxpUciHal_applyCountryCaps(const char country_code[2],
313 const uint8_t *cc_resp, uint32_t cc_resp_len)
314 {
315 phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
316
317 uint16_t idx = 1; // first byte = number countries
318 bool country_code_found = false;
319
320 while (idx < cc_resp_len) {
321 uint8_t tag = cc_resp[idx++];
322 uint8_t len = cc_resp[idx++];
323
324 if (country_code_found) {
325 switch (tag) {
326 case UWB_ENABLE_TAG:
327 if (len == 1) {
328 rt_set->uwb_enable = cc_resp[idx];
329 NXPLOG_UCIHAL_D("CountryCaps uwb_enable = %u", cc_resp[idx]);
330 }
331 break;
332 case CHANNEL_5_TAG:
333 if (len == 1 && !cc_resp[idx]) {
334 rt_set->restricted_channel_mask |= 1<< 5;
335 NXPLOG_UCIHAL_D("CountryCaps channel 5 support = %u", cc_resp[idx]);
336 }
337 break;
338 case CHANNEL_9_TAG:
339 if (len == 1 && !cc_resp[idx]) {
340 rt_set->restricted_channel_mask |= 1<< 9;
341 NXPLOG_UCIHAL_D("CountryCaps channel 9 support = %u", cc_resp[idx]);
342 }
343 break;
344 case TX_POWER_TAG:
345 if (len == 2) {
346 rt_set->tx_power_offset = (short)((cc_resp[idx + 0]) | (((cc_resp[idx + 1]) << RMS_TX_POWER_SHIFT) & 0xFF00));
347 NXPLOG_UCIHAL_D("CountryCaps tx_power_offset = %d", rt_set->tx_power_offset);
348 }
349 break;
350 default:
351 break;
352 }
353 }
354 if (tag == COUNTRY_CODE_TAG) {
355 country_code_found = (cc_resp[idx + 0] == country_code[0]) && (cc_resp[idx + 1] == country_code[1]);
356 }
357 idx += len;
358 }
359 }
360
361 /*******************************************************************************
362 * Function phNxpUciHal_is_retry_not_required
363 *
364 * Description UCI command retry check
365 *
366 * Returns true/false
367 *
368 *******************************************************************************/
phNxpUciHal_is_retry_not_required(uint8_t uci_octet0)369 static bool phNxpUciHal_is_retry_not_required(uint8_t uci_octet0) {
370 bool isRetryRequired = false, isChained_cmd = false, isData_Msg = false;
371 isChained_cmd = (bool)((uci_octet0 & UCI_PBF_ST_CONT) >> UCI_PBF_SHIFT);
372 isData_Msg = ((uci_octet0 & UCI_MT_MASK) >> UCI_MT_SHIFT) == UCI_MT_DATA;
373 isRetryRequired = isChained_cmd | isData_Msg;
374 return isRetryRequired;
375 }
376
377 /******************************************************************************
378 * Function CountryCodeCapsGenTxPowerPacket
379 *
380 * Description This function makes tx antenna power calibration command
381 * with gtx_power[] + tx_power_offset
382 *
383 * Returns true if packet has been updated
384 *
385 ******************************************************************************/
CountryCodeCapsGenTxPowerPacket(uint8_t * packet,size_t packet_len,uint16_t * out_len)386 static bool CountryCodeCapsGenTxPowerPacket(uint8_t *packet, size_t packet_len, uint16_t *out_len)
387 {
388 phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
389
390 if (rt_set->tx_power_offset == 0)
391 return false;
392
393 if (gtx_power.empty())
394 return false;
395
396 if (gtx_power.size() > packet_len)
397 return false;
398
399 uint16_t gtx_power_len = gtx_power.size();
400 memcpy(packet, gtx_power.data(), gtx_power_len);
401 uint8_t index = UCI_MSG_HDR_SIZE + 2; // channel + Tag
402
403 // Length
404 if (index > gtx_power_len) {
405 NXPLOG_UCIHAL_E("Upper-layer calibration command for tx_power is invalid.");
406 return false;
407 }
408 if (!packet[index] || (packet[index] + index) > gtx_power_len) {
409 NXPLOG_UCIHAL_E("Upper-layer calibration command for tx_power is invalid.");
410 return false;
411 }
412 index++;
413
414 // Value[0] = Number of antennas
415 uint8_t num_of_antennas = packet[index++];
416
417 while (num_of_antennas--) {
418 // each entry = { antenna-id(1) + peak_tx(2) + id_rms(2) }
419 if ((index + 5) < gtx_power_len) {
420 NXPLOG_UCIHAL_E("Upper-layer calibration command for tx_power is invalid.");
421 return false;
422 }
423
424 index += 3; // antenna Id(1) + Peak Tx(2)
425
426 // Adjust id_rms part
427 long tx_power_long = packet[index] | ((packet[index + 1] << RMS_TX_POWER_SHIFT) & 0xFF00);
428 tx_power_long += rt_set->tx_power_offset;
429
430 if (tx_power_long < 0)
431 tx_power_long = 0;
432
433 uint16_t tx_power_u16 = (uint16_t)tx_power_long;
434 packet[index++] = tx_power_u16 & 0xff;
435 packet[index++] = tx_power_u16 >> RMS_TX_POWER_SHIFT;
436 }
437
438 if (out_len)
439 *out_len = gtx_power_len;
440
441 return true;
442 }
443
444 /*******************************************************************************
445 * Function phNxpUciHal_handle_set_calibration
446 *
447 * Description Remembers SET_VENDOR_SET_CALIBRATION_CMD packet
448 *
449 * Returns void
450 *
451 *******************************************************************************/
phNxpUciHal_handle_set_calibration(const uint8_t * p_data,uint16_t data_len)452 void phNxpUciHal_handle_set_calibration(const uint8_t *p_data, uint16_t data_len)
453 {
454 // Only saves the SET_CALIBRATION_CMD from upper-layer
455 if (nxpucihal_ctrl.hal_ext_enabled) {
456 return;
457 }
458
459 // SET_DEVICE_CALIBRATION_CMD Packet format: Channel(1) + TLV
460 if (data_len < 6) {
461 return;
462 }
463 const uint8_t channel = p_data[UCI_MSG_HDR_SIZE + 0];
464 const uint8_t tag = p_data[UCI_MSG_HDR_SIZE + 1];
465 if (tag != NXP_PARAM_ID_TX_POWER_PER_ANTENNA) {
466 return;
467 }
468
469 phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
470
471 // RMS Tx power -> Octet [4, 5] in calib data
472 NXPLOG_UCIHAL_D("phNxpUciHal_handle_set_calibration channel=%u tx_power_offset=%d", channel, rt_set->tx_power_offset);
473
474 // Backup the packet to gtx_power[]
475 gtx_power = std::move(std::vector<uint8_t> {p_data, p_data + data_len});
476
477 // Patch SET_CALIBRATION_CMD per gtx_power + tx_power_offset
478 CountryCodeCapsGenTxPowerPacket(nxpucihal_ctrl.p_cmd_data, sizeof(nxpucihal_ctrl.p_cmd_data), &nxpucihal_ctrl.cmd_len);
479 }
480
481 /******************************************************************************
482 * Function CountryCodeCapsApplyTxPower
483 *
484 * Description This function sets the TX power from COUNTRY_CODE_CAPS
485 *
486 * Returns false if no tx_power_offset or no Upper-layer Calibration cmd was given
487 * true if it was successfully applied.
488 *
489 ******************************************************************************/
CountryCodeCapsApplyTxPower(void)490 static bool CountryCodeCapsApplyTxPower(void)
491 {
492 if (gtx_power.empty())
493 return false;
494
495 // use whole packet as-is from upper-layer command (gtx_power[])
496 std::vector<uint8_t> packet(gtx_power.size());
497 uint16_t packet_size = 0;
498 if (!CountryCodeCapsGenTxPowerPacket(packet.data(), packet.size(), &packet_size))
499 return false;
500
501 tHAL_UWB_STATUS status = phNxpUciHal_send_ext_cmd(packet_size, packet.data());
502 if (status != UWBSTATUS_SUCCESS) {
503 NXPLOG_UCIHAL_D("%s: send failed", __func__);
504 }
505
506 gtx_power.clear();
507
508 return true;
509 }
510
511 // Channels
512 const static uint8_t cal_channels[] = {5, 6, 8, 9};
513
extcal_do_xtal(void)514 static void extcal_do_xtal(void)
515 {
516 int ret;
517
518 // RF_CLK_ACCURACY_CALIB (otp supported)
519 // parameters: cal.otp.xtal=0|1, cal.xtal=X
520 uint8_t otp_xtal_flag = 0;
521 uint8_t xtal_data[32];
522 size_t xtal_data_len = 0;
523
524 if (NxpConfig_GetNum("cal.otp.xtal", &otp_xtal_flag, 1) && otp_xtal_flag) {
525 nxpucihal_ctrl.uwb_chip->read_otp(EXTCAL_PARAM_CLK_ACCURACY, xtal_data, sizeof(xtal_data), &xtal_data_len);
526 }
527 if (!xtal_data_len) {
528 long retlen = 0;
529 if (NxpConfig_GetByteArray("cal.xtal", xtal_data, sizeof(xtal_data), &retlen)) {
530 xtal_data_len = retlen;
531 }
532 }
533
534 if (xtal_data_len) {
535 NXPLOG_UCIHAL_D("Apply CLK_ACCURARY (len=%zu, from-otp=%c)", xtal_data_len, otp_xtal_flag ? 'y' : 'n');
536
537 ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_CLK_ACCURACY, 0, xtal_data, xtal_data_len);
538
539 if (ret != UWBSTATUS_SUCCESS) {
540 NXPLOG_UCIHAL_E("Failed to apply CLK_ACCURACY (len=%zu, from-otp=%c)",
541 xtal_data_len, otp_xtal_flag ? 'y' : 'n');
542 }
543 }
544 }
545
extcal_do_ant_delay(void)546 static void extcal_do_ant_delay(void)
547 {
548 std::bitset<8> rx_antenna_mask(nxpucihal_ctrl.cal_rx_antenna_mask);
549 const uint8_t n_rx_antennas = rx_antenna_mask.size();
550
551 // RX_ANT_DELAY_CALIB
552 // parameter: cal.ant<N>.ch<N>.ant_delay=X
553 // N(1) + N * {AntennaID(1), Rxdelay(Q14.2)}
554 if (n_rx_antennas) {
555
556 const int16_t extra_delay = nxpucihal_ctrl.uwb_chip->extra_group_delay();
557
558 if (extra_delay) {
559 NXPLOG_UCIHAL_D("RX_ANT_DELAY_CALIB: Extra compensation '%d'", extra_delay);
560 }
561
562 for (auto ch : cal_channels) {
563 std::vector<uint8_t> entries;
564 uint8_t n_entries = 0;
565
566 for (auto i = 0; i < n_rx_antennas; i++) {
567 if (!rx_antenna_mask[i])
568 continue;
569
570 const uint8_t ant_id = i + 1;
571 uint16_t delay_value;
572 char key[32];
573 std::snprintf(key, sizeof(key), "cal.ant%u.ch%u.ant_delay", ant_id, ch);
574
575 if (!NxpConfig_GetNum(key, &delay_value, 2))
576 continue;
577
578 delay_value = delay_value + extra_delay;
579 NXPLOG_UCIHAL_D("Apply RX_ANT_DELAY_CALIB: %s = %u", key, delay_value);
580 entries.push_back(ant_id);
581 // Little Endian
582 entries.push_back(delay_value & 0xff);
583 entries.push_back(delay_value >> 8);
584 n_entries++;
585 }
586
587 if (!n_entries)
588 continue;
589
590 entries.insert(entries.begin(), n_entries);
591 tHAL_UWB_STATUS ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_RX_ANT_DELAY, ch, entries.data(), entries.size());
592 if (ret != UWBSTATUS_SUCCESS) {
593 NXPLOG_UCIHAL_E("Failed to apply RX_ANT_DELAY for channel %u", ch);
594 }
595 }
596 }
597 }
598
extcal_do_tx_power(void)599 static void extcal_do_tx_power(void)
600 {
601 std::bitset<8> tx_antenna_mask(nxpucihal_ctrl.cal_tx_antenna_mask);
602 const uint8_t n_tx_antennas = tx_antenna_mask.size();
603
604 // TX_POWER
605 // parameter: cal.ant<N>.ch<N>.tx_power={...}
606 if (n_tx_antennas) {
607 for (auto ch : cal_channels) {
608 std::vector<uint8_t> entries;
609 uint8_t n_entries = 0;
610
611 for (auto i = 0; i < n_tx_antennas; i++) {
612 if (!tx_antenna_mask[i])
613 continue;
614
615 char key[32];
616 const uint8_t ant_id = i + 1;
617 std::snprintf(key, sizeof(key), "cal.ant%u.ch%u.tx_power", ant_id, ch);
618
619 uint8_t power_value[32];
620 long retlen = 0;
621 if (!NxpConfig_GetByteArray(key, power_value, sizeof(power_value), &retlen)) {
622 continue;
623 }
624
625 NXPLOG_UCIHAL_D("Apply TX_POWER: %s = { %lu bytes }", key, retlen);
626 entries.push_back(ant_id);
627 entries.insert(entries.end(), power_value, power_value + retlen);
628 n_entries++;
629 }
630
631 if (!n_entries)
632 continue;
633
634 entries.insert(entries.begin(), n_entries);
635 tHAL_UWB_STATUS ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_TX_POWER, ch, entries.data(), entries.size());
636 if (ret != UWBSTATUS_SUCCESS) {
637 NXPLOG_UCIHAL_E("Failed to apply TX_POWER for channel %u", ch);
638 }
639 }
640 }
641 }
642
extcal_do_tx_pulse_shape(void)643 static void extcal_do_tx_pulse_shape(void)
644 {
645 // parameters: cal.tx_pulse_shape={...}
646 long retlen = 0;
647 uint8_t data[64];
648
649 if (NxpConfig_GetByteArray("cal.tx_pulse_shape", data, sizeof(data), &retlen) && retlen) {
650 NXPLOG_UCIHAL_D("Apply TX_PULSE_SHAPE: data = { %lu bytes }", retlen);
651
652 tHAL_UWB_STATUS ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_TX_PULSE_SHAPE, 0, data, (size_t)retlen);
653 if (ret != UWBSTATUS_SUCCESS) {
654 NXPLOG_UCIHAL_E("Failed to apply TX_PULSE_SHAPE.");
655 }
656 }
657 }
658
extcal_do_tx_base_band(void)659 static void extcal_do_tx_base_band(void)
660 {
661 // TX_BASE_BAND_CONTROL, DDFS_TONE_CONFIG
662 // parameters: cal.ddfs_enable=1|0, cal.dc_suppress=1|0, ddfs_tone_config={...}
663 uint8_t ddfs_enable = 0, dc_suppress = 0;
664 uint8_t ddfs_tone[256];
665 long retlen = 0;
666 tHAL_UWB_STATUS ret;
667
668 if (NxpConfig_GetNum("cal.ddfs_enable", &ddfs_enable, 1)) {
669 NXPLOG_UCIHAL_D("Apply TX_BASE_BAND_CONTROL: ddfs_enable=%u", ddfs_enable);
670 }
671 if (NxpConfig_GetNum("cal.dc_suppress", &dc_suppress, 1)) {
672 NXPLOG_UCIHAL_D("Apply TX_BASE_BAND_CONTROL: dc_suppress=%u", dc_suppress);
673 }
674
675 // DDFS_TONE_CONFIG
676 if (ddfs_enable) {
677 if (!NxpConfig_GetByteArray("cal.ddfs_tone_config", ddfs_tone, sizeof(ddfs_tone), &retlen) || !retlen) {
678 NXPLOG_UCIHAL_E("cal.ddfs_tone_config is not supplied while cal.ddfs_enable=1, ddfs was not enabled.");
679 ddfs_enable = 0;
680 } else {
681 NXPLOG_UCIHAL_D("Apply DDFS_TONE_CONFIG: ddfs_tone_config = { %lu bytes }", retlen);
682
683 ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_DDFS_TONE_CONFIG, 0, ddfs_tone, (size_t)retlen);
684 if (ret != UWBSTATUS_SUCCESS) {
685 NXPLOG_UCIHAL_E("Failed to apply DDFS_TONE_CONFIG, ddfs was not enabled.");
686 ddfs_enable = 0;
687 }
688 }
689 }
690
691 // TX_BASE_BAND_CONTROL
692 {
693 uint8_t flag = 0;
694 if (ddfs_enable)
695 flag |= 0x01;
696 if (dc_suppress)
697 flag |= 0x02;
698 ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_TX_BASE_BAND_CONTROL, 0, &flag, 1);
699 if (ret) {
700 NXPLOG_UCIHAL_E("Failed to apply TX_BASE_BAND_CONTROL");
701 }
702 }
703 }
704
705 /******************************************************************************
706 * Function phNxpUciHal_extcal_handle_coreinit
707 *
708 * Description Apply additional core device settings
709 *
710 * Returns void.
711 *
712 ******************************************************************************/
phNxpUciHal_extcal_handle_coreinit(void)713 void phNxpUciHal_extcal_handle_coreinit(void)
714 {
715 // read rx_aantenna_mask, tx_antenna_mask
716 uint8_t rx_antenna_mask_n = 0x1;
717 uint8_t tx_antenna_mask_n = 0x1;
718 if (!NxpConfig_GetNum("cal.rx_antenna_mask", &rx_antenna_mask_n, 1)) {
719 NXPLOG_UCIHAL_E("cal.rx_antenna_mask is not specified, use default 0x%x", rx_antenna_mask_n);
720 }
721 if (!NxpConfig_GetNum("cal.tx_antenna_mask", &tx_antenna_mask_n, 1)) {
722 NXPLOG_UCIHAL_E("cal.tx_antenna_mask is not specified, use default 0x%x", tx_antenna_mask_n);
723 }
724 nxpucihal_ctrl.cal_rx_antenna_mask = rx_antenna_mask_n;
725 nxpucihal_ctrl.cal_tx_antenna_mask = tx_antenna_mask_n;
726
727 extcal_do_xtal();
728 extcal_do_ant_delay();
729 }
730
apply_per_country_calibrations(void)731 void apply_per_country_calibrations(void)
732 {
733 // TX-POWER can be provided by
734 // 1) COUNTRY_CODE_CAPS with offset values.
735 // 2) Extra calibration files with absolute tx power values
736 // only one should be applied if both were provided by platform
737 if (!CountryCodeCapsApplyTxPower()) {
738 extcal_do_tx_power();
739 }
740
741 // These are only available from extra calibration files
742 extcal_do_tx_pulse_shape();
743 extcal_do_tx_base_band();
744
745 }
746
747 /******************************************************************************
748 * Function phNxpUciHal_handle_set_country_code
749 *
750 * Description Apply per-country settings
751 *
752 * Returns void.
753 *
754 ******************************************************************************/
phNxpUciHal_handle_set_country_code(const char country_code[2])755 void phNxpUciHal_handle_set_country_code(const char country_code[2])
756 {
757 NXPLOG_UCIHAL_D("Apply country code %c%c", country_code[0], country_code[1]);
758
759 phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
760 phNxpUciHal_resetRuntimeSettings();
761
762 if (!is_valid_country_code(country_code)) {
763 NXPLOG_UCIHAL_D("Country code %c%c is invalid, UWB should be disabled", country_code[0], country_code[1]);
764 }
765
766 if (NxpConfig_SetCountryCode(country_code)) {
767 // Load ExtraCal restrictions
768 uint16_t mask= 0;
769 if (NxpConfig_GetNum("cal.restricted_channels", &mask, sizeof(mask))) {
770 NXPLOG_UCIHAL_D("Restriction flag, restricted channel mask=0x%x", mask);
771 rt_set->restricted_channel_mask = mask;
772 }
773
774 uint8_t uwb_disable = 0;
775 if (NxpConfig_GetNum("cal.uwb_disable", &uwb_disable, sizeof(uwb_disable))) {
776 NXPLOG_UCIHAL_D("Restriction flag, uwb_disable=%u", uwb_disable);
777 rt_set->uwb_enable = !uwb_disable;
778 }
779
780 // Apply COUNTRY_CODE_CAPS
781 uint8_t cc_caps[UCI_MAX_DATA_LEN];
782 long retlen = 0;
783 if (NxpConfig_GetByteArray(NAME_NXP_UWB_COUNTRY_CODE_CAPS, cc_caps, sizeof(cc_caps), &retlen) && retlen) {
784 NXPLOG_UCIHAL_D("COUNTRY_CODE_CAPS is provided.");
785 phNxpUciHal_applyCountryCaps(country_code, cc_caps, retlen);
786 }
787
788 // Check country code validity
789 if (!is_valid_country_code(country_code) && rt_set->uwb_enable) {
790 NXPLOG_UCIHAL_E("UWB is not disabled by configuration files with invalid country code %c%c,"
791 " forcing it disabled", country_code[0], country_code[1]);
792 rt_set->uwb_enable = false;
793 }
794
795 // Apply per-country calibration, it's handled by SessionTrack
796 SessionTrack_onCountryCodeChanged();
797 }
798
799 // send country code response to upper layer
800 nxpucihal_ctrl.rx_data_len = 5;
801 static uint8_t rsp_data[5] = { 0x4c, 0x01, 0x00, 0x01 };
802 if (rt_set->uwb_enable) {
803 rsp_data[4] = UWBSTATUS_SUCCESS;
804 } else {
805 rsp_data[4] = UCI_STATUS_CODE_ANDROID_REGULATION_UWB_OFF;
806 }
807 (*nxpucihal_ctrl.p_uwb_stack_data_cback)(nxpucihal_ctrl.rx_data_len, rsp_data);
808 }
809
810 // TODO: support fragmented packets
811 /*************************************************************************************
812 * Function phNxpUciHal_handle_set_app_config
813 *
814 * Description Handle SESSION_SET_APP_CONFIG_CMD packet,
815 * remove unsupported parameters
816 *
817 * Returns true : SESSION_SET_APP_CONFIG_CMD/RSP was handled by this function
818 * false : This packet should go to chip
819 *
820 *************************************************************************************/
phNxpUciHal_handle_set_app_config(uint16_t * data_len,uint8_t * p_data)821 bool phNxpUciHal_handle_set_app_config(uint16_t *data_len, uint8_t *p_data)
822 {
823 const phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
824 // Android vendor specific app configs not supported by FW
825 const uint8_t tags_to_del[] = {
826 UCI_PARAM_ID_TX_ADAPTIVE_PAYLOAD_POWER,
827 UCI_PARAM_ID_AOA_AZIMUTH_MEASUREMENTS,
828 UCI_PARAM_ID_AOA_ELEVATION_MEASUREMENTS,
829 UCI_PARAM_ID_RANGE_MEASUREMENTS
830 };
831
832 // check basic validity
833 uint16_t payload_len = (p_data[UCI_CMD_LENGTH_PARAM_BYTE1] & 0xFF) |
834 ((p_data[UCI_CMD_LENGTH_PARAM_BYTE2] & 0xFF) << 8);
835 if (payload_len != (*data_len - UCI_MSG_HDR_SIZE)) {
836 NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD: payload length mismatch");
837 return false;
838 }
839 if (!p_data[UCI_CMD_NUM_CONFIG_PARAM_BYTE]) {
840 return false;
841 }
842
843 uint32_t session_handle = le_bytes_to_cpu<uint32_t>(&p_data[UCI_MSG_SESSION_SET_APP_CONFIG_HANDLE_OFFSET]);
844 uint8_t ch = 0;
845
846 // Create local copy of cmd_data for data manipulation
847 uint8_t uciCmd[UCI_MAX_DATA_LEN];
848 uint16_t packet_len = *data_len;
849 if (sizeof(uciCmd) < packet_len) {
850 NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD packet size %u is too big to handle, skip patching.", packet_len);
851 return false;
852 }
853 // 9 = Header 4 + SessionID 4 + NumOfConfigs 1
854 uint8_t i = 9, j = 9;
855 uint8_t nr_deleted = 0, bytes_deleted = 0;
856 memcpy(uciCmd, p_data, i);
857
858 while (i < packet_len) {
859 if ( (i + 2) >= packet_len) {
860 NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD parse error at %u", i);
861 return false;
862 }
863
864 // All parameters should have 1 byte tag
865 uint8_t tlv_tag = p_data[i + 0];
866 uint8_t tlv_len = p_data[i + 1];
867 uint8_t param_len = 2 + tlv_len;
868 if ((i + param_len) > packet_len) {
869 NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD parse error at %u", i);
870 }
871
872 // check restricted channel
873 if (tlv_tag == UCI_PARAM_ID_CHANNEL_NUMBER && tlv_len == 1) {
874 ch = p_data[i + 2];
875
876 if (((ch == CHANNEL_NUM_5) && (rt_set->restricted_channel_mask & (1 << 5))) ||
877 ((ch == CHANNEL_NUM_9) && (rt_set->restricted_channel_mask & (1 << 9)))) {
878 phNxpUciHal_print_packet(NXP_TML_UCI_CMD_AP_2_UWBS, p_data, packet_len);
879 NXPLOG_UCIHAL_D("Country code blocked channel %u", ch);
880
881 // send setAppConfig response with UCI_STATUS_CODE_ANDROID_REGULATION_UWB_OFF response
882 static uint8_t rsp_data[] = { 0x41, 0x03, 0x04, 0x04,
883 UCI_STATUS_FAILED, 0x01, tlv_tag, UCI_STATUS_CODE_ANDROID_REGULATION_UWB_OFF
884 };
885 nxpucihal_ctrl.rx_data_len = sizeof(rsp_data);
886 (*nxpucihal_ctrl.p_uwb_stack_data_cback)(nxpucihal_ctrl.rx_data_len, rsp_data);
887 return true;
888 }
889 }
890
891 // remove unsupported parameters
892 if (std::find(std::begin(tags_to_del), std::end(tags_to_del), tlv_tag) == std::end(tags_to_del)) {
893 memcpy(&uciCmd[j], &p_data[i], param_len);
894 j += param_len;
895 } else {
896 NXPLOG_UCIHAL_D("Removed param payload with Tag ID:0x%02x", tlv_tag);
897 nr_deleted++;
898 bytes_deleted += param_len;
899 }
900 i += param_len;
901 }
902 if (nr_deleted) {
903 phNxpUciHal_print_packet(NXP_TML_UCI_CMD_AP_2_UWBS, p_data, packet_len);
904
905 // uci number of config params update
906 if (uciCmd[UCI_CMD_NUM_CONFIG_PARAM_BYTE] < nr_deleted) {
907 NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD cannot parse packet: wrong nr_parameters");
908 return false;
909 }
910 uciCmd[UCI_CMD_NUM_CONFIG_PARAM_BYTE] -= nr_deleted;
911
912 // uci command length update
913 if (packet_len < (UCI_MSG_HDR_SIZE + bytes_deleted)) {
914 NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD cannot parse packet: underflow");
915 return false;
916 }
917 packet_len -= bytes_deleted;
918 payload_len = packet_len - UCI_MSG_HDR_SIZE;
919 uciCmd[UCI_CMD_LENGTH_PARAM_BYTE2] = (payload_len & 0xFF00) >> 8;
920 uciCmd[UCI_CMD_LENGTH_PARAM_BYTE1] = (payload_len & 0xFF);
921
922 // Swap
923 memcpy(p_data, uciCmd, packet_len);
924 *data_len = packet_len;
925 }
926
927 SessionTrack_onAppConfig(session_handle, ch);
928
929 return false;
930 }
931
phNxpUciHal_handle_get_caps_info(uint16_t data_len,uint8_t * p_data)932 void phNxpUciHal_handle_get_caps_info(uint16_t data_len, uint8_t *p_data)
933 {
934 if (data_len < UCI_MSG_CORE_GET_CAPS_INFO_NR_OFFSET)
935 return;
936
937 uint8_t status = p_data[UCI_RESPONSE_STATUS_OFFSET];
938 uint8_t nr = p_data[UCI_MSG_CORE_GET_CAPS_INFO_NR_OFFSET];
939 if (status != UWBSTATUS_SUCCESS || nr < 1)
940 return;
941
942 auto tlvs = decodeTlvBytes({0xe0, 0xe1, 0xe2, 0xe3}, &p_data[UCI_MSG_CORE_GET_CAPS_INFO_TLV_OFFSET], data_len - UCI_MSG_CORE_GET_CAPS_INFO_TLV_OFFSET);
943 if (tlvs.size() != nr) {
944 NXPLOG_UCIHAL_E("Failed to parse DevCaps %zu != %u", tlvs.size(), nr);
945 }
946
947 // Remove all NXP vendor specific parameters
948 for (auto it = tlvs.begin(); it != tlvs.end();) {
949 if (it->first > 0xff)
950 it = tlvs.erase(it);
951 else
952 it++;
953 }
954
955 // Override AOA_SUPPORT_TAG_ID
956 auto it = tlvs.find(AOA_SUPPORT_TAG_ID);
957 if (it != tlvs.end()) {
958 if (nxpucihal_ctrl.numberOfAntennaPairs == 1) {
959 it->second = std::vector<uint8_t>{0x01};
960 } else if (nxpucihal_ctrl.numberOfAntennaPairs > 1) {
961 it->second = std::vector<uint8_t>{0x05};
962 } else {
963 it->second = std::vector<uint8_t>{0x00};
964 }
965 }
966
967 // Byteorder of CCC_SUPPORTED_PROTOCOL_VERSIONS_ID
968 it = tlvs.find(CCC_SUPPORTED_PROTOCOL_VERSIONS_ID);
969 if (it != tlvs.end() && it->second.size() == 2) {
970 std::swap(it->second[0], it->second[1]);
971 }
972
973 // Append UWB_VENDOR_CAPABILITY from configuration files
974 {
975 std::array<uint8_t, NXP_MAX_CONFIG_STRING_LEN> buffer;
976 long retlen = 0;
977 if (NxpConfig_GetByteArray(NAME_UWB_VENDOR_CAPABILITY, buffer.data(),
978 buffer.size(), &retlen) && retlen) {
979 auto vendorTlvs = decodeTlvBytes({}, buffer.data(), retlen);
980 for (auto const& [key, val] : vendorTlvs) {
981 tlvs[key] = val;
982 }
983 }
984 }
985
986 // Apply restrictions
987 const phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
988
989 uint8_t fira_channels = 0xff;
990 if (rt_set->restricted_channel_mask & (1 << 5))
991 fira_channels &= CHANNEL_5_MASK;
992 if (rt_set->restricted_channel_mask & (1 << 9))
993 fira_channels &= CHANNEL_9_MASK;
994
995 uint8_t ccc_channels = 0;
996 if (!(rt_set->restricted_channel_mask & (1 << 5)))
997 ccc_channels |= 0x01;
998 if (!(rt_set->restricted_channel_mask & (1 << 9)))
999 ccc_channels |= 0x02;
1000
1001 tlvs[UWB_CHANNELS] = std::vector{fira_channels};
1002 tlvs[CCC_UWB_CHANNELS] = std::vector{ccc_channels};
1003
1004 // Convert it back to raw packet bytes
1005 uint8_t packet[256];
1006
1007 // header
1008 memcpy(packet, p_data, UCI_MSG_HDR_SIZE);
1009 // status
1010 packet[UCI_RESPONSE_STATUS_OFFSET] = UWBSTATUS_SUCCESS;
1011 // nr
1012 packet[UCI_MSG_CORE_GET_CAPS_INFO_NR_OFFSET] = tlvs.size();
1013
1014 // tlvs
1015 auto tlv_bytes = encodeTlvBytes(tlvs);
1016 if ((tlv_bytes.size() + UCI_MSG_CORE_GET_CAPS_INFO_TLV_OFFSET) > sizeof(packet)) {
1017 NXPLOG_UCIHAL_E("DevCaps overflow!");
1018 } else {
1019 uint8_t packet_len = UCI_MSG_CORE_GET_CAPS_INFO_TLV_OFFSET + tlv_bytes.size();
1020 packet[UCI_PAYLOAD_LENGTH_OFFSET] = packet_len - UCI_MSG_HDR_SIZE;
1021 memcpy(&packet[UCI_MSG_CORE_GET_CAPS_INFO_TLV_OFFSET], tlv_bytes.data(), tlv_bytes.size());
1022
1023 phNxpUciHal_print_packet(NXP_TML_UCI_RSP_NTF_UWBS_2_AP, packet, packet_len);
1024
1025 // send GET CAPS INFO response to the Upper Layer
1026 (*nxpucihal_ctrl.p_uwb_stack_data_cback)(packet_len, packet);
1027 // skip the incoming packet as we have send the modified response
1028 // already
1029 nxpucihal_ctrl.isSkipPacket = 1;
1030 }
1031 }
1032