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