• 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 <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