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