1 /******************************************************************************
2 *
3 * Copyright (C) 2011-2012 Broadcom Corporation
4 * Copyright 2018-2019, 2023-2024 NXP
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 ******************************************************************************/
19 //#define LOG_NDEBUG 0
20 #define LOG_TAG "NxpUwbConf"
21
22 #include <limits.h>
23 #include <sys/stat.h>
24
25 #include <cstddef>
26 #include <cstdint>
27 #include <iomanip>
28 #include <list>
29 #include <memory>
30 #include <sstream>
31 #include <sstream>
32 #include <string>
33 #include <unordered_map>
34 #include <unordered_set>
35 #include <vector>
36
37 #include <android-base/logging.h>
38 #include <cutils/properties.h>
39 #include <log/log.h>
40
41 #include "phNxpConfig.h"
42 #include "phNxpUciHal.h"
43 #include "phNxpUciHal_ext.h"
44 #include "phNxpUciHal_utils.h"
45 #include "phNxpLog.h"
46
47 namespace {
48
49 static const char default_nxp_config_path[] = "/vendor/etc/libuwb-nxp.conf";
50 static const char country_code_config_name[] = "libuwb-countrycode.conf";
51 static const char nxp_uci_config_file[] = "libuwb-uci.conf";
52 static const char default_uci_config_path[] = "/vendor/etc/";
53
54 static const char country_code_specifier[] = "<country>";
55 static const char sku_specifier[] = "<sku>";
56 static const char extid_specifier[] = "<extid>";
57 static const char revision_specifier[] = "<revision>";
58
59 static const char extid_config_name[] = "cal.extid";
60 static const char extid_default_value[] = "defaultextid";
61
62 static const char prop_name_calsku[] = "persist.vendor.uwb.cal.sku";
63 static const char prop_default_calsku[] = "defaultsku";
64
65 static const char prop_name_revision[] = "persist.vendor.uwb.cal.revision";
66 static const char prop_default_revision[] = "defaultrevision";
67
68 using namespace::std;
69
70 class uwbParam
71 {
72 public:
73 enum class type { STRING, NUMBER, BYTEARRAY, STRINGARRAY };
74 uwbParam();
75 uwbParam(const uwbParam& param);
76 uwbParam(uwbParam&& param);
77
78 uwbParam(const string& value);
79 uwbParam(vector<uint8_t>&& value);
80 uwbParam(unsigned long value);
81 uwbParam(vector<string>&& value);
82
83 virtual ~uwbParam();
84
getType() const85 type getType() const { return m_type; }
numValue() const86 unsigned long numValue() const {return m_numValue;}
str_value() const87 const char* str_value() const {return m_str_value.c_str();}
str_len() const88 size_t str_len() const {return m_str_value.length();}
arr_value() const89 const uint8_t* arr_value() const { return m_arrValue.data(); }
arr_len() const90 size_t arr_len() const { return m_arrValue.size(); }
91
str_arr_len() const92 size_t str_arr_len() const { return m_arrStrValue.size(); }
str_arr_elem(const int index) const93 const char* str_arr_elem(const int index) const { return m_arrStrValue[index].c_str(); }
str_arr_elem_len(const int index) const94 size_t str_arr_elem_len(const int index) const { return m_arrStrValue[index].length(); }
95
96 void dump(const string &tag) const;
97 private:
98 // TODO: use uint64_t or uint32_t instead of unsigned long.
99 unsigned long m_numValue;
100 string m_str_value;
101 vector<uint8_t> m_arrValue;
102 vector<string> m_arrStrValue;
103 type m_type;
104 };
105
106 class CUwbNxpConfig
107 {
108 public:
109 CUwbNxpConfig();
110 CUwbNxpConfig(CUwbNxpConfig&& config);
111 CUwbNxpConfig(const char *filepath);
112 virtual ~CUwbNxpConfig();
113 CUwbNxpConfig& operator=(CUwbNxpConfig&& config);
114
115 bool open(const char *filepath);
isValid() const116 bool isValid() const { return mValidFile; }
reset()117 void reset() {
118 m_map.clear();
119 mValidFile = false;
120 }
121
122 const uwbParam* find(const char* p_name) const;
123 void setCountry(const string& strCountry);
getFilePath() const124 const char* getFilePath() const {
125 return mFilePath.c_str();
126 }
127
128 void dump() const;
129
get_data() const130 const unordered_map<string, uwbParam>& get_data() const {
131 return m_map;
132 }
133 private:
134 bool readConfig();
135
136 unordered_map<string, uwbParam> m_map;
137 bool mValidFile;
138 string mFilePath;
139 };
140
141 /*******************************************************************************
142 **
143 ** Function: isPrintable()
144 **
145 ** Description: determine if 'c' is printable
146 **
147 ** Returns: 1, if printable, otherwise 0
148 **
149 *******************************************************************************/
isPrintable(char c)150 static inline bool isPrintable(char c)
151 {
152 return (c >= 'A' && c <= 'Z') ||
153 (c >= 'a' && c <= 'z') ||
154 (c >= '0' && c <= '9') ||
155 c == '/' || c == '_' || c == '-' || c == '.' || c == ',';
156 }
157
158 /*******************************************************************************
159 **
160 ** Function: isDigit()
161 **
162 ** Description: determine if 'c' is numeral digit
163 **
164 ** Returns: true, if numerical digit
165 **
166 *******************************************************************************/
isDigit(char c,int base)167 static inline bool isDigit(char c, int base)
168 {
169 if (base == 10) {
170 return isdigit(c);
171 } else if (base == 16) {
172 return isxdigit(c);
173 } else {
174 return false;
175 }
176 }
177
isArrayDelimeter(char c)178 static inline bool isArrayDelimeter(char c)
179 {
180 return (isspace(c) || c== ',' || c == ':' || c == '-' || c == '}');
181 }
182
183 /*******************************************************************************
184 **
185 ** Function: getDigitValue()
186 **
187 ** Description: return numerical value of a decimal or hex char
188 **
189 ** Returns: numerical value if decimal or hex char, otherwise 0
190 **
191 *******************************************************************************/
getDigitValue(char c,int base)192 inline int getDigitValue(char c, int base)
193 {
194 if ('0' <= c && c <= '9')
195 return c - '0';
196 if (base == 16)
197 {
198 if ('A' <= c && c <= 'F')
199 return c - 'A' + 10;
200 else if ('a' <= c && c <= 'f')
201 return c - 'a' + 10;
202 }
203 return 0;
204 }
205
206 /*******************************************************************************
207 **
208 ** Function: CUwbNxpConfig::readConfig()
209 **
210 ** Description: read Config settings and parse them into a linked list
211 ** move the element from linked list to a array at the end
212 **
213 ** Returns: 1, if there are any config data, 0 otherwise
214 **
215 *******************************************************************************/
readConfig()216 bool CUwbNxpConfig::readConfig()
217 {
218 enum {
219 BEGIN_LINE = 1,
220 TOKEN,
221 STR_VALUE,
222 NUM_VALUE,
223 ARR_SPACE,
224 ARR_STR,
225 ARR_STR_SPACE,
226 ARR_NUM,
227 BEGIN_HEX,
228 BEGIN_QUOTE,
229 END_LINE
230 };
231
232 FILE* fd;
233 string token;
234 string strValue;
235 unsigned long numValue = 0;
236 vector<uint8_t> arrValue;
237 vector<string> arrStr;
238 int base = 0;
239 int c;
240 const char *name = mFilePath.c_str();
241 unsigned long state = BEGIN_LINE;
242
243 mValidFile = false;
244 m_map.clear();
245
246 /* open config file, read it into a buffer */
247 if ((fd = fopen(name, "r")) == NULL)
248 {
249 ALOGV("Extra calibration file %s failed to open.", name);
250 return false;
251 }
252 ALOGV("%s Opened config %s\n", __func__, name);
253
254 for (;;) {
255 c = fgetc(fd);
256
257 switch (state) {
258 case BEGIN_LINE:
259 if (isPrintable(c)) {
260 token.clear();
261 numValue = 0;
262 strValue.clear();
263 arrValue.clear();
264 arrStr.clear();
265 state = TOKEN;
266 token.push_back(c);
267 } else {
268 state = END_LINE;
269 }
270 break;
271 case TOKEN:
272 if (c == '=') {
273 state = BEGIN_QUOTE;
274 } else if (isPrintable(c)) {
275 token.push_back(c);
276 } else {
277 state = END_LINE;
278 }
279 break;
280 case BEGIN_QUOTE:
281 if (c == '"') {
282 state = STR_VALUE;
283 base = 0;
284 } else if (c == '0') {
285 state = BEGIN_HEX;
286 } else if (isDigit(c, 10)) {
287 state = NUM_VALUE;
288 base = 10;
289 numValue = getDigitValue(c, base);
290 } else if (c == '{') {
291 state = ARR_SPACE;
292 base = 16;
293 } else {
294 state = END_LINE;
295 }
296 break;
297 case BEGIN_HEX:
298 if (c == 'x' || c == 'X') {
299 state = NUM_VALUE;
300 base = 16;
301 numValue = 0;
302 } else if (isDigit(c, 10)) {
303 state = NUM_VALUE;
304 base = 10;
305 numValue = getDigitValue(c, base);
306 } else {
307 m_map.try_emplace(token, move(uwbParam(numValue)));
308 state = END_LINE;
309 }
310 break;
311 case NUM_VALUE:
312 if (isDigit(c, base)) {
313 numValue *= base;
314 numValue += getDigitValue(c, base);
315 } else {m_map.try_emplace(token, move(uwbParam(numValue)));
316 state = END_LINE;
317 }
318 break;
319 case ARR_SPACE:
320 if (isDigit(c, 16)) {
321 numValue = getDigitValue(c, base);
322 state = ARR_NUM;
323 } else if (c == '}') {
324 m_map.try_emplace(token, move(uwbParam(move(arrValue))));
325 state = END_LINE;
326 } else if (c == '"') {
327 state = ARR_STR;
328 } else if (c == EOF) {
329 state = END_LINE;
330 }
331 break;
332 case ARR_STR:
333 if (c == '"') {
334 arrStr.emplace_back(move(strValue));
335 strValue.clear();
336 state = ARR_STR_SPACE;
337 } else {
338 strValue.push_back(c);
339 }
340 break;
341 case ARR_STR_SPACE:
342 if (c == '}') {
343 m_map.try_emplace(token, move(uwbParam(move(arrStr))));
344 state = END_LINE;
345 } else if (c == '"') {
346 state = ARR_STR;
347 }
348 break;
349 case ARR_NUM:
350 if (isDigit(c, 16)) {
351 numValue *= 16;
352 numValue += getDigitValue(c, base);
353 } else if (isArrayDelimeter(c)) {
354 arrValue.push_back(numValue & 0xff);
355 state = ARR_SPACE;
356 } else {
357 state = END_LINE;
358 }
359 if (c == '}') {
360 m_map.try_emplace(token, move(uwbParam(move(arrValue))));
361 state = END_LINE;
362 }
363 break;
364 case STR_VALUE:
365 if (c == '"') {
366 state = END_LINE;
367 m_map.try_emplace(token, move(uwbParam(strValue)));
368 } else {
369 strValue.push_back(c);
370 }
371 break;
372 case END_LINE:
373 // do nothing
374 default:
375 break;
376 }
377 if (c == EOF)
378 break;
379 else if (state == END_LINE && (c == '\n' || c == '\r'))
380 state = BEGIN_LINE;
381 else if (c == '#')
382 state = END_LINE;
383 }
384
385 if (fclose(fd) != 0) {
386 ALOGE("[%s] fclose failed", __func__);
387 }
388
389 if (m_map.size() > 0) {
390 mValidFile = true;
391 ALOGI("Extra calibration file %s opened.", name);
392 }
393
394 return mValidFile;
395 }
396
397 /*******************************************************************************
398 **
399 ** Function: CUwbNxpConfig::CUwbNxpConfig()
400 **
401 ** Description: class constructor
402 **
403 ** Returns: none
404 **
405 *******************************************************************************/
CUwbNxpConfig()406 CUwbNxpConfig::CUwbNxpConfig() :
407 mValidFile(false)
408 {
409 }
410
411 /*******************************************************************************
412 **
413 ** Function: CUwbNxpConfig::~CUwbNxpConfig()
414 **
415 ** Description: class destructor
416 **
417 ** Returns: none
418 **
419 *******************************************************************************/
~CUwbNxpConfig()420 CUwbNxpConfig::~CUwbNxpConfig()
421 {
422 }
423
CUwbNxpConfig(const char * filepath)424 CUwbNxpConfig::CUwbNxpConfig(const char *filepath)
425 {
426 open(filepath);
427 }
428
CUwbNxpConfig(CUwbNxpConfig && config)429 CUwbNxpConfig::CUwbNxpConfig(CUwbNxpConfig&& config)
430 {
431 m_map = move(config.m_map);
432 mValidFile = config.mValidFile;
433 mFilePath = move(config.mFilePath);
434
435 config.mValidFile = false;
436 }
437
operator =(CUwbNxpConfig && config)438 CUwbNxpConfig& CUwbNxpConfig::operator=(CUwbNxpConfig&& config)
439 {
440 m_map = move(config.m_map);
441 mValidFile = config.mValidFile;
442 mFilePath = move(config.mFilePath);
443
444 config.mValidFile = false;
445 return *this;
446 }
447
open(const char * filepath)448 bool CUwbNxpConfig::open(const char *filepath)
449 {
450 mValidFile = false;
451 mFilePath = filepath;
452
453 return readConfig();
454 }
455
456 /*******************************************************************************
457 **
458 ** Function: CUwbNxpConfig::find()
459 **
460 ** Description: search if a setting exist in the setting array
461 **
462 ** Returns: pointer to the setting object
463 **
464 *******************************************************************************/
find(const char * p_name) const465 const uwbParam* CUwbNxpConfig::find(const char* p_name) const
466 {
467 const auto it = m_map.find(p_name);
468
469 if (it == m_map.cend()) {
470 return NULL;
471 }
472 return &it->second;
473 }
474
475 /*******************************************************************************
476 **
477 ** Function: CUwbNxpConfig::dump()
478 **
479 ** Description: prints all elements in the list
480 **
481 ** Returns: none
482 **
483 *******************************************************************************/
dump() const484 void CUwbNxpConfig::dump() const
485 {
486 ALOGV("Dump configuration file %s : %s, %zu entries", mFilePath.c_str(),
487 mValidFile ? "valid" : "invalid", m_map.size());
488
489 for (auto &it : m_map) {
490 auto &key = it.first;
491 auto ¶m = it.second;
492 param.dump(key);
493 }
494 }
495
496 /*******************************************************************************/
uwbParam()497 uwbParam::uwbParam() :
498 m_numValue(0),
499 m_type(type::NUMBER)
500 {
501 }
502
~uwbParam()503 uwbParam::~uwbParam()
504 {
505 }
506
uwbParam(const uwbParam & param)507 uwbParam::uwbParam(const uwbParam ¶m) :
508 m_numValue(param.m_numValue),
509 m_str_value(param.m_str_value),
510 m_arrValue(param.m_arrValue),
511 m_arrStrValue(param.m_arrStrValue),
512 m_type(param.m_type)
513 {
514 }
515
uwbParam(uwbParam && param)516 uwbParam::uwbParam(uwbParam &¶m) :
517 m_numValue(param.m_numValue),
518 m_str_value(move(param.m_str_value)),
519 m_arrValue(move(param.m_arrValue)),
520 m_arrStrValue(move(param.m_arrStrValue)),
521 m_type(param.m_type)
522 {
523 }
524
uwbParam(const string & value)525 uwbParam::uwbParam(const string& value) :
526 m_numValue(0),
527 m_str_value(value),
528 m_type(type::STRING)
529 {
530 }
531
uwbParam(unsigned long value)532 uwbParam::uwbParam(unsigned long value) :
533 m_numValue(value),
534 m_type(type::NUMBER)
535 {
536 }
537
uwbParam(vector<uint8_t> && value)538 uwbParam::uwbParam(vector<uint8_t> &&value) :
539 m_arrValue(move(value)),
540 m_type(type::BYTEARRAY)
541 {
542 }
543
uwbParam(vector<string> && value)544 uwbParam::uwbParam(vector<string> &&value) :
545 m_arrStrValue(move(value)),
546 m_type(type::STRINGARRAY)
547 {
548 }
549
550
dump(const string & tag) const551 void uwbParam::dump(const string &tag) const
552 {
553 if (m_type == type::NUMBER) {
554 ALOGV(" - %s = 0x%lx", tag.c_str(), m_numValue);
555 } else if (m_type == type::STRING) {
556 ALOGV(" - %s = %s", tag.c_str(), m_str_value.c_str());
557 } else if (m_type == type::BYTEARRAY) {
558 stringstream ss_hex;
559 ss_hex.fill('0');
560 for (auto b : m_arrValue) {
561 ss_hex << setw(2) << hex << (int)b << " ";
562 }
563 ALOGV(" - %s = { %s}", tag.c_str(), ss_hex.str().c_str());
564 } else if (m_type == type::STRINGARRAY) {
565 stringstream ss;
566 for (auto s : m_arrStrValue) {
567 ss << "\"" << s << "\", ";
568 }
569 ALOGV(" - %s = { %s}", tag.c_str(), ss.str().c_str());
570 }
571 }
572 /*******************************************************************************/
573 class RegionCodeMap {
574 public:
loadMapping(const char * filepath)575 void loadMapping(const char *filepath) {
576 CUwbNxpConfig config(filepath);
577 if (!config.isValid()) {
578 ALOGW("Region mapping was not provided.");
579 return;
580 }
581
582 ALOGI("Region mapping was provided by %s", filepath);
583 auto &all_params = config.get_data();
584 for (auto &it : all_params) {
585 const auto ®ion_str = it.first;
586 const uwbParam *param = &it.second;
587
588 // split space-separated strings into set
589 stringstream ss(param->str_value());
590 string cc;
591 unordered_set<string> cc_set;
592 while (ss >> cc) {
593 if (cc.length() == 2 && isupper(cc[0]) && isupper(cc[1])) {
594 cc_set.emplace(move(cc));
595 }
596 }
597 auto result = m_map.try_emplace(region_str, move(cc_set));
598 if (!result.second) {
599 // region conlifct : merge
600 result.first->second.merge(move(cc_set));
601 }
602 }
603 m_config = move(config);
604 }
xlateCountryCode(const char country_code[2])605 string xlateCountryCode(const char country_code[2]) {
606 string code{country_code[0], country_code[1]};
607 if (m_config.isValid()) {
608 for (auto &it : m_map) {
609 const auto ®ion_str = it.first;
610 const auto &cc_set = it.second;
611 if (cc_set.find(code) != cc_set.end()) {
612 ALOGV("map country code %c%c --> %s",
613 country_code[0], country_code[1], region_str.c_str());
614 return region_str;
615 }
616 }
617 }
618 return code;
619 }
reset()620 void reset() {
621 m_config.reset();
622 m_map.clear();
623 }
dump()624 void dump() {
625 ALOGV("Region mapping dump:");
626 for (auto &entry : m_map) {
627 const auto ®ion_str = entry.first;
628 const auto &cc_set = entry.second;
629 stringstream ss;
630 for (const auto s : cc_set) {
631 ss << "\"" << s << "\", ";
632 }
633 ALOGV("- %s = { %s}", region_str.c_str(), ss.str().c_str());
634 }
635 }
636 private:
637 CUwbNxpConfig m_config;
638 unordered_map<string, unordered_set<string>> m_map;
639 };
640
641 /*******************************************************************************/
642 class CascadeConfig {
643 public:
644 CascadeConfig();
645
646 void init(const char *main_config);
647 void deinit();
648 bool setCountryCode(const char country_code[2]);
649
650 const uwbParam* find(const char *name) const;
651 bool getValue(const char* name, char* pValue, size_t len) const;
652 bool getValue(const char* name, unsigned long& rValue) const;
653 bool getValue(const char* name, uint8_t* pValue, size_t len, size_t* readlen) const;
654 private:
655 // default_nxp_config_path
656 CUwbNxpConfig mMainConfig;
657
658 // uci config
659 CUwbNxpConfig mUciConfig;
660
661 // EXTRA_CONF_PATH[N]
662 std::vector<std::pair<string, CUwbNxpConfig>> mExtraConfig;
663
664 // [COUNTRY_CODE_CAP_FILE_LOCATION]/country_code_config_name
665 CUwbNxpConfig mCapsConfig;
666
667 // Region Code mapping
668 RegionCodeMap mRegionMap;
669
670 // current set of specifiers for EXTRA_CONF_PATH[]
671 struct ExtraConfPathSpecifiers {
672 string mCurSku;
673 string mCurExtid;
674 string mCurRegionCode;
675 string mCurRevision;
reset__anonc2d6b5820111::CascadeConfig::ExtraConfPathSpecifiers676 void reset() {
677 mCurSku.clear();
678 mCurExtid.clear();
679 mCurRegionCode.clear();
680 mCurRevision.clear();
681 }
682 };
683 ExtraConfPathSpecifiers mExtraConfSpecifiers;
684
685 // Re-evaluate filepaths of mExtraConfig with mExtraConfSpecifiers, and re-load them.
686 // returns true if any of entries were updated.
687 bool evaluateExtraConfPaths();
688
dump()689 void dump() {
690 mMainConfig.dump();
691 mUciConfig.dump();
692
693 for (const auto &[filename, config] : mExtraConfig)
694 config.dump();
695
696 mCapsConfig.dump();
697 mRegionMap.dump();
698 }
699 };
700
CascadeConfig()701 CascadeConfig::CascadeConfig()
702 {
703 }
704
evaluateExtraConfPaths()705 bool CascadeConfig::evaluateExtraConfPaths()
706 {
707 bool updated = false;
708
709 for (auto& [filename, config] : mExtraConfig) {
710 std::string new_filename(filename);
711
712 auto posSku = filename.find(sku_specifier);
713 if (posSku != std::string::npos && !mExtraConfSpecifiers.mCurSku.empty()) {
714 new_filename.replace(posSku, strlen(sku_specifier), mExtraConfSpecifiers.mCurSku);
715 }
716
717 auto posExtid = filename.find(extid_specifier);
718 if (posExtid != std::string::npos && !mExtraConfSpecifiers.mCurExtid.empty()) {
719 new_filename.replace(posExtid, strlen(extid_specifier), mExtraConfSpecifiers.mCurExtid);
720 }
721
722 auto posCountry = filename.find(country_code_specifier);
723 if (posCountry != std::string::npos && !mExtraConfSpecifiers.mCurRegionCode.empty()) {
724 new_filename.replace(posCountry, strlen(country_code_specifier), mExtraConfSpecifiers.mCurRegionCode);
725 }
726
727 auto posRevision = filename.find(revision_specifier);
728 if (posRevision != std::string::npos && !mExtraConfSpecifiers.mCurRevision.empty()) {
729 new_filename.replace(posRevision, strlen(revision_specifier), mExtraConfSpecifiers.mCurRevision);
730 }
731
732 // re-open the file if filepath got re-evaluated.
733 if (new_filename != config.getFilePath()) {
734 config.open(new_filename.c_str());
735 updated = true;
736 }
737 }
738 return updated;
739 }
740
init(const char * main_config)741 void CascadeConfig::init(const char *main_config)
742 {
743 ALOGV("CascadeConfig initialize with %s", main_config);
744
745 // Main config file
746 CUwbNxpConfig config(main_config);
747 if (!config.isValid()) {
748 ALOGW("Failed to load main config file");
749 return;
750 }
751 mMainConfig = move(config);
752
753 {
754 // UCI config file
755 std::string uciConfigFilePath = default_uci_config_path;
756 uciConfigFilePath += nxp_uci_config_file;
757
758 CUwbNxpConfig config(uciConfigFilePath.c_str());
759 if (!config.isValid()) {
760 ALOGW("Failed to load uci config file:%s",
761 uciConfigFilePath.c_str());
762 } else {
763 mUciConfig = move(config);
764 }
765 }
766
767 char sku_value[PROPERTY_VALUE_MAX];
768 char revision_value[PROPERTY_VALUE_MAX];
769 property_get(prop_name_calsku, sku_value, prop_default_calsku);
770 property_get(prop_name_revision, revision_value, prop_default_revision);
771
772 // Read EXTRA_CONF_PATH[N]
773 for (int i = 1; i <= 10; i++) {
774 char key[32];
775 snprintf(key, sizeof(key), "EXTRA_CONF_PATH_%d", i);
776 const uwbParam *param = mMainConfig.find(key);
777 if (!param)
778 continue;
779
780 std::string filename(param->str_value());
781
782 auto entry = std::make_pair(param->str_value(), CUwbNxpConfig(filename.c_str()));
783 mExtraConfig.emplace_back(std::move(entry));
784 }
785
786 // evaluate <sku> and <revision>
787 mExtraConfSpecifiers.mCurSku = sku_value;
788 mExtraConfSpecifiers.mCurRevision = revision_value;
789 evaluateExtraConfPaths();
790
791 // re-evaluate with "<extid>"
792 char extid_value[PROPERTY_VALUE_MAX];
793 if (!NxpConfig_GetStr(extid_config_name, extid_value, sizeof(extid_value))) {
794 strcpy(extid_value, extid_default_value);
795 }
796 mExtraConfSpecifiers.mCurExtid = extid_value;
797 evaluateExtraConfPaths();
798
799 ALOGI("Provided specifiers: sku=[%s] revision=[%s] extid=[%s]", sku_value, revision_value, extid_value);
800
801 // Pick one libuwb-countrycode.conf with the highest VERSION number
802 // from multiple directories specified by COUNTRY_CODE_CAP_FILE_LOCATION
803 size_t arrLen = 0;
804 if (NxpConfig_GetStrArrayLen(NAME_COUNTRY_CODE_CAP_FILE_LOCATION, &arrLen) && arrLen > 0) {
805 constexpr size_t loc_max_len = 260;
806 auto loc = make_unique<char[]>(loc_max_len);
807 int version, max_version = -1;
808 string strPickedPath;
809 bool foundCapFile = false;
810 CUwbNxpConfig pickedConfig;
811
812 for (int i = 0; i < arrLen; i++) {
813 if (!NxpConfig_GetStrArrayVal(NAME_COUNTRY_CODE_CAP_FILE_LOCATION, i, loc.get(), loc_max_len)) {
814 continue;
815 }
816 string strPath(loc.get());
817 strPath += country_code_config_name;
818
819 ALOGV("Try to load %s", strPath.c_str());
820
821 CUwbNxpConfig config(strPath.c_str());
822
823 const uwbParam *param = config.find(NAME_NXP_COUNTRY_CODE_VERSION);
824 version = param ? atoi(param->str_value()) : -2;
825 if (version > max_version) {
826 foundCapFile = true;
827 pickedConfig = move(config);
828 strPickedPath = move(strPath);
829 max_version = version;
830 }
831 }
832 if (foundCapFile) {
833 mCapsConfig = move(pickedConfig);
834 ALOGI("CountryCodeCaps file %s loaded with VERSION=%d", strPickedPath.c_str(), max_version);
835 } else {
836 ALOGI("No CountryCodeCaps specified");
837 }
838 } else {
839 ALOGI(NAME_COUNTRY_CODE_CAP_FILE_LOCATION " was not specified, skip loading CountryCodeCaps");
840 }
841
842 // Load region mapping
843 const uwbParam *param = find(NAME_REGION_MAP_PATH);
844 if (param) {
845 mRegionMap.loadMapping(param->str_value());
846 }
847
848 ALOGD("CascadeConfig initialized");
849
850 dump();
851 }
852
deinit()853 void CascadeConfig::deinit()
854 {
855 mMainConfig.reset();
856 mExtraConfig.clear();
857 mCapsConfig.reset();
858 mRegionMap.reset();
859 mUciConfig.reset();
860 mExtraConfSpecifiers.reset();
861 }
862
setCountryCode(const char country_code[2])863 bool CascadeConfig::setCountryCode(const char country_code[2])
864 {
865 string strRegion = mRegionMap.xlateCountryCode(country_code);
866
867 if (strRegion == mExtraConfSpecifiers.mCurRegionCode) {
868 ALOGI("Same region code(%c%c --> %s), per-country configuration not updated.",
869 country_code[0], country_code[1], strRegion.c_str());
870 return false;
871 }
872
873 ALOGI("Apply country code %c%c --> %s\n", country_code[0], country_code[1], strRegion.c_str());
874 mExtraConfSpecifiers.mCurRegionCode = strRegion;
875
876 return evaluateExtraConfPaths();
877 }
878
find(const char * name) const879 const uwbParam* CascadeConfig::find(const char *name) const
880 {
881 const uwbParam* param = NULL;
882
883 param = mCapsConfig.find(name);
884 if (param)
885 return param;
886
887 for (auto it = mExtraConfig.rbegin(); it != mExtraConfig.rend(); it++) {
888 auto &config = it->second;
889 param = config.find(name);
890 if (param)
891 break;
892 }
893 if (!param) {
894 param = mMainConfig.find(name);
895 }
896 if (!param) {
897 param = mUciConfig.find(name);
898 }
899 return param;
900 }
901
902 // TODO: move these getValue() helpers out of the class
getValue(const char * name,char * pValue,size_t len) const903 bool CascadeConfig::getValue(const char* name, char* pValue, size_t len) const
904 {
905 const uwbParam *param = find(name);
906 if (!param)
907 return false;
908 if (param->getType() != uwbParam::type::STRING)
909 return false;
910 if (len < (param->str_len() + 1))
911 return false;
912
913 strncpy(pValue, param->str_value(), len);
914 return true;
915 }
916
getValue(const char * name,uint8_t * pValue,size_t len,size_t * readlen) const917 bool CascadeConfig::getValue(const char* name, uint8_t* pValue, size_t len, size_t* readlen) const
918 {
919 const uwbParam *param = find(name);
920 if (!param)
921 return false;
922 if (param->getType() != uwbParam::type::BYTEARRAY)
923 return false;
924 if (len < param->arr_len())
925 return false;
926 memcpy(pValue, param->arr_value(), param->arr_len());
927 if (readlen)
928 *readlen = param->arr_len();
929 return true;
930 }
931
getValue(const char * name,unsigned long & rValue) const932 bool CascadeConfig::getValue(const char* name, unsigned long& rValue) const
933 {
934 const uwbParam *param = find(name);
935 if (!param)
936 return false;
937 if (param->getType() != uwbParam::type::NUMBER)
938 return false;
939
940 rValue = param->numValue();
941 return true;
942 }
943
944 } // namespace
945
946 static CascadeConfig gConfig;
947
NxpConfig_Init(void)948 void NxpConfig_Init(void)
949 {
950 gConfig.init(default_nxp_config_path);
951 }
952
NxpConfig_Deinit(void)953 void NxpConfig_Deinit(void)
954 {
955 gConfig.deinit();
956 }
957
958 // return true if new per-country configuration file was load.
959 // false if it can stay at the current configuration.
NxpConfig_SetCountryCode(const char country_code[2])960 bool NxpConfig_SetCountryCode(const char country_code[2])
961 {
962 return gConfig.setCountryCode(country_code);
963 }
964
965 /*******************************************************************************
966 **
967 ** Function: NxpConfig_GetStr
968 **
969 ** Description: API function for getting a string value of a setting
970 **
971 ** Returns: True if found, otherwise False.
972 **
973 *******************************************************************************/
NxpConfig_GetStr(const char * name,char * pValue,size_t len)974 bool NxpConfig_GetStr(const char* name, char* pValue, size_t len)
975 {
976 return gConfig.getValue(name, pValue, len);
977 }
978
979 /*******************************************************************************
980 **
981 ** Function: NxpConfig_GetByteArray()
982 **
983 ** Description: Read byte array value from the config file.
984 **
985 ** Parameters:
986 ** name - name of the config param to read.
987 ** pValue - pointer to input buffer.
988 ** bufflen - input buffer length.
989 ** len - out parameter to return the number of bytes read from config file,
990 ** return -1 in case bufflen is not enough.
991 **
992 ** Returns: TRUE[1] if config param name is found in the config file, else FALSE[0]
993 **
994 *******************************************************************************/
NxpConfig_GetByteArray(const char * name,uint8_t * pValue,size_t bufflen,size_t * len)995 bool NxpConfig_GetByteArray(const char* name, uint8_t* pValue, size_t bufflen, size_t* len)
996 {
997 return gConfig.getValue(name, pValue, bufflen, len);
998 }
999
1000 /*******************************************************************************
1001 **
1002 ** Function: NxpConfig_GetNum
1003 **
1004 ** Description: API function for getting a numerical value of a setting
1005 **
1006 ** Returns: true, if successful
1007 **
1008 *******************************************************************************/
NxpConfig_GetNum(const char * name,void * pValue,size_t len)1009 bool NxpConfig_GetNum(const char* name, void* pValue, size_t len)
1010 {
1011 if ((name == nullptr) || (pValue == nullptr)){
1012 ALOGE("[%s] Invalid arguments", __func__);
1013 return false;
1014 }
1015 const uwbParam* pParam = gConfig.find(name);
1016
1017 if ((pParam == nullptr) || (pParam->getType() != uwbParam::type::NUMBER)) {
1018 ALOGE("Config:%s not found in the config file", name);
1019 return false;
1020 }
1021
1022 unsigned long v = pParam->numValue();
1023 switch (len)
1024 {
1025 case sizeof(unsigned long):
1026 *(static_cast<unsigned long*>(pValue)) = (unsigned long)v;
1027 break;
1028 case sizeof(unsigned short):
1029 *(static_cast<unsigned short*>(pValue)) = (unsigned short)v;
1030 break;
1031 case sizeof(unsigned char):
1032 *(static_cast<unsigned char*> (pValue)) = (unsigned char)v;
1033 break;
1034 default:
1035 ALOGE("[%s] unsupported length:%zu", __func__, len);
1036 return false;
1037 }
1038 return true;
1039 }
1040
1041 // Get the length of a 'string-array' type parameter
NxpConfig_GetStrArrayLen(const char * name,size_t * pLen)1042 bool NxpConfig_GetStrArrayLen(const char* name, size_t* pLen)
1043 {
1044 const uwbParam* param = gConfig.find(name);
1045 if (!param || param->getType() != uwbParam::type::STRINGARRAY)
1046 return false;
1047
1048 *pLen = param->str_arr_len();
1049 return true;
1050 }
1051
1052 // Get a string value from 'string-array' type parameters, index zero-based
NxpConfig_GetStrArrayVal(const char * name,int index,char * pValue,size_t len)1053 bool NxpConfig_GetStrArrayVal(const char* name, int index, char* pValue, size_t len)
1054 {
1055 const uwbParam* param = gConfig.find(name);
1056 if (!param || param->getType() != uwbParam::type::STRINGARRAY)
1057 return false;
1058 if (index < 0 || index >= param->str_arr_len())
1059 return false;
1060
1061 if (len < param->str_arr_elem_len(index) + 1)
1062 return false;
1063 strncpy(pValue, param->str_arr_elem(index), len);
1064 return true;
1065 }
1066