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