blob: 8d4477a6812b3f00d3b8c029e55925f544fce714 [file] [log] [blame]
/*
* Copyright 2012-2019, 2022-2023 NXP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <sys/stat.h>
#include <atomic>
#include <bitset>
#include <map>
#include <vector>
#include <cutils/properties.h>
#include "phNxpConfig.h"
#include "phNxpLog.h"
#include "phNxpUciHal.h"
#include "phNxpUciHal_ext.h"
#include "phNxpUciHal_utils.h"
#include "phTmlUwb.h"
#include "phUwbCommon.h"
#include "sessionTrack.h"
/* Timeout value to wait for response from DEVICE_TYPE_SR1xx */
#define MAX_COMMAND_RETRY_COUNT 5
#define HAL_EXTNS_WRITE_RSP_TIMEOUT_MS 100
#define HAL_HW_RESET_NTF_TIMEOUT 10000 /* 10 sec wait */
/******************* Global variables *****************************************/
extern phNxpUciHal_Control_t nxpucihal_ctrl;
static std::vector<uint8_t> gtx_power;
/************** HAL extension functions ***************************************/
static bool phNxpUciHal_is_retry_not_required(uint8_t uci_octet0);
static void phNxpUciHal_clear_thermal_error_status();
static void phNxpUciHal_hw_reset_ntf_timeout_cb(uint32_t timerId,
void *pContext);
/******************************************************************************
* Function phNxpUciHal_process_ext_cmd_rsp
*
* Description This function process the extension command response. It
* also checks the received response to expected response.
*
* Returns returns UWBSTATUS_SUCCESS if response is as expected else
* returns failure.
*
******************************************************************************/
tHAL_UWB_STATUS phNxpUciHal_process_ext_cmd_rsp(size_t cmd_len,
const uint8_t *p_cmd) {
if (cmd_len > sizeof(nxpucihal_ctrl.p_cmd_data)) {
NXPLOG_UCIHAL_E("Packet size is too big to send: %u.", cmd_len);
return UWBSTATUS_FAILED;
}
if (cmd_len < 1) {
return UWBSTATUS_FAILED;
}
/* Create local copy of cmd_data */
memcpy(nxpucihal_ctrl.p_cmd_data, p_cmd, cmd_len);
nxpucihal_ctrl.cmd_len = cmd_len;
// PBF=1 or DATA packet: don't check RSP
// upper-layer should handle the case of UWBSTATUS_COMMAND_RETRANSMIT && isRetryNotRequired
if (phNxpUciHal_is_retry_not_required(p_cmd[0]) ||
cmd_len < UCI_MSG_HDR_SIZE) {
return phNxpUciHal_write_unlocked();
}
const uint8_t mt = (p_cmd[0] & UCI_MT_MASK) >> UCI_MT_SHIFT;
const uint8_t gid = p_cmd[0] & UCI_GID_MASK;
const uint8_t oid = p_cmd[1] & UCI_OID_MASK;
if (mt == UCI_MT_CMD && gid == UCI_GID_SESSION_CONTROL && oid == UCI_MSG_SESSION_START) {
SessionTrack_onSessionStart(cmd_len, p_cmd);
}
/* Vendor Specific Parsing logic */
if (phNxpUciHal_parse(nxpucihal_ctrl.cmd_len, nxpucihal_ctrl.p_cmd_data)) {
return UWBSTATUS_SUCCESS;
}
tHAL_UWB_STATUS status = UWBSTATUS_FAILED;
int nr_retries = 0;
int nr_timedout = 0;
while(nr_retries < MAX_COMMAND_RETRY_COUNT) {
nxpucihal_ctrl.cmdrsp.StartCmd(gid, oid);
status = phNxpUciHal_write_unlocked();
if (status != UWBSTATUS_SUCCESS) {
NXPLOG_UCIHAL_D("phNxpUciHal_write failed for hal ext");
nxpucihal_ctrl.cmdrsp.Cancel();
return status;
}
// Wait for rsp
status = nxpucihal_ctrl.cmdrsp.Wait(HAL_EXTNS_WRITE_RSP_TIMEOUT_MS);
if (status == UWBSTATUS_RESPONSE_TIMEOUT) {
nr_timedout++;
nr_retries++;
} else if (status == UWBSTATUS_COMMAND_RETRANSMIT) {
// TODO: Do not retransmit CMD by here when !nxpucihal_ctrl.hal_ext_enabled,
// Upper layer should take care of it.
nr_retries++;
} else {
break;
}
}
if (nr_retries >= MAX_COMMAND_RETRY_COUNT) {
NXPLOG_UCIHAL_E("Failed to process cmd/rsp 0x%x", status);
phNxpUciHal_send_dev_error_status_ntf();
return UWBSTATUS_FAILED;
}
if (nr_timedout > 0) {
NXPLOG_UCIHAL_E("Warning: CMD/RSP retried %d times (timeout:%d)\n",
nr_retries, nr_timedout);
}
return status;
}
/******************************************************************************
* Function phNxpUciHal_send_ext_cmd
*
* Description This function send the extension command to UWBC. No
* response is checked by this function but it waits for
* the response to come.
*
* Returns Returns UWBSTATUS_SUCCESS if sending cmd is successful and
* response is received.
*
******************************************************************************/
tHAL_UWB_STATUS phNxpUciHal_send_ext_cmd(uint16_t cmd_len, const uint8_t* p_cmd) {
if (cmd_len >= UCI_MAX_DATA_LEN) {
return UWBSTATUS_FAILED;
}
HAL_ENABLE_EXT();
tHAL_UWB_STATUS status = phNxpUciHal_process_ext_cmd_rsp(cmd_len, p_cmd);
HAL_DISABLE_EXT();
return status;
}
/******************************************************************************
* Function phNxpUciHal_set_board_config
*
* Description This function is called to set the board varaint config
* Returns return 0 on success and -1 on fail, On success
* update the acutual state of operation in arg pointer
*
******************************************************************************/
tHAL_UWB_STATUS phNxpUciHal_set_board_config(){
tHAL_UWB_STATUS status;
uint8_t buffer[] = {0x2E,0x00,0x00,0x02,0x01,0x01};
/* Set the board variant configurations */
unsigned long num = 0;
NXPLOG_UCIHAL_D("%s: enter; ", __func__);
uint8_t boardConfig = 0, boardVersion = 0;
if(NxpConfig_GetNum(NAME_UWB_BOARD_VARIANT_CONFIG, &num, sizeof(num))){
boardConfig = (uint8_t)num;
NXPLOG_UCIHAL_D("%s: NAME_UWB_BOARD_VARIANT_CONFIG: %x", __func__,boardConfig);
} else {
NXPLOG_UCIHAL_D("%s: NAME_UWB_BOARD_VARIANT_CONFIG: failed %x", __func__,boardConfig);
}
if(NxpConfig_GetNum(NAME_UWB_BOARD_VARIANT_VERSION, &num, sizeof(num))){
boardVersion = (uint8_t)num;
NXPLOG_UCIHAL_D("%s: NAME_UWB_BOARD_VARIANT_VERSION: %x", __func__,boardVersion);
} else{
NXPLOG_UCIHAL_D("%s: NAME_UWB_BOARD_VARIANT_VERSION: failed %lx", __func__,num);
}
buffer[4] = boardConfig;
buffer[5] = boardVersion;
status = phNxpUciHal_send_ext_cmd(sizeof(buffer), buffer);
return status;
}
/*******************************************************************************
**
** Function phNxpUciHal_process_ext_rsp
**
** Description Process extension function response
**
** Returns UWBSTATUS_SUCCESS if success
**
*******************************************************************************/
tHAL_UWB_STATUS phNxpUciHal_process_ext_rsp(uint16_t rsp_len, uint8_t* p_buff){
tHAL_UWB_STATUS status;
int NumOfTlv, index;
uint8_t paramId, extParamId, IdStatus;
index = UCI_NTF_PAYLOAD_OFFSET; // index for payload start
status = p_buff[index++];
if(status == UCI_STATUS_OK){
NXPLOG_UCIHAL_D("%s: status success %d", __func__, status);
return UWBSTATUS_SUCCESS;
}
NumOfTlv = p_buff[index++];
while (index < rsp_len) {
paramId = p_buff[index++];
if(paramId == EXTENDED_DEVICE_CONFIG_ID) {
extParamId = p_buff[index++];
IdStatus = p_buff[index++];
switch(extParamId) {
case UCI_EXT_PARAM_DELAY_CALIBRATION_VALUE:
case UCI_EXT_PARAM_AOA_CALIBRATION_CTRL:
case UCI_EXT_PARAM_DPD_WAKEUP_SRC:
case UCI_EXT_PARAM_WTX_COUNT_CONFIG:
case UCI_EXT_PARAM_DPD_ENTRY_TIMEOUT:
case UCI_EXT_PARAM_WIFI_COEX_FEATURE:
case UCI_EXT_PARAM_TX_BASE_BAND_CONFIG:
case UCI_EXT_PARAM_DDFS_TONE_CONFIG:
case UCI_EXT_PARAM_TX_PULSE_SHAPE_CONFIG:
case UCI_EXT_PARAM_CLK_CONFIG_CTRL:
if(IdStatus == UCI_STATUS_FEATURE_NOT_SUPPORTED){
NXPLOG_UCIHAL_E("%s: Vendor config param: %x %x is Not Supported", __func__, paramId, extParamId);
status = UWBSTATUS_SUCCESS;
} else {
status = UWBSTATUS_FAILED;
return status;
}
break;
default:
NXPLOG_UCIHAL_D("%s: Vendor param ID: %x", __func__, extParamId);
break;
}
} else {
IdStatus = p_buff[index++];
switch(paramId) {
case UCI_PARAM_ID_LOW_POWER_MODE:
if(IdStatus == UCI_STATUS_FEATURE_NOT_SUPPORTED){
NXPLOG_UCIHAL_E("%s: Generic config param: %x is Not Supported", __func__, paramId);
status = UWBSTATUS_SUCCESS;
} else {
status = UWBSTATUS_FAILED;
return status;
}
break;
default:
NXPLOG_UCIHAL_D("%s: Generic param ID: %x", __func__, paramId);
break;
}
}
}
NXPLOG_UCIHAL_D("%s: exit %d", __func__, status);
return status;
}
/*******************************************************************************
* Function phNxpUciHal_resetRuntimeSettings
*
* Description reset per-country code settigs to default
*
*******************************************************************************/
static void phNxpUciHal_resetRuntimeSettings(void)
{
phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
rt_set->uwb_enable = true;
rt_set->restricted_channel_mask = 0;
rt_set->tx_power_offset = 0;
}
/*******************************************************************************
* Function phNxpUciHal_applyCountryCaps
*
* Description Update runtime settings with given COUNTRY_CODE_CAPS byte array
*
* Returns void
*
*******************************************************************************/
static void phNxpUciHal_applyCountryCaps(const char country_code[2],
const uint8_t *cc_resp, uint32_t cc_resp_len)
{
phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
uint16_t idx = 1; // first byte = number countries
bool country_code_found = false;
while (idx < cc_resp_len) {
uint8_t tag = cc_resp[idx++];
uint8_t len = cc_resp[idx++];
if (country_code_found) {
switch (tag) {
case UWB_ENABLE_TAG:
if (len == 1) {
rt_set->uwb_enable = cc_resp[idx];
NXPLOG_UCIHAL_D("CountryCaps uwb_enable = %u", cc_resp[idx]);
}
break;
case CHANNEL_5_TAG:
if (len == 1 && !cc_resp[idx]) {
rt_set->restricted_channel_mask |= 1<< 5;
NXPLOG_UCIHAL_D("CountryCaps channel 5 support = %u", cc_resp[idx]);
}
break;
case CHANNEL_9_TAG:
if (len == 1 && !cc_resp[idx]) {
rt_set->restricted_channel_mask |= 1<< 9;
NXPLOG_UCIHAL_D("CountryCaps channel 9 support = %u", cc_resp[idx]);
}
break;
case TX_POWER_TAG:
if (len == 2) {
rt_set->tx_power_offset = (short)((cc_resp[idx + 0]) | (((cc_resp[idx + 1]) << RMS_TX_POWER_SHIFT) & 0xFF00));
NXPLOG_UCIHAL_D("CountryCaps tx_power_offset = %d", rt_set->tx_power_offset);
}
break;
default:
break;
}
}
if (tag == COUNTRY_CODE_TAG) {
country_code_found = (cc_resp[idx + 0] == country_code[0]) && (cc_resp[idx + 1] == country_code[1]);
}
idx += len;
}
}
/*******************************************************************************
* Function phNxpUciHal_is_retry_not_required
*
* Description UCI command retry check
*
* Returns true/false
*
*******************************************************************************/
static bool phNxpUciHal_is_retry_not_required(uint8_t uci_octet0) {
bool isRetryRequired = false, isChained_cmd = false, isData_Msg = false;
isChained_cmd = (bool)((uci_octet0 & UCI_PBF_ST_CONT) >> UCI_PBF_SHIFT);
isData_Msg = ((uci_octet0 & UCI_MT_MASK) >> UCI_MT_SHIFT) == UCI_MT_DATA;
isRetryRequired = isChained_cmd | isData_Msg;
return isRetryRequired;
}
/******************************************************************************
* Function CountryCodeCapsGenTxPowerPacket
*
* Description This function makes tx antenna power calibration command
* with gtx_power[] + tx_power_offset
*
* Returns true if packet has been updated
*
******************************************************************************/
static bool CountryCodeCapsGenTxPowerPacket(uint8_t *packet, size_t packet_len, uint16_t *out_len)
{
phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
if (rt_set->tx_power_offset == 0)
return false;
if (gtx_power.empty())
return false;
if (gtx_power.size() > packet_len)
return false;
uint16_t gtx_power_len = gtx_power.size();
memcpy(packet, gtx_power.data(), gtx_power_len);
uint8_t index = UCI_MSG_HDR_SIZE + 2; // channel + Tag
// Length
if (index > gtx_power_len) {
NXPLOG_UCIHAL_E("Upper-layer calibration command for tx_power is invalid.");
return false;
}
if (!packet[index] || (packet[index] + index) > gtx_power_len) {
NXPLOG_UCIHAL_E("Upper-layer calibration command for tx_power is invalid.");
return false;
}
index++;
// Value[0] = Number of antennas
uint8_t num_of_antennas = packet[index++];
while (num_of_antennas--) {
// each entry = { antenna-id(1) + peak_tx(2) + id_rms(2) }
if ((index + 5) < gtx_power_len) {
NXPLOG_UCIHAL_E("Upper-layer calibration command for tx_power is invalid.");
return false;
}
index += 3; // antenna Id(1) + Peak Tx(2)
// Adjust id_rms part
long tx_power_long = packet[index] | ((packet[index + 1] << RMS_TX_POWER_SHIFT) & 0xFF00);
tx_power_long += rt_set->tx_power_offset;
if (tx_power_long < 0)
tx_power_long = 0;
uint16_t tx_power_u16 = (uint16_t)tx_power_long;
packet[index++] = tx_power_u16 & 0xff;
packet[index++] = tx_power_u16 >> RMS_TX_POWER_SHIFT;
}
if (out_len)
*out_len = gtx_power_len;
return true;
}
/*******************************************************************************
* Function phNxpUciHal_handle_set_calibration
*
* Description Remembers SET_VENDOR_SET_CALIBRATION_CMD packet
*
* Returns void
*
*******************************************************************************/
void phNxpUciHal_handle_set_calibration(const uint8_t *p_data, uint16_t data_len)
{
// Only saves the SET_CALIBRATION_CMD from upper-layer
if (nxpucihal_ctrl.hal_ext_enabled) {
return;
}
// SET_DEVICE_CALIBRATION_CMD Packet format: Channel(1) + TLV
if (data_len < 6) {
return;
}
const uint8_t channel = p_data[UCI_MSG_HDR_SIZE + 0];
const uint8_t tag = p_data[UCI_MSG_HDR_SIZE + 1];
if (tag != NXP_PARAM_ID_TX_POWER_PER_ANTENNA) {
return;
}
phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
// RMS Tx power -> Octet [4, 5] in calib data
NXPLOG_UCIHAL_D("phNxpUciHal_handle_set_calibration channel=%u tx_power_offset=%d", channel, rt_set->tx_power_offset);
// Backup the packet to gtx_power[]
gtx_power = std::move(std::vector<uint8_t> {p_data, p_data + data_len});
// Patch SET_CALIBRATION_CMD per gtx_power + tx_power_offset
CountryCodeCapsGenTxPowerPacket(nxpucihal_ctrl.p_cmd_data, sizeof(nxpucihal_ctrl.p_cmd_data), &nxpucihal_ctrl.cmd_len);
}
/******************************************************************************
* Function CountryCodeCapsApplyTxPower
*
* Description This function sets the TX power from COUNTRY_CODE_CAPS
*
* Returns false if no tx_power_offset or no Upper-layer Calibration cmd was given
* true if it was successfully applied.
*
******************************************************************************/
static bool CountryCodeCapsApplyTxPower(void)
{
if (gtx_power.empty())
return false;
// use whole packet as-is from upper-layer command (gtx_power[])
std::vector<uint8_t> packet(gtx_power.size());
uint16_t packet_size = 0;
if (!CountryCodeCapsGenTxPowerPacket(packet.data(), packet.size(), &packet_size))
return false;
tHAL_UWB_STATUS status = phNxpUciHal_send_ext_cmd(packet_size, packet.data());
if (status != UWBSTATUS_SUCCESS) {
NXPLOG_UCIHAL_D("%s: send failed", __func__);
}
gtx_power.clear();
return true;
}
static void extcal_do_xtal(void)
{
int ret;
// RF_CLK_ACCURACY_CALIB (otp supported)
// parameters: cal.otp.xtal=0|1, cal.xtal=X
uint8_t otp_xtal_flag = 0;
uint8_t xtal_data[32];
size_t xtal_data_len = 0;
if (NxpConfig_GetNum("cal.otp.xtal", &otp_xtal_flag, 1) && otp_xtal_flag) {
nxpucihal_ctrl.uwb_chip->read_otp(EXTCAL_PARAM_CLK_ACCURACY, xtal_data, sizeof(xtal_data), &xtal_data_len);
}
if (!xtal_data_len) {
long retlen = 0;
if (NxpConfig_GetByteArray("cal.xtal", xtal_data, sizeof(xtal_data), &retlen)) {
xtal_data_len = retlen;
}
}
if (xtal_data_len) {
NXPLOG_UCIHAL_D("Apply CLK_ACCURARY (len=%zu, from-otp=%c)", xtal_data_len, otp_xtal_flag ? 'y' : 'n');
ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_CLK_ACCURACY, 0, xtal_data, xtal_data_len);
if (ret != UWBSTATUS_SUCCESS) {
NXPLOG_UCIHAL_E("Failed to apply CLK_ACCURACY (len=%zu, from-otp=%c)",
xtal_data_len, otp_xtal_flag ? 'y' : 'n');
}
}
}
static void extcal_do_ant_delay(void)
{
std::bitset<8> rx_antenna_mask(nxpucihal_ctrl.cal_rx_antenna_mask);
const uint8_t n_rx_antennas = rx_antenna_mask.size();
const uint8_t *cal_channels = NULL;
uint8_t nr_cal_channels = 0;
nxpucihal_ctrl.uwb_chip->get_supported_channels(&cal_channels, &nr_cal_channels);
// RX_ANT_DELAY_CALIB
// parameter: cal.ant<N>.ch<N>.ant_delay=X
// N(1) + N * {AntennaID(1), Rxdelay(Q14.2)}
if (n_rx_antennas) {
for (int i = 0; i < nr_cal_channels; i++) {
uint8_t ch = cal_channels[i];
std::vector<uint8_t> entries;
uint8_t n_entries = 0;
for (auto i = 0; i < n_rx_antennas; i++) {
if (!rx_antenna_mask[i])
continue;
const uint8_t ant_id = i + 1;
uint16_t delay_value, version_value;
bool value_provided = false;
const std::string key_ant_delay = std::format("cal.ant{}.ch{}.ant_delay", ant_id, ch);
const std::string key_force_version = key_ant_delay + std::format(".force_version", ant_id, ch);
// 1) try cal.ant{N}.ch{N}.ant_delay.force_value.{N}
if (NxpConfig_GetNum(key_force_version.c_str(), &version_value, 2)) {
const std::string key_force_value = key_ant_delay + std::format(".force_value.{}", ant_id, ch, version_value);
if (NxpConfig_GetNum(key_force_value.c_str(), &delay_value, 2)) {
value_provided = true;
NXPLOG_UCIHAL_D("Apply RX_ANT_DELAY_CALIB %s = %u", key_force_value.c_str(), delay_value);
}
}
// 2) try cal.ant{N}.ch{N}.ant_delay
if (!value_provided) {
if (NxpConfig_GetNum(key_ant_delay.c_str(), &delay_value, 2)) {
value_provided = true;
NXPLOG_UCIHAL_D("Apply RX_ANT_DELAY_CALIB: %s = %u", key_ant_delay.c_str(), delay_value);
}
}
if (!value_provided) {
NXPLOG_UCIHAL_V("%s was not provided from configuration files.", key_ant_delay.c_str());
continue;
}
entries.push_back(ant_id);
// Little Endian
entries.push_back(delay_value & 0xff);
entries.push_back(delay_value >> 8);
n_entries++;
}
if (!n_entries)
continue;
entries.insert(entries.begin(), n_entries);
tHAL_UWB_STATUS ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_RX_ANT_DELAY, ch, entries.data(), entries.size());
if (ret != UWBSTATUS_SUCCESS) {
NXPLOG_UCIHAL_E("Failed to apply RX_ANT_DELAY for channel %u", ch);
}
}
}
}
static void extcal_do_tx_power(void)
{
std::bitset<8> tx_antenna_mask(nxpucihal_ctrl.cal_tx_antenna_mask);
const uint8_t n_tx_antennas = tx_antenna_mask.size();
const uint8_t *cal_channels = NULL;
uint8_t nr_cal_channels = 0;
nxpucihal_ctrl.uwb_chip->get_supported_channels(&cal_channels, &nr_cal_channels);
// TX_POWER
// parameter: cal.ant<N>.ch<N>.tx_power={...}
if (n_tx_antennas) {
for (int i = 0; i < nr_cal_channels; i++) {
uint8_t ch = cal_channels[i];
std::vector<uint8_t> entries;
uint8_t n_entries = 0;
for (auto i = 0; i < n_tx_antennas; i++) {
if (!tx_antenna_mask[i])
continue;
char key[32];
const uint8_t ant_id = i + 1;
std::snprintf(key, sizeof(key), "cal.ant%u.ch%u.tx_power", ant_id, ch);
uint8_t power_value[32];
long retlen = 0;
if (!NxpConfig_GetByteArray(key, power_value, sizeof(power_value), &retlen)) {
continue;
}
NXPLOG_UCIHAL_D("Apply TX_POWER: %s = { %lu bytes }", key, retlen);
entries.push_back(ant_id);
entries.insert(entries.end(), power_value, power_value + retlen);
n_entries++;
}
if (!n_entries)
continue;
entries.insert(entries.begin(), n_entries);
tHAL_UWB_STATUS ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_TX_POWER, ch, entries.data(), entries.size());
if (ret != UWBSTATUS_SUCCESS) {
NXPLOG_UCIHAL_E("Failed to apply TX_POWER for channel %u", ch);
}
}
}
}
static void extcal_do_tx_pulse_shape(void)
{
// parameters: cal.tx_pulse_shape={...}
long retlen = 0;
uint8_t data[64];
if (NxpConfig_GetByteArray("cal.tx_pulse_shape", data, sizeof(data), &retlen) && retlen) {
NXPLOG_UCIHAL_D("Apply TX_PULSE_SHAPE: data = { %lu bytes }", retlen);
tHAL_UWB_STATUS ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_TX_PULSE_SHAPE, 0, data, (size_t)retlen);
if (ret != UWBSTATUS_SUCCESS) {
NXPLOG_UCIHAL_E("Failed to apply TX_PULSE_SHAPE.");
}
}
}
static void extcal_do_tx_base_band(void)
{
// TX_BASE_BAND_CONTROL, DDFS_TONE_CONFIG
// parameters: cal.ddfs_enable=1|0, cal.dc_suppress=1|0, ddfs_tone_config={...}
uint8_t ddfs_enable = 0, dc_suppress = 0;
uint8_t ddfs_tone[256];
long retlen = 0;
tHAL_UWB_STATUS ret;
if (NxpConfig_GetNum("cal.ddfs_enable", &ddfs_enable, 1)) {
NXPLOG_UCIHAL_D("Apply TX_BASE_BAND_CONTROL: ddfs_enable=%u", ddfs_enable);
}
if (NxpConfig_GetNum("cal.dc_suppress", &dc_suppress, 1)) {
NXPLOG_UCIHAL_D("Apply TX_BASE_BAND_CONTROL: dc_suppress=%u", dc_suppress);
}
// DDFS_TONE_CONFIG
if (ddfs_enable) {
if (!NxpConfig_GetByteArray("cal.ddfs_tone_config", ddfs_tone, sizeof(ddfs_tone), &retlen) || !retlen) {
NXPLOG_UCIHAL_E("cal.ddfs_tone_config is not supplied while cal.ddfs_enable=1, ddfs was not enabled.");
ddfs_enable = 0;
} else {
NXPLOG_UCIHAL_D("Apply DDFS_TONE_CONFIG: ddfs_tone_config = { %lu bytes }", retlen);
ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_DDFS_TONE_CONFIG, 0, ddfs_tone, (size_t)retlen);
if (ret != UWBSTATUS_SUCCESS) {
NXPLOG_UCIHAL_E("Failed to apply DDFS_TONE_CONFIG, ddfs was not enabled.");
ddfs_enable = 0;
}
}
}
// TX_BASE_BAND_CONTROL
{
uint8_t flag = 0;
if (ddfs_enable)
flag |= 0x01;
if (dc_suppress)
flag |= 0x02;
ret = nxpucihal_ctrl.uwb_chip->apply_calibration(EXTCAL_PARAM_TX_BASE_BAND_CONTROL, 0, &flag, 1);
if (ret) {
NXPLOG_UCIHAL_E("Failed to apply TX_BASE_BAND_CONTROL");
}
}
}
/******************************************************************************
* Function phNxpUciHal_extcal_handle_coreinit
*
* Description Apply additional core device settings
*
* Returns void.
*
******************************************************************************/
void phNxpUciHal_extcal_handle_coreinit(void)
{
// read rx_aantenna_mask, tx_antenna_mask
uint8_t rx_antenna_mask_n = 0x1;
uint8_t tx_antenna_mask_n = 0x1;
if (!NxpConfig_GetNum("cal.rx_antenna_mask", &rx_antenna_mask_n, 1)) {
NXPLOG_UCIHAL_E("cal.rx_antenna_mask is not specified, use default 0x%x", rx_antenna_mask_n);
}
if (!NxpConfig_GetNum("cal.tx_antenna_mask", &tx_antenna_mask_n, 1)) {
NXPLOG_UCIHAL_E("cal.tx_antenna_mask is not specified, use default 0x%x", tx_antenna_mask_n);
}
nxpucihal_ctrl.cal_rx_antenna_mask = rx_antenna_mask_n;
nxpucihal_ctrl.cal_tx_antenna_mask = tx_antenna_mask_n;
extcal_do_xtal();
extcal_do_ant_delay();
}
void apply_per_country_calibrations(void)
{
// TX-POWER can be provided by
// 1) COUNTRY_CODE_CAPS with offset values.
// 2) Extra calibration files with absolute tx power values
// only one should be applied if both were provided by platform
if (!CountryCodeCapsApplyTxPower()) {
extcal_do_tx_power();
}
// These are only available from extra calibration files
extcal_do_tx_pulse_shape();
extcal_do_tx_base_band();
}
/******************************************************************************
* Function phNxpUciHal_handle_set_country_code
*
* Description Apply per-country settings
*
* Returns void.
*
******************************************************************************/
void phNxpUciHal_handle_set_country_code(const char country_code[2])
{
NXPLOG_UCIHAL_D("Apply country code %c%c", country_code[0], country_code[1]);
phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
phNxpUciHal_resetRuntimeSettings();
if (!is_valid_country_code(country_code)) {
NXPLOG_UCIHAL_D("Country code %c%c is invalid, UWB should be disabled", country_code[0], country_code[1]);
}
if (NxpConfig_SetCountryCode(country_code)) {
// Load ExtraCal restrictions
uint16_t mask= 0;
if (NxpConfig_GetNum("cal.restricted_channels", &mask, sizeof(mask))) {
NXPLOG_UCIHAL_D("Restriction flag, restricted channel mask=0x%x", mask);
rt_set->restricted_channel_mask = mask;
}
uint8_t uwb_disable = 0;
if (NxpConfig_GetNum("cal.uwb_disable", &uwb_disable, sizeof(uwb_disable))) {
NXPLOG_UCIHAL_D("Restriction flag, uwb_disable=%u", uwb_disable);
rt_set->uwb_enable = !uwb_disable;
}
// Apply COUNTRY_CODE_CAPS
uint8_t cc_caps[UCI_MAX_DATA_LEN];
long retlen = 0;
if (NxpConfig_GetByteArray(NAME_NXP_UWB_COUNTRY_CODE_CAPS, cc_caps, sizeof(cc_caps), &retlen) && retlen) {
NXPLOG_UCIHAL_D("COUNTRY_CODE_CAPS is provided.");
phNxpUciHal_applyCountryCaps(country_code, cc_caps, retlen);
}
// Check country code validity
if (!is_valid_country_code(country_code) && rt_set->uwb_enable) {
NXPLOG_UCIHAL_E("UWB is not disabled by configuration files with invalid country code %c%c,"
" forcing it disabled", country_code[0], country_code[1]);
rt_set->uwb_enable = false;
}
// Apply per-country calibration, it's handled by SessionTrack
SessionTrack_onCountryCodeChanged();
}
// send country code response to upper layer
static uint8_t rsp_data[5] = { 0x4c, 0x01, 0x00, 0x01 };
if (rt_set->uwb_enable) {
rsp_data[4] = UWBSTATUS_SUCCESS;
} else {
rsp_data[4] = UCI_STATUS_CODE_ANDROID_REGULATION_UWB_OFF;
}
(*nxpucihal_ctrl.p_uwb_stack_data_cback)(sizeof(rsp_data), rsp_data);
}
// TODO: support fragmented packets
/*************************************************************************************
* Function phNxpUciHal_handle_set_app_config
*
* Description Handle SESSION_SET_APP_CONFIG_CMD packet,
* remove unsupported parameters
*
* Returns true : SESSION_SET_APP_CONFIG_CMD/RSP was handled by this function
* false : This packet should go to chip
*
*************************************************************************************/
bool phNxpUciHal_handle_set_app_config(uint16_t *data_len, uint8_t *p_data)
{
const phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
// Android vendor specific app configs not supported by FW
const uint8_t tags_to_del[] = {
UCI_PARAM_ID_TX_ADAPTIVE_PAYLOAD_POWER,
UCI_PARAM_ID_AOA_AZIMUTH_MEASUREMENTS,
UCI_PARAM_ID_AOA_ELEVATION_MEASUREMENTS,
UCI_PARAM_ID_RANGE_MEASUREMENTS
};
// check basic validity
uint16_t payload_len = (p_data[UCI_CMD_LENGTH_PARAM_BYTE1] & 0xFF) |
((p_data[UCI_CMD_LENGTH_PARAM_BYTE2] & 0xFF) << 8);
if (payload_len != (*data_len - UCI_MSG_HDR_SIZE)) {
NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD: payload length mismatch");
return false;
}
if (!p_data[UCI_CMD_NUM_CONFIG_PARAM_BYTE]) {
return false;
}
uint32_t session_handle = le_bytes_to_cpu<uint32_t>(&p_data[UCI_MSG_SESSION_SET_APP_CONFIG_HANDLE_OFFSET]);
uint8_t ch = 0;
// Create local copy of cmd_data for data manipulation
uint8_t uciCmd[UCI_MAX_DATA_LEN];
uint16_t packet_len = *data_len;
if (sizeof(uciCmd) < packet_len) {
NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD packet size %u is too big to handle, skip patching.", packet_len);
return false;
}
// 9 = Header 4 + SessionID 4 + NumOfConfigs 1
uint8_t i = 9, j = 9;
uint8_t nr_deleted = 0, bytes_deleted = 0;
memcpy(uciCmd, p_data, i);
while (i < packet_len) {
if ( (i + 2) >= packet_len) {
NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD parse error at %u", i);
return false;
}
// All parameters should have 1 byte tag
uint8_t tlv_tag = p_data[i + 0];
uint8_t tlv_len = p_data[i + 1];
uint8_t param_len = 2 + tlv_len;
if ((i + param_len) > packet_len) {
NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD parse error at %u", i);
}
// check restricted channel
if (tlv_tag == UCI_PARAM_ID_CHANNEL_NUMBER && tlv_len == 1) {
ch = p_data[i + 2];
if (((ch == CHANNEL_NUM_5) && (rt_set->restricted_channel_mask & (1 << 5))) ||
((ch == CHANNEL_NUM_9) && (rt_set->restricted_channel_mask & (1 << 9)))) {
phNxpUciHal_print_packet(NXP_TML_UCI_CMD_AP_2_UWBS, p_data, packet_len);
NXPLOG_UCIHAL_D("Country code blocked channel %u", ch);
// send setAppConfig response with UCI_STATUS_CODE_ANDROID_REGULATION_UWB_OFF response
static uint8_t rsp_data[] = { 0x41, 0x03, 0x04, 0x04,
UCI_STATUS_FAILED, 0x01, tlv_tag, UCI_STATUS_CODE_ANDROID_REGULATION_UWB_OFF
};
(*nxpucihal_ctrl.p_uwb_stack_data_cback)(sizeof(rsp_data), rsp_data);
return true;
}
}
// remove unsupported parameters
if (std::find(std::begin(tags_to_del), std::end(tags_to_del), tlv_tag) == std::end(tags_to_del)) {
memcpy(&uciCmd[j], &p_data[i], param_len);
j += param_len;
} else {
NXPLOG_UCIHAL_D("Removed param payload with Tag ID:0x%02x", tlv_tag);
nr_deleted++;
bytes_deleted += param_len;
}
i += param_len;
}
if (nr_deleted) {
phNxpUciHal_print_packet(NXP_TML_UCI_CMD_AP_2_UWBS, p_data, packet_len);
// uci number of config params update
if (uciCmd[UCI_CMD_NUM_CONFIG_PARAM_BYTE] < nr_deleted) {
NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD cannot parse packet: wrong nr_parameters");
return false;
}
uciCmd[UCI_CMD_NUM_CONFIG_PARAM_BYTE] -= nr_deleted;
// uci command length update
if (packet_len < (UCI_MSG_HDR_SIZE + bytes_deleted)) {
NXPLOG_UCIHAL_E("SESSION_SET_APP_CONFIG_CMD cannot parse packet: underflow");
return false;
}
packet_len -= bytes_deleted;
payload_len = packet_len - UCI_MSG_HDR_SIZE;
uciCmd[UCI_CMD_LENGTH_PARAM_BYTE2] = (payload_len & 0xFF00) >> 8;
uciCmd[UCI_CMD_LENGTH_PARAM_BYTE1] = (payload_len & 0xFF);
// Swap
memcpy(p_data, uciCmd, packet_len);
*data_len = packet_len;
}
SessionTrack_onAppConfig(session_handle, ch);
return false;
}
bool phNxpUciHal_handle_get_caps_info(size_t data_len, const uint8_t *p_data)
{
if (data_len < UCI_MSG_CORE_GET_CAPS_INFO_NR_OFFSET)
return false;
uint8_t status = p_data[UCI_RESPONSE_STATUS_OFFSET];
uint8_t nr = p_data[UCI_MSG_CORE_GET_CAPS_INFO_NR_OFFSET];
if (status != UWBSTATUS_SUCCESS || nr < 1)
return false;
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);
if (tlvs.size() != nr) {
NXPLOG_UCIHAL_E("Failed to parse DevCaps %zu != %u", tlvs.size(), nr);
}
// Remove all NXP vendor specific parameters
for (auto it = tlvs.begin(); it != tlvs.end();) {
if (it->first > 0xff)
it = tlvs.erase(it);
else
it++;
}
// Override AOA_SUPPORT_TAG_ID
auto it = tlvs.find(AOA_SUPPORT_TAG_ID);
if (it != tlvs.end()) {
if (nxpucihal_ctrl.numberOfAntennaPairs == 1) {
it->second = std::vector<uint8_t>{0x01};
} else if (nxpucihal_ctrl.numberOfAntennaPairs > 1) {
it->second = std::vector<uint8_t>{0x05};
} else {
it->second = std::vector<uint8_t>{0x00};
}
}
// Byteorder of CCC_SUPPORTED_PROTOCOL_VERSIONS_ID
it = tlvs.find(CCC_SUPPORTED_PROTOCOL_VERSIONS_ID);
if (it != tlvs.end() && it->second.size() == 2) {
std::swap(it->second[0], it->second[1]);
}
// Append UWB_VENDOR_CAPABILITY from configuration files
{
std::array<uint8_t, NXP_MAX_CONFIG_STRING_LEN> buffer;
long retlen = 0;
if (NxpConfig_GetByteArray(NAME_UWB_VENDOR_CAPABILITY, buffer.data(),
buffer.size(), &retlen) && retlen) {
auto vendorTlvs = decodeTlvBytes({}, buffer.data(), retlen);
for (auto const& [key, val] : vendorTlvs) {
tlvs[key] = val;
}
}
}
// Apply restrictions
const phNxpUciHal_Runtime_Settings_t *rt_set = &nxpucihal_ctrl.rt_settings;
uint8_t fira_channels = 0xff;
if (rt_set->restricted_channel_mask & (1 << 5))
fira_channels &= CHANNEL_5_MASK;
if (rt_set->restricted_channel_mask & (1 << 9))
fira_channels &= CHANNEL_9_MASK;
uint8_t ccc_channels = 0;
if (!(rt_set->restricted_channel_mask & (1 << 5)))
ccc_channels |= 0x01;
if (!(rt_set->restricted_channel_mask & (1 << 9)))
ccc_channels |= 0x02;
tlvs[UWB_CHANNELS] = std::vector{fira_channels};
tlvs[CCC_UWB_CHANNELS] = std::vector{ccc_channels};
// Convert it back to raw packet bytes
uint8_t packet[256];
// header
memcpy(packet, p_data, UCI_MSG_HDR_SIZE);
// status
packet[UCI_RESPONSE_STATUS_OFFSET] = UWBSTATUS_SUCCESS;
// nr
packet[UCI_MSG_CORE_GET_CAPS_INFO_NR_OFFSET] = tlvs.size();
// tlvs
auto tlv_bytes = encodeTlvBytes(tlvs);
if ((tlv_bytes.size() + UCI_MSG_CORE_GET_CAPS_INFO_TLV_OFFSET) > sizeof(packet)) {
NXPLOG_UCIHAL_E("DevCaps overflow!");
return false;
} else {
uint8_t packet_len = UCI_MSG_CORE_GET_CAPS_INFO_TLV_OFFSET + tlv_bytes.size();
packet[UCI_PAYLOAD_LENGTH_OFFSET] = packet_len - UCI_MSG_HDR_SIZE;
memcpy(&packet[UCI_MSG_CORE_GET_CAPS_INFO_TLV_OFFSET], tlv_bytes.data(), tlv_bytes.size());
phNxpUciHal_print_packet(NXP_TML_UCI_RSP_NTF_UWBS_2_AP, packet, packet_len);
// send GET CAPS INFO response to the Upper Layer
(*nxpucihal_ctrl.p_uwb_stack_data_cback)(packet_len, packet);
// skip the incoming packet as we have send the modified response
// already
return true;
}
}