| /****************************************************************************** |
| * |
| * Copyright (C) 2011-2012 Broadcom Corporation |
| * Copyright 2018-2019, 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. |
| * |
| ******************************************************************************/ |
| #define LOG_TAG "NxpUwbConf" |
| |
| #include <sys/stat.h> |
| |
| #include <iomanip> |
| #include <memory> |
| #include <sstream> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <sstream> |
| #include <string> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <vector> |
| |
| #include <android-base/logging.h> |
| #include <cutils/properties.h> |
| #include <log/log.h> |
| |
| #include "phNxpConfig.h" |
| #include "phNxpUciHal.h" |
| #include "phNxpUciHal_ext.h" |
| #include "phNxpUciHal_utils.h" |
| #include "phNxpLog.h" |
| |
| const char default_nxp_config_path[] = "/vendor/etc/libuwb-nxp.conf"; |
| const char country_code_config_name[] = "libuwb-countrycode.conf"; |
| const char country_code_specifier[] = "<country>"; |
| |
| using namespace::std; |
| |
| class uwbParam |
| { |
| public: |
| enum class type { STRING, NUMBER, BYTEARRAY, STRINGARRAY }; |
| uwbParam(); |
| uwbParam(const uwbParam& param); |
| uwbParam(uwbParam&& param); |
| |
| uwbParam(const string& value); |
| uwbParam(vector<uint8_t>&& value); |
| uwbParam(unsigned long value); |
| uwbParam(vector<string>&& value); |
| |
| virtual ~uwbParam(); |
| |
| type getType() const { return m_type; } |
| unsigned long numValue() const {return m_numValue;} |
| const char* str_value() const {return m_str_value.c_str();} |
| size_t str_len() const {return m_str_value.length();} |
| const uint8_t* arr_value() const { return m_arrValue.data(); } |
| size_t arr_len() const { return m_arrValue.size(); } |
| |
| size_t str_arr_len() const { return m_arrStrValue.size(); } |
| const char* str_arr_elem(const int index) const { return m_arrStrValue[index].c_str(); } |
| size_t str_arr_elem_len(const int index) const { return m_arrStrValue[index].length(); } |
| |
| void dump(const string &tag) const; |
| private: |
| unsigned long m_numValue; |
| string m_str_value; |
| vector<uint8_t> m_arrValue; |
| vector<string> m_arrStrValue; |
| type m_type; |
| }; |
| |
| class CUwbNxpConfig |
| { |
| public: |
| CUwbNxpConfig(); |
| CUwbNxpConfig(CUwbNxpConfig&& config); |
| CUwbNxpConfig(const char *filepath); |
| virtual ~CUwbNxpConfig(); |
| CUwbNxpConfig& operator=(CUwbNxpConfig&& config); |
| |
| bool isValid() const { return mValidFile; } |
| bool isCountrySpecific() const { return mCountrySpecific; } |
| void reset() { |
| m_map.clear(); |
| mValidFile = false; |
| } |
| |
| const uwbParam* find(const char* p_name) const; |
| void setCountry(const string& strCountry); |
| |
| void dump() const; |
| |
| const unordered_map<string, uwbParam>& get_data() const { |
| return m_map; |
| } |
| private: |
| bool readConfig(); |
| |
| unordered_map<string, uwbParam> m_map; |
| bool mValidFile; |
| string mFilePath; |
| string mCurrentFile; |
| bool mCountrySpecific; |
| }; |
| |
| /******************************************************************************* |
| ** |
| ** Function: isPrintable() |
| ** |
| ** Description: determine if 'c' is printable |
| ** |
| ** Returns: 1, if printable, otherwise 0 |
| ** |
| *******************************************************************************/ |
| static inline bool isPrintable(char c) |
| { |
| return (c >= 'A' && c <= 'Z') || |
| (c >= 'a' && c <= 'z') || |
| (c >= '0' && c <= '9') || |
| c == '/' || c == '_' || c == '-' || c == '.' || c == ','; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: isDigit() |
| ** |
| ** Description: determine if 'c' is numeral digit |
| ** |
| ** Returns: true, if numerical digit |
| ** |
| *******************************************************************************/ |
| static inline bool isDigit(char c, int base) |
| { |
| if (base == 10) { |
| return isdigit(c); |
| } else if (base == 16) { |
| return isxdigit(c); |
| } else { |
| return false; |
| } |
| } |
| |
| static inline bool isArrayDelimeter(char c) |
| { |
| return (isspace(c) || c== ',' || c == ':' || c == '-' || c == '}'); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: getDigitValue() |
| ** |
| ** Description: return numerical value of a decimal or hex char |
| ** |
| ** Returns: numerical value if decimal or hex char, otherwise 0 |
| ** |
| *******************************************************************************/ |
| inline int getDigitValue(char c, int base) |
| { |
| if ('0' <= c && c <= '9') |
| return c - '0'; |
| if (base == 16) |
| { |
| if ('A' <= c && c <= 'F') |
| return c - 'A' + 10; |
| else if ('a' <= c && c <= 'f') |
| return c - 'a' + 10; |
| } |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: CUwbNxpConfig::readConfig() |
| ** |
| ** Description: read Config settings and parse them into a linked list |
| ** move the element from linked list to a array at the end |
| ** |
| ** Returns: 1, if there are any config data, 0 otherwise |
| ** |
| *******************************************************************************/ |
| bool CUwbNxpConfig::readConfig() |
| { |
| enum { |
| BEGIN_LINE = 1, |
| TOKEN, |
| STR_VALUE, |
| NUM_VALUE, |
| ARR_SPACE, |
| ARR_STR, |
| ARR_STR_SPACE, |
| ARR_NUM, |
| BEGIN_HEX, |
| BEGIN_QUOTE, |
| END_LINE |
| }; |
| |
| FILE* fd; |
| string token; |
| string strValue; |
| unsigned long numValue = 0; |
| vector<uint8_t> arrValue; |
| vector<string> arrStr; |
| int base = 0; |
| int c; |
| const char *name = mCurrentFile.c_str(); |
| unsigned long state = BEGIN_LINE; |
| |
| mValidFile = false; |
| m_map.clear(); |
| |
| /* open config file, read it into a buffer */ |
| if ((fd = fopen(name, "r")) == NULL) |
| { |
| ALOGD_IF(uwb_debug_enabled, "%s Cannot open config file %s\n", __func__, name); |
| return false; |
| } |
| ALOGD_IF(uwb_debug_enabled, "%s Opened config %s\n", __func__, name); |
| |
| for (;;) { |
| c = fgetc(fd); |
| |
| switch (state) { |
| case BEGIN_LINE: |
| if (isPrintable(c)) { |
| token.clear(); |
| numValue = 0; |
| strValue.clear(); |
| arrValue.clear(); |
| arrStr.clear(); |
| state = TOKEN; |
| token.push_back(c); |
| } else { |
| state = END_LINE; |
| } |
| break; |
| case TOKEN: |
| if (c == '=') { |
| state = BEGIN_QUOTE; |
| } else if (isPrintable(c)) { |
| token.push_back(c); |
| } else { |
| state = END_LINE; |
| } |
| break; |
| case BEGIN_QUOTE: |
| if (c == '"') { |
| state = STR_VALUE; |
| base = 0; |
| } else if (c == '0') { |
| state = BEGIN_HEX; |
| } else if (isDigit(c, 10)) { |
| state = NUM_VALUE; |
| base = 10; |
| numValue = getDigitValue(c, base); |
| } else if (c == '{') { |
| state = ARR_SPACE; |
| base = 16; |
| } else { |
| state = END_LINE; |
| } |
| break; |
| case BEGIN_HEX: |
| if (c == 'x' || c == 'X') { |
| state = NUM_VALUE; |
| base = 16; |
| numValue = 0; |
| } else if (isDigit(c, 10)) { |
| state = NUM_VALUE; |
| base = 10; |
| numValue = getDigitValue(c, base); |
| } else { |
| m_map.try_emplace(token, move(uwbParam(numValue))); |
| state = END_LINE; |
| } |
| break; |
| case NUM_VALUE: |
| if (isDigit(c, base)) { |
| numValue *= base; |
| numValue += getDigitValue(c, base); |
| } else {m_map.try_emplace(token, move(uwbParam(numValue))); |
| state = END_LINE; |
| } |
| break; |
| case ARR_SPACE: |
| if (isDigit(c, 16)) { |
| numValue = getDigitValue(c, base); |
| state = ARR_NUM; |
| } else if (c == '}') { |
| m_map.try_emplace(token, move(uwbParam(move(arrValue)))); |
| state = END_LINE; |
| } else if (c == '"') { |
| state = ARR_STR; |
| } else if (c == EOF) { |
| state = END_LINE; |
| } |
| break; |
| case ARR_STR: |
| if (c == '"') { |
| arrStr.emplace_back(move(strValue)); |
| strValue.clear(); |
| state = ARR_STR_SPACE; |
| } else { |
| strValue.push_back(c); |
| } |
| break; |
| case ARR_STR_SPACE: |
| if (c == '}') { |
| m_map.try_emplace(token, move(uwbParam(move(arrStr)))); |
| state = END_LINE; |
| } else if (c == '"') { |
| state = ARR_STR; |
| } |
| break; |
| case ARR_NUM: |
| if (isDigit(c, 16)) { |
| numValue *= 16; |
| numValue += getDigitValue(c, base); |
| } else if (isArrayDelimeter(c)) { |
| arrValue.push_back(numValue & 0xff); |
| state = ARR_SPACE; |
| } else { |
| state = END_LINE; |
| } |
| if (c == '}') { |
| m_map.try_emplace(token, move(uwbParam(move(arrValue)))); |
| state = END_LINE; |
| } |
| break; |
| case STR_VALUE: |
| if (c == '"') { |
| state = END_LINE; |
| m_map.try_emplace(token, move(uwbParam(strValue))); |
| } else { |
| strValue.push_back(c); |
| } |
| break; |
| case END_LINE: |
| // do nothing |
| default: |
| break; |
| } |
| if (c == EOF) |
| break; |
| else if (state == END_LINE && (c == '\n' || c == '\r')) |
| state = BEGIN_LINE; |
| else if (c == '#') |
| state = END_LINE; |
| } |
| |
| fclose(fd); |
| |
| if (m_map.size() > 0) { |
| mValidFile = true; |
| } |
| |
| return mValidFile; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: CUwbNxpConfig::CUwbNxpConfig() |
| ** |
| ** Description: class constructor |
| ** |
| ** Returns: none |
| ** |
| *******************************************************************************/ |
| CUwbNxpConfig::CUwbNxpConfig() : |
| mValidFile(false), |
| mCountrySpecific(false) |
| { |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: CUwbNxpConfig::~CUwbNxpConfig() |
| ** |
| ** Description: class destructor |
| ** |
| ** Returns: none |
| ** |
| *******************************************************************************/ |
| CUwbNxpConfig::~CUwbNxpConfig() |
| { |
| } |
| |
| CUwbNxpConfig::CUwbNxpConfig(const char *filepath) : |
| mValidFile(false), |
| mFilePath(filepath), |
| mCountrySpecific(false) |
| { |
| auto pos = mFilePath.find(country_code_specifier); |
| if (pos == string::npos) { |
| mCurrentFile = mFilePath; |
| readConfig(); |
| } else { |
| mCountrySpecific = true; |
| } |
| } |
| |
| CUwbNxpConfig::CUwbNxpConfig(CUwbNxpConfig&& config) |
| { |
| m_map = move(config.m_map); |
| mValidFile = config.mValidFile; |
| mFilePath = move(config.mFilePath); |
| mCurrentFile = move(config.mCurrentFile); |
| mCountrySpecific = config.mCountrySpecific; |
| |
| config.mValidFile = false; |
| } |
| |
| CUwbNxpConfig& CUwbNxpConfig::operator=(CUwbNxpConfig&& config) |
| { |
| m_map = move(config.m_map); |
| mValidFile = config.mValidFile; |
| mFilePath = move(config.mFilePath); |
| mCurrentFile = move(config.mCurrentFile); |
| mCountrySpecific = config.mCountrySpecific; |
| |
| config.mValidFile = false; |
| return *this; |
| } |
| |
| void CUwbNxpConfig::setCountry(const string& strCountry) |
| { |
| if (!isCountrySpecific()) |
| return; |
| |
| mCurrentFile = mFilePath; |
| auto pos = mCurrentFile.find(country_code_specifier); |
| if (pos == string::npos) { |
| return; |
| } |
| |
| mCurrentFile.replace(pos, strlen(country_code_specifier), strCountry); |
| readConfig(); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: CUwbNxpConfig::find() |
| ** |
| ** Description: search if a setting exist in the setting array |
| ** |
| ** Returns: pointer to the setting object |
| ** |
| *******************************************************************************/ |
| const uwbParam* CUwbNxpConfig::find(const char* p_name) const |
| { |
| const auto it = m_map.find(p_name); |
| |
| if (it == m_map.cend()) { |
| return NULL; |
| } |
| return &it->second; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: CUwbNxpConfig::dump() |
| ** |
| ** Description: prints all elements in the list |
| ** |
| ** Returns: none |
| ** |
| *******************************************************************************/ |
| void CUwbNxpConfig::dump() const |
| { |
| ALOGD("Dump configuration file %s : %s, %zu entries", mCurrentFile.c_str(), |
| mValidFile ? "valid" : "invalid", m_map.size()); |
| |
| for (auto &it : m_map) { |
| auto &key = it.first; |
| auto ¶m = it.second; |
| param.dump(key); |
| } |
| } |
| |
| /*******************************************************************************/ |
| uwbParam::uwbParam() : |
| m_numValue(0), |
| m_type(type::NUMBER) |
| { |
| } |
| |
| uwbParam::~uwbParam() |
| { |
| } |
| |
| uwbParam::uwbParam(const uwbParam ¶m) : |
| m_numValue(param.m_numValue), |
| m_str_value(param.m_str_value), |
| m_arrValue(param.m_arrValue), |
| m_arrStrValue(param.m_arrStrValue), |
| m_type(param.m_type) |
| { |
| ALOGD_IF(uwb_debug_enabled, "uwbParam copy-constructor"); |
| } |
| |
| uwbParam::uwbParam(uwbParam &¶m) : |
| m_numValue(param.m_numValue), |
| m_str_value(move(param.m_str_value)), |
| m_arrValue(move(param.m_arrValue)), |
| m_arrStrValue(move(param.m_arrStrValue)), |
| m_type(param.m_type) |
| { |
| } |
| |
| uwbParam::uwbParam(const string& value) : |
| m_numValue(0), |
| m_str_value(value), |
| m_type(type::STRING) |
| { |
| } |
| |
| uwbParam::uwbParam(unsigned long value) : |
| m_numValue(value), |
| m_type(type::NUMBER) |
| { |
| } |
| |
| uwbParam::uwbParam(vector<uint8_t> &&value) : |
| m_arrValue(move(value)), |
| m_type(type::BYTEARRAY) |
| { |
| } |
| |
| uwbParam::uwbParam(vector<string> &&value) : |
| m_arrStrValue(move(value)), |
| m_type(type::STRINGARRAY) |
| { |
| } |
| |
| |
| void uwbParam::dump(const string &tag) const |
| { |
| if (m_type == type::NUMBER) { |
| ALOGD(" - %s = 0x%lx", tag.c_str(), m_numValue); |
| } else if (m_type == type::STRING) { |
| ALOGD(" - %s = %s", tag.c_str(), m_str_value.c_str()); |
| } else if (m_type == type::BYTEARRAY) { |
| stringstream ss_hex; |
| ss_hex.fill('0'); |
| for (auto b : m_arrValue) { |
| ss_hex << setw(2) << hex << (int)b << " "; |
| } |
| ALOGD(" - %s = { %s}", tag.c_str(), ss_hex.str().c_str()); |
| } else if (m_type == type::STRINGARRAY) { |
| stringstream ss; |
| for (auto s : m_arrStrValue) { |
| ss << "\"" << s << "\", "; |
| } |
| ALOGD(" - %s = { %s}", tag.c_str(), ss.str().c_str()); |
| } |
| } |
| /*******************************************************************************/ |
| class RegionCodeMap { |
| public: |
| void loadMapping(const char *filepath) { |
| CUwbNxpConfig config(filepath); |
| if (!config.isValid()) { |
| ALOGW("Region mapping was not provided."); |
| return; |
| } |
| |
| ALOGI("Region mapping was provided by %s", filepath); |
| auto &all_params = config.get_data(); |
| for (auto &it : all_params) { |
| const auto ®ion_str = it.first; |
| const uwbParam *param = &it.second; |
| |
| // split space-separated strings into set |
| stringstream ss(param->str_value()); |
| string cc; |
| unordered_set<string> cc_set; |
| while (ss >> cc) { |
| if (cc.length() == 2 && isupper(cc[0]) && isupper(cc[1])) { |
| cc_set.emplace(move(cc)); |
| } |
| } |
| auto result = m_map.try_emplace(region_str, move(cc_set)); |
| if (!result.second) { |
| // region conlifct : merge |
| result.first->second.merge(move(cc_set)); |
| } |
| } |
| m_config = move(config); |
| } |
| string xlateCountryCode(const char country_code[2]) { |
| string code{country_code[0], country_code[1]}; |
| if (m_config.isValid()) { |
| for (auto &it : m_map) { |
| const auto ®ion_str = it.first; |
| const auto &cc_set = it.second; |
| if (cc_set.find(code) != cc_set.end()) { |
| ALOGD_IF(uwb_debug_enabled, "map country code %c%c --> %s", |
| country_code[0], country_code[1], region_str.c_str()); |
| return region_str; |
| } |
| } |
| } |
| return code; |
| } |
| void dump() { |
| ALOGD("Region mapping dump:"); |
| for (auto &entry : m_map) { |
| const auto ®ion_str = entry.first; |
| const auto &cc_set = entry.second; |
| stringstream ss; |
| for (const auto s : cc_set) { |
| ss << "\"" << s << "\", "; |
| } |
| ALOGD("- %s = { %s}", region_str.c_str(), ss.str().c_str()); |
| } |
| } |
| private: |
| CUwbNxpConfig m_config; |
| unordered_map<string, unordered_set<string>> m_map; |
| }; |
| |
| /*******************************************************************************/ |
| class CascadeConfig { |
| public: |
| CascadeConfig(); |
| |
| void init(const char *main_config); |
| void setCountryCode(const char country_code[2]); |
| |
| const uwbParam* find(const char *name) const; |
| bool getValue(const char* name, char* pValue, size_t len) const; |
| bool getValue(const char* name, unsigned long& rValue) const; |
| bool getValue(const char* name, uint8_t* pValue, long len, long* readlen) const; |
| private: |
| // default_nxp_config_path |
| CUwbNxpConfig mMainConfig; |
| |
| // EXTRA_CONF_PATH[N] |
| vector<CUwbNxpConfig> mExtraConfig; |
| |
| // [COUNTRY_CODE_CAP_FILE_LOCATION]/country_code_config_name |
| CUwbNxpConfig mCapsConfig; |
| |
| // Region Code mapping |
| RegionCodeMap mRegionMap; |
| }; |
| |
| CascadeConfig::CascadeConfig() |
| { |
| } |
| |
| void CascadeConfig::init(const char *main_config) |
| { |
| ALOGD("CascadeConfig initialize with %s", main_config); |
| |
| // Main config file |
| CUwbNxpConfig config(main_config); |
| if (!config.isValid()) { |
| ALOGW("Failed to load main config file"); |
| return; |
| } |
| mMainConfig = move(config); |
| |
| // Read EXTRA_CONF_PATH[N] |
| for (int i = 1; i <= 10; i++) { |
| char key[32]; |
| snprintf(key, sizeof(key), "EXTRA_CONF_PATH_%d", i); |
| const uwbParam *param = mMainConfig.find(key); |
| if (!param) |
| continue; |
| CUwbNxpConfig config(param->str_value()); |
| ALOGI("Extra calibration file %s : %svalid", param->str_value(), config.isValid() ? "" : "in"); |
| if (config.isValid() || config.isCountrySpecific()) { |
| mExtraConfig.emplace_back(move(config)); |
| } |
| } |
| |
| // Pick one libuwb-countrycode.conf with the highest VERSION number |
| // from multiple directories specified by COUNTRY_CODE_CAP_FILE_LOCATION |
| unsigned long arrLen = 0; |
| if (NxpConfig_GetStrArrayLen(NAME_COUNTRY_CODE_CAP_FILE_LOCATION, &arrLen) && arrLen > 0) { |
| const long loc_max_len = 260; |
| auto loc = make_unique<char[]>(loc_max_len); |
| int version, max_version = -1; |
| string strPickedPath; |
| bool foundCapFile = false; |
| CUwbNxpConfig pickedConfig; |
| |
| for (int i = 0; i < arrLen; i++) { |
| if (!NxpConfig_GetStrArrayVal(NAME_COUNTRY_CODE_CAP_FILE_LOCATION, i, loc.get(), loc_max_len)) { |
| continue; |
| } |
| string strPath(loc.get()); |
| strPath += country_code_config_name; |
| |
| ALOGD_IF(uwb_debug_enabled, "Try to load %s", strPath.c_str()); |
| |
| CUwbNxpConfig config(strPath.c_str()); |
| |
| const uwbParam *param = config.find(NAME_NXP_COUNTRY_CODE_VERSION); |
| version = param ? atoi(param->str_value()) : -2; |
| if (version > max_version) { |
| foundCapFile = true; |
| pickedConfig = move(config); |
| strPickedPath = strPath; |
| max_version = version; |
| } |
| } |
| if (foundCapFile) { |
| mCapsConfig = move(pickedConfig); |
| ALOGI("CountryCodeCaps file %s loaded with VERSION=%d", strPickedPath.c_str(), max_version); |
| } else { |
| ALOGI("No CountryCodeCaps specified"); |
| } |
| } else { |
| ALOGI(NAME_COUNTRY_CODE_CAP_FILE_LOCATION " was not specified, skip loading CountryCodeCaps"); |
| } |
| |
| // Load region mapping |
| const uwbParam *param = find(NAME_REGION_MAP_PATH); |
| if (param) { |
| mRegionMap.loadMapping(param->str_value()); |
| } |
| |
| if (uwb_debug_enabled) { |
| ALOGD("CascadeConfig initialized"); |
| |
| mMainConfig.dump(); |
| |
| for (const auto &config : mExtraConfig) |
| config.dump(); |
| |
| mCapsConfig.dump(); |
| |
| mRegionMap.dump(); |
| } |
| } |
| |
| extern bool isCountryCodeMapCreated; |
| void CascadeConfig::setCountryCode(const char country_code[2]) |
| { |
| string strCountry = mRegionMap.xlateCountryCode(country_code); |
| |
| ALOGD_IF(uwb_debug_enabled, "Apply country code %c%c --> %s\n", country_code[0], country_code[1], strCountry.c_str()); |
| for (auto &x : mExtraConfig) { |
| if (x.isCountrySpecific()) { |
| x.setCountry(strCountry); |
| x.dump(); |
| } |
| } |
| |
| // Load 'COUNTRY_CODE_CAPS' and apply it to 'conf_map' |
| auto cc_data = make_unique<uint8_t[]>(UCI_MAX_DATA_LEN); |
| const uwbParam *param = mCapsConfig.find(NAME_NXP_UWB_COUNTRY_CODE_CAPS); |
| if (param) { |
| uint32_t retlen = param->arr_len(); |
| phNxpUciHal_getCountryCaps(param->arr_value(), country_code, cc_data.get(), &retlen); |
| if (get_conf_map(cc_data.get(), retlen)) { |
| isCountryCodeMapCreated = true; |
| NXPLOG_UCIHAL_D("Country code caps loaded"); |
| } |
| } |
| } |
| |
| const uwbParam* CascadeConfig::find(const char *name) const |
| { |
| const uwbParam* param = NULL; |
| for (auto it = mExtraConfig.rbegin(); it != mExtraConfig.rend(); it++) { |
| param = it->find(name); |
| if (param) |
| break; |
| } |
| if (!param) { |
| param = mMainConfig.find(name); |
| } |
| return param; |
| } |
| |
| // TODO: move these getValue() helpers out of the class |
| bool CascadeConfig::getValue(const char* name, char* pValue, size_t len) const |
| { |
| const uwbParam *param = find(name); |
| if (!param) |
| return false; |
| if (param->getType() != uwbParam::type::STRING) |
| return false; |
| if (len < (param->str_len() + 1)) |
| return false; |
| |
| strncpy(pValue, param->str_value(), len); |
| return true; |
| } |
| |
| bool CascadeConfig::getValue(const char* name, uint8_t* pValue, long len, long* readlen) const |
| { |
| const uwbParam *param = find(name); |
| if (!param) |
| return false; |
| if (param->getType() != uwbParam::type::BYTEARRAY) |
| return false; |
| if (len < param->arr_len()) |
| return false; |
| memcpy(pValue, param->arr_value(), param->arr_len()); |
| if (readlen) |
| *readlen = param->arr_len(); |
| return true; |
| } |
| |
| bool CascadeConfig::getValue(const char* name, unsigned long& rValue) const |
| { |
| const uwbParam *param = find(name); |
| if (!param) |
| return false; |
| if (param->getType() != uwbParam::type::NUMBER) |
| return false; |
| |
| rValue = param->numValue(); |
| return true; |
| } |
| |
| /*******************************************************************************/ |
| |
| static CascadeConfig gConfig; |
| |
| extern "C" void NxpConfig_Init(void) |
| { |
| gConfig.init(default_nxp_config_path); |
| } |
| |
| extern "C" void NxpConfig_SetCountryCode(const char country_code[2]) |
| { |
| gConfig.setCountryCode(country_code); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: NxpConfig_GetStr |
| ** |
| ** Description: API function for getting a string value of a setting |
| ** |
| ** Returns: True if found, otherwise False. |
| ** |
| *******************************************************************************/ |
| extern "C" int NxpConfig_GetStr(const char* name, char* pValue, unsigned long len) |
| { |
| return gConfig.getValue(name, pValue, len); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: NxpConfig_GetByteArray() |
| ** |
| ** Description: Read byte array value from the config file. |
| ** |
| ** Parameters: |
| ** name - name of the config param to read. |
| ** pValue - pointer to input buffer. |
| ** bufflen - input buffer length. |
| ** len - out parameter to return the number of bytes read from config file, |
| ** return -1 in case bufflen is not enough. |
| ** |
| ** Returns: TRUE[1] if config param name is found in the config file, else FALSE[0] |
| ** |
| *******************************************************************************/ |
| extern "C" int NxpConfig_GetByteArray(const char* name, uint8_t* pValue, long bufflen, long *len) |
| { |
| return gConfig.getValue(name, pValue, bufflen,len); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function: NxpConfig_GetNum |
| ** |
| ** Description: API function for getting a numerical value of a setting |
| ** |
| ** Returns: true, if successful |
| ** |
| *******************************************************************************/ |
| extern "C" int NxpConfig_GetNum(const char* name, void* pValue, unsigned long len) |
| { |
| if (pValue == NULL){ |
| return false; |
| } |
| const uwbParam* pParam = gConfig.find(name); |
| |
| if (pParam == NULL) |
| return false; |
| if (pParam->getType() != uwbParam::type::NUMBER) |
| return false; |
| |
| unsigned long v = pParam->numValue(); |
| switch (len) |
| { |
| case sizeof(unsigned long): |
| *(static_cast<unsigned long*>(pValue)) = (unsigned long)v; |
| break; |
| case sizeof(unsigned short): |
| *(static_cast<unsigned short*>(pValue)) = (unsigned short)v; |
| break; |
| case sizeof(unsigned char): |
| *(static_cast<unsigned char*> (pValue)) = (unsigned char)v; |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| // Get the length of a 'string-array' type parameter |
| int NxpConfig_GetStrArrayLen(const char* name, unsigned long* pLen) |
| { |
| const uwbParam* param = gConfig.find(name); |
| if (!param || param->getType() != uwbParam::type::STRINGARRAY) |
| return false; |
| |
| *pLen = param->str_arr_len(); |
| return true; |
| } |
| |
| // Get a string value from 'string-array' type parameters, index zero-based |
| int NxpConfig_GetStrArrayVal(const char* name, int index, char* pValue, unsigned long len) |
| { |
| const uwbParam* param = gConfig.find(name); |
| if (!param || param->getType() != uwbParam::type::STRINGARRAY) |
| return false; |
| if (index < 0 || index >= param->str_arr_len()) |
| return false; |
| |
| if (len < param->str_arr_elem_len(index) + 1) |
| return false; |
| strncpy(pValue, param->str_arr_elem(index), len); |
| return true; |
| } |