1 /* 2 * Copyright (c) 2018 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include "cfg_all.h" 20 #include "cfg_define.h" 21 #include "cfg_dispatcher.h" 22 #include "cfg_ucfg_api.h" 23 #include "i_cfg.h" 24 #include "i_cfg_objmgr.h" 25 #include "qdf_atomic.h" 26 #include "qdf_list.h" 27 #include "qdf_mem.h" 28 #include "qdf_module.h" 29 #include "qdf_parse.h" 30 #include "qdf_status.h" 31 #include "qdf_str.h" 32 #include "qdf_trace.h" 33 #include "qdf_types.h" 34 #include "wlan_objmgr_psoc_obj.h" 35 36 /** 37 * struct cfg_value_store - backing store for an ini file 38 * @path: file path of the ini file 39 * @node: internal list node for keeping track of all the allocated stores 40 * @users: number of references on the store 41 * @values: a values struct containing the parsed values from the ini file 42 */ 43 struct cfg_value_store { 44 char *path; 45 qdf_list_node_t node; 46 qdf_atomic_t users; 47 struct cfg_values values; 48 }; 49 50 /* define/populate dynamic metadata lookup table */ 51 52 /** 53 * struct cfg_meta - configuration item metadata for dynamic lookup during parse 54 * @name: name of the config item used in the ini file (i.e. "gScanDwellTime") 55 * @item_handler: parsing callback based on the type of the config item 56 * @min: minimum value for use in bounds checking (min_len for strings) 57 * @max: maximum value for use in bounds checking (max_len for strings) 58 * @fallback: the fallback behavior to use when configured values are invalid 59 */ 60 struct cfg_meta { 61 const char *name; 62 const uint32_t field_offset; 63 void (*const item_handler)(struct cfg_value_store *store, 64 const struct cfg_meta *meta, 65 const char *value); 66 const int32_t min; 67 const int32_t max; 68 const enum cfg_fallback_behavior fallback; 69 }; 70 71 /* ini item handler functions */ 72 73 #define cfg_value_ptr(store, meta) \ 74 ((void *)&(store)->values + (meta)->field_offset) 75 76 static void cfg_int_item_handler(struct cfg_value_store *store, 77 const struct cfg_meta *meta, 78 const char *str_value) 79 { 80 QDF_STATUS status; 81 int32_t *store_value = cfg_value_ptr(store, meta); 82 int32_t value; 83 84 status = qdf_int32_parse(str_value, &value); 85 if (QDF_IS_STATUS_ERROR(status)) { 86 cfg_err("%s=%s - Invalid format (status %d); Using default %d", 87 meta->name, str_value, status, *store_value); 88 return; 89 } 90 91 QDF_BUG(meta->min <= meta->max); 92 if (meta->min > meta->max) { 93 cfg_err("Invalid config item meta for %s", meta->name); 94 return; 95 } 96 97 if (value >= meta->min && value <= meta->max) { 98 *store_value = value; 99 return; 100 } 101 102 switch (meta->fallback) { 103 default: 104 QDF_DEBUG_PANIC(); 105 /* fall through */ 106 case CFG_VALUE_OR_DEFAULT: 107 /* store already contains default */ 108 break; 109 case CFG_VALUE_OR_CLAMP: 110 *store_value = __cfg_clamp(value, meta->min, meta->max); 111 break; 112 } 113 114 cfg_err("%s=%d - Out of range [%d, %d]; Using %d", 115 meta->name, value, meta->min, meta->max, *store_value); 116 } 117 118 static void cfg_uint_item_handler(struct cfg_value_store *store, 119 const struct cfg_meta *meta, 120 const char *str_value) 121 { 122 QDF_STATUS status; 123 uint32_t *store_value = cfg_value_ptr(store, meta); 124 uint32_t value; 125 uint32_t min; 126 uint32_t max; 127 128 /** 129 * Since meta min and max are of type int32_t 130 * We need explicit type casting to avoid 131 * implicit wrap around for uint32_t type cfg data. 132 */ 133 min = (uint32_t)meta->min; 134 max = (uint32_t)meta->max; 135 136 status = qdf_uint32_parse(str_value, &value); 137 if (QDF_IS_STATUS_ERROR(status)) { 138 cfg_err("%s=%s - Invalid format (status %d); Using default %u", 139 meta->name, str_value, status, *store_value); 140 return; 141 } 142 143 QDF_BUG(min <= max); 144 if (min > max) { 145 cfg_err("Invalid config item meta for %s", meta->name); 146 return; 147 } 148 149 if (value >= min && value <= max) { 150 *store_value = value; 151 return; 152 } 153 154 switch (meta->fallback) { 155 default: 156 QDF_DEBUG_PANIC(); 157 /* fall through */ 158 case CFG_VALUE_OR_DEFAULT: 159 /* store already contains default */ 160 break; 161 case CFG_VALUE_OR_CLAMP: 162 *store_value = __cfg_clamp(value, min, max); 163 break; 164 } 165 166 cfg_err("%s=%u - Out of range [%d, %d]; Using %u", 167 meta->name, value, min, max, *store_value); 168 } 169 170 static void cfg_bool_item_handler(struct cfg_value_store *store, 171 const struct cfg_meta *meta, 172 const char *str_value) 173 { 174 QDF_STATUS status; 175 bool *store_value = cfg_value_ptr(store, meta); 176 177 status = qdf_bool_parse(str_value, store_value); 178 if (QDF_IS_STATUS_SUCCESS(status)) 179 return; 180 181 cfg_err("%s=%s - Invalid format (status %d); Using default '%s'", 182 meta->name, str_value, status, *store_value ? "true" : "false"); 183 } 184 185 static void cfg_string_item_handler(struct cfg_value_store *store, 186 const struct cfg_meta *meta, 187 const char *str_value) 188 { 189 char *store_value = cfg_value_ptr(store, meta); 190 qdf_size_t len; 191 192 QDF_BUG(meta->min >= 0); 193 QDF_BUG(meta->min <= meta->max); 194 if (meta->min < 0 || meta->min > meta->max) { 195 cfg_err("Invalid config item meta for %s", meta->name); 196 return; 197 } 198 199 /* ensure min length */ 200 len = qdf_str_nlen(str_value, meta->min); 201 if (len < meta->min) { 202 cfg_err("%s=%s - Too short; Using default '%s'", 203 meta->name, str_value, store_value); 204 return; 205 } 206 207 /* check max length */ 208 len += qdf_str_nlen(str_value + meta->min, meta->max - meta->min + 1); 209 if (len > meta->max) { 210 cfg_err("%s=%s - Too long; Using default '%s'", 211 meta->name, str_value, store_value); 212 return; 213 } 214 215 qdf_str_lcopy(store_value, str_value, meta->max + 1); 216 } 217 218 static void cfg_mac_item_handler(struct cfg_value_store *store, 219 const struct cfg_meta *meta, 220 const char *str_value) 221 { 222 QDF_STATUS status; 223 struct qdf_mac_addr *store_value = cfg_value_ptr(store, meta); 224 225 status = qdf_mac_parse(str_value, store_value); 226 if (QDF_IS_STATUS_SUCCESS(status)) 227 return; 228 229 cfg_err("%s=%s - Invalid format (status %d); Using default " 230 QDF_MAC_ADDR_STR, meta->name, str_value, status, 231 QDF_MAC_ADDR_ARRAY(store_value->bytes)); 232 } 233 234 static void cfg_ipv4_item_handler(struct cfg_value_store *store, 235 const struct cfg_meta *meta, 236 const char *str_value) 237 { 238 QDF_STATUS status; 239 struct qdf_ipv4_addr *store_value = cfg_value_ptr(store, meta); 240 241 status = qdf_ipv4_parse(str_value, store_value); 242 if (QDF_IS_STATUS_SUCCESS(status)) 243 return; 244 245 cfg_err("%s=%s - Invalid format (status %d); Using default " 246 QDF_IPV4_ADDR_STR, meta->name, str_value, status, 247 QDF_IPV4_ADDR_ARRAY(store_value->bytes)); 248 } 249 250 static void cfg_ipv6_item_handler(struct cfg_value_store *store, 251 const struct cfg_meta *meta, 252 const char *str_value) 253 { 254 QDF_STATUS status; 255 struct qdf_ipv6_addr *store_value = cfg_value_ptr(store, meta); 256 257 status = qdf_ipv6_parse(str_value, store_value); 258 if (QDF_IS_STATUS_SUCCESS(status)) 259 return; 260 261 cfg_err("%s=%s - Invalid format (status %d); Using default " 262 QDF_IPV6_ADDR_STR, meta->name, str_value, status, 263 QDF_IPV6_ADDR_ARRAY(store_value->bytes)); 264 } 265 266 /* populate metadata lookup table */ 267 #undef __CFG_ANY 268 #define __CFG_ANY(_id, _mtype, _ctype, _name, _min, _max, _fallback, ...) \ 269 { \ 270 .name = _name, \ 271 .field_offset = qdf_offsetof(struct cfg_values, _id##_internal), \ 272 .item_handler = cfg_ ## _mtype ## _item_handler, \ 273 .min = _min, \ 274 .max = _max, \ 275 .fallback = _fallback, \ 276 }, 277 278 #define cfg_INT_item_handler cfg_int_item_handler 279 #define cfg_UINT_item_handler cfg_uint_item_handler 280 #define cfg_BOOL_item_handler cfg_bool_item_handler 281 #define cfg_STRING_item_handler cfg_string_item_handler 282 #define cfg_MAC_item_handler cfg_mac_item_handler 283 #define cfg_IPV4_item_handler cfg_ipv4_item_handler 284 #define cfg_IPV6_item_handler cfg_ipv6_item_handler 285 286 static const struct cfg_meta cfg_meta_lookup_table[] = { 287 CFG_ALL 288 }; 289 290 /* default store initializer */ 291 292 static void cfg_store_set_defaults(struct cfg_value_store *store) 293 { 294 #undef __CFG_ANY 295 #define __CFG_ANY(id, mtype, ctype, name, min, max, fallback, desc, def...) \ 296 ctype id = def; 297 298 CFG_ALL 299 300 #undef __CFG_STRING 301 #define __CFG_STRING(id, mtype, ctype, name, min_len, max_len, ...) \ 302 qdf_str_lcopy((char *)&store->values.id##_internal, id, max_len + 1); 303 #undef __CFG_ANY 304 #define __CFG_ANY(id, mtype, ctype, name, min, max, fallback, desc, def...) \ 305 *(ctype *)&store->values.id##_internal = id; 306 307 CFG_ALL 308 } 309 310 static const struct cfg_meta *cfg_lookup_meta(const char *name) 311 { 312 int i; 313 314 QDF_BUG(name); 315 if (!name) 316 return NULL; 317 318 /* linear search for now; optimize in the future if needed */ 319 for (i = 0; i < QDF_ARRAY_SIZE(cfg_meta_lookup_table); i++) { 320 const struct cfg_meta *meta = &cfg_meta_lookup_table[i]; 321 322 if (qdf_str_eq(name, meta->name)) 323 return meta; 324 } 325 326 return NULL; 327 } 328 329 static QDF_STATUS 330 cfg_ini_item_handler(void *context, const char *key, const char *value) 331 { 332 struct cfg_value_store *store = context; 333 const struct cfg_meta *meta; 334 335 meta = cfg_lookup_meta(key); 336 if (!meta) { 337 /* TODO: promote to 'err' or 'warn' once legacy is ported */ 338 cfg_info("Unknown config item '%s'", key); 339 return QDF_STATUS_SUCCESS; 340 } 341 342 QDF_BUG(meta->item_handler); 343 if (!meta->item_handler) 344 return QDF_STATUS_SUCCESS; 345 346 meta->item_handler(store, meta, value); 347 348 return QDF_STATUS_SUCCESS; 349 } 350 351 static QDF_STATUS cfg_ini_section_handler(void *context, const char *name) 352 { 353 cfg_err("Unexpected section '%s'. Sections are not supported.", name); 354 355 return QDF_STATUS_SUCCESS; 356 } 357 358 #define cfg_assert_success(expr) \ 359 do { \ 360 QDF_STATUS __assert_status = (expr); \ 361 QDF_BUG(QDF_IS_STATUS_SUCCESS(__assert_status)); \ 362 } while (0) 363 364 static bool __cfg_is_init; 365 static struct cfg_value_store *__cfg_global_store; 366 static qdf_list_t __cfg_stores_list; 367 static qdf_spinlock_t __cfg_stores_lock; 368 369 struct cfg_psoc_ctx { 370 struct cfg_value_store *store; 371 }; 372 373 static QDF_STATUS 374 cfg_store_alloc(const char *path, struct cfg_value_store **out_store) 375 { 376 QDF_STATUS status; 377 struct cfg_value_store *store; 378 379 cfg_enter(); 380 381 store = qdf_mem_malloc(sizeof(*store)); 382 if (!store) { 383 cfg_err("Out of memory"); 384 return QDF_STATUS_E_NOMEM; 385 } 386 387 status = qdf_str_dup(&store->path, path); 388 if (QDF_IS_STATUS_ERROR(status)) 389 goto free_store; 390 391 status = qdf_atomic_init(&store->users); 392 if (QDF_IS_STATUS_ERROR(status)) 393 goto free_path; 394 qdf_atomic_inc(&store->users); 395 396 qdf_spin_lock_bh(&__cfg_stores_lock); 397 status = qdf_list_insert_back(&__cfg_stores_list, &store->node); 398 qdf_spin_unlock_bh(&__cfg_stores_lock); 399 if (QDF_IS_STATUS_ERROR(status)) 400 goto free_path; 401 402 *out_store = store; 403 404 return QDF_STATUS_SUCCESS; 405 406 free_path: 407 qdf_mem_free(store->path); 408 409 free_store: 410 qdf_mem_free(store); 411 412 return status; 413 } 414 415 static void cfg_store_free(struct cfg_value_store *store) 416 { 417 QDF_STATUS status; 418 419 cfg_enter(); 420 421 qdf_spin_lock_bh(&__cfg_stores_lock); 422 status = qdf_list_remove_node(&__cfg_stores_list, &store->node); 423 qdf_spin_unlock_bh(&__cfg_stores_lock); 424 if (QDF_IS_STATUS_ERROR(status)) { 425 cfg_err("Failed config store list removal; status:%d", status); 426 QDF_DEBUG_PANIC(); 427 } 428 429 qdf_mem_free(store->path); 430 qdf_mem_free(store); 431 } 432 433 static QDF_STATUS 434 cfg_store_get(const char *path, struct cfg_value_store **out_store) 435 { 436 QDF_STATUS status; 437 qdf_list_node_t *node; 438 439 *out_store = NULL; 440 441 qdf_spin_lock_bh(&__cfg_stores_lock); 442 status = qdf_list_peek_front(&__cfg_stores_list, &node); 443 while (QDF_IS_STATUS_SUCCESS(status)) { 444 struct cfg_value_store *store = 445 qdf_container_of(node, struct cfg_value_store, node); 446 447 if (qdf_str_eq(path, store->path)) { 448 qdf_atomic_inc(&store->users); 449 *out_store = store; 450 break; 451 } 452 453 status = qdf_list_peek_next(&__cfg_stores_list, node, &node); 454 } 455 qdf_spin_unlock_bh(&__cfg_stores_lock); 456 457 return status; 458 } 459 460 static void cfg_store_put(struct cfg_value_store *store) 461 { 462 if (qdf_atomic_dec_and_test(&store->users)) 463 cfg_store_free(store); 464 } 465 466 static struct cfg_psoc_ctx *cfg_psoc_get_ctx(struct wlan_objmgr_psoc *psoc) 467 { 468 struct cfg_psoc_ctx *psoc_ctx; 469 470 psoc_ctx = cfg_psoc_get_priv(psoc); 471 QDF_BUG(psoc_ctx); 472 473 return psoc_ctx; 474 } 475 476 struct cfg_values *cfg_psoc_get_values(struct wlan_objmgr_psoc *psoc) 477 { 478 return &cfg_psoc_get_ctx(psoc)->store->values; 479 } 480 qdf_export_symbol(cfg_psoc_get_values); 481 482 static QDF_STATUS 483 cfg_ini_parse_to_store(const char *path, struct cfg_value_store *store) 484 { 485 QDF_STATUS status; 486 487 status = qdf_ini_parse(path, store, cfg_ini_item_handler, 488 cfg_ini_section_handler); 489 if (QDF_IS_STATUS_ERROR(status)) 490 cfg_err("Failed to parse *.ini file @ %s; status:%d", 491 path, status); 492 493 return status; 494 } 495 496 static void cfg_init(void) 497 { 498 qdf_list_create(&__cfg_stores_list, 0); 499 qdf_spinlock_create(&__cfg_stores_lock); 500 } 501 502 static void cfg_deinit(void) 503 { 504 qdf_spinlock_destroy(&__cfg_stores_lock); 505 qdf_list_destroy(&__cfg_stores_list); 506 } 507 508 static void cfg_try_deinit(void) 509 { 510 bool empty; 511 512 qdf_spin_lock_bh(&__cfg_stores_lock); 513 empty = qdf_list_empty(&__cfg_stores_list); 514 qdf_spin_unlock_bh(&__cfg_stores_lock); 515 516 if (empty) 517 cfg_deinit(); 518 } 519 520 static QDF_STATUS 521 cfg_on_psoc_create(struct wlan_objmgr_psoc *psoc, void *context) 522 { 523 QDF_STATUS status; 524 struct cfg_psoc_ctx *psoc_ctx; 525 526 cfg_enter(); 527 528 QDF_BUG(__cfg_global_store); 529 if (!__cfg_global_store) 530 return QDF_STATUS_E_FAILURE; 531 532 psoc_ctx = qdf_mem_malloc(sizeof(*psoc_ctx)); 533 if (!psoc_ctx) { 534 cfg_err("Out of memory"); 535 return QDF_STATUS_E_NOMEM; 536 } 537 538 qdf_atomic_inc(&__cfg_global_store->users); 539 psoc_ctx->store = __cfg_global_store; 540 541 status = cfg_psoc_set_priv(psoc, psoc_ctx); 542 if (QDF_IS_STATUS_ERROR(status)) 543 goto put_store; 544 545 return QDF_STATUS_SUCCESS; 546 547 put_store: 548 cfg_store_put(__cfg_global_store); 549 qdf_mem_free(psoc_ctx); 550 551 return status; 552 } 553 554 static QDF_STATUS 555 cfg_on_psoc_destroy(struct wlan_objmgr_psoc *psoc, void *context) 556 { 557 QDF_STATUS status; 558 struct cfg_psoc_ctx *psoc_ctx; 559 560 cfg_enter(); 561 562 psoc_ctx = cfg_psoc_get_ctx(psoc); 563 status = cfg_psoc_unset_priv(psoc, psoc_ctx); 564 565 cfg_store_put(psoc_ctx->store); 566 qdf_mem_free(psoc_ctx); 567 568 return status; 569 } 570 571 QDF_STATUS cfg_dispatcher_init(void) 572 { 573 QDF_STATUS status; 574 575 cfg_enter(); 576 577 QDF_BUG(!__cfg_is_init); 578 if (__cfg_is_init) 579 return QDF_STATUS_E_INVAL; 580 581 status = cfg_psoc_register_create(cfg_on_psoc_create); 582 if (QDF_IS_STATUS_ERROR(status)) 583 return status; 584 585 status = cfg_psoc_register_destroy(cfg_on_psoc_destroy); 586 if (QDF_IS_STATUS_ERROR(status)) 587 goto unreg_create; 588 589 __cfg_is_init = true; 590 591 return QDF_STATUS_SUCCESS; 592 593 unreg_create: 594 cfg_assert_success(cfg_psoc_unregister_create(cfg_on_psoc_create)); 595 596 return status; 597 } 598 599 QDF_STATUS cfg_dispatcher_deinit(void) 600 { 601 cfg_enter(); 602 603 QDF_BUG(__cfg_is_init); 604 if (!__cfg_is_init) 605 return QDF_STATUS_E_INVAL; 606 607 __cfg_is_init = false; 608 609 cfg_assert_success(cfg_psoc_unregister_create(cfg_on_psoc_create)); 610 cfg_assert_success(cfg_psoc_unregister_destroy(cfg_on_psoc_destroy)); 611 612 cfg_try_deinit(); 613 614 return QDF_STATUS_SUCCESS; 615 } 616 617 QDF_STATUS cfg_parse(const char *path) 618 { 619 QDF_STATUS status; 620 struct cfg_value_store *store; 621 622 cfg_enter(); 623 624 QDF_BUG(!__cfg_global_store); 625 if (__cfg_global_store) 626 return QDF_STATUS_E_INVAL; 627 628 cfg_init(); 629 630 status = cfg_store_alloc(path, &store); 631 if (QDF_IS_STATUS_ERROR(status)) 632 goto deinit; 633 634 cfg_store_set_defaults(store); 635 636 status = cfg_ini_parse_to_store(path, store); 637 if (QDF_IS_STATUS_ERROR(status)) 638 goto free_store; 639 640 __cfg_global_store = store; 641 642 return QDF_STATUS_SUCCESS; 643 644 free_store: 645 cfg_store_free(store); 646 647 deinit: 648 cfg_deinit(); 649 650 return status; 651 } 652 653 void cfg_release(void) 654 { 655 cfg_enter(); 656 657 QDF_BUG(__cfg_global_store); 658 if (!__cfg_global_store) 659 return; 660 661 cfg_store_put(__cfg_global_store); 662 __cfg_global_store = NULL; 663 664 cfg_try_deinit(); 665 } 666 667 QDF_STATUS cfg_psoc_parse(struct wlan_objmgr_psoc *psoc, const char *path) 668 { 669 QDF_STATUS status; 670 struct cfg_value_store *store; 671 struct cfg_psoc_ctx *psoc_ctx; 672 673 cfg_enter(); 674 675 QDF_BUG(__cfg_global_store); 676 if (!__cfg_global_store) 677 return QDF_STATUS_E_INVAL; 678 679 QDF_BUG(__cfg_is_init); 680 if (!__cfg_is_init) 681 return QDF_STATUS_E_INVAL; 682 683 QDF_BUG(psoc); 684 if (!psoc) 685 return QDF_STATUS_E_INVAL; 686 687 QDF_BUG(path); 688 if (!path) 689 return QDF_STATUS_E_INVAL; 690 691 psoc_ctx = cfg_psoc_get_ctx(psoc); 692 693 QDF_BUG(psoc_ctx->store == __cfg_global_store); 694 if (psoc_ctx->store != __cfg_global_store) 695 return QDF_STATUS_SUCCESS; 696 697 /* check if @path has been parsed before */ 698 status = cfg_store_get(path, &store); 699 if (QDF_IS_STATUS_ERROR(status)) { 700 status = cfg_store_alloc(path, &store); 701 if (QDF_IS_STATUS_ERROR(status)) 702 return status; 703 704 /* inherit global configuration */ 705 qdf_mem_copy(&store->values, &__cfg_global_store->values, 706 sizeof(store->values)); 707 708 status = cfg_ini_parse_to_store(path, store); 709 if (QDF_IS_STATUS_ERROR(status)) 710 goto put_store; 711 } 712 713 psoc_ctx->store = store; 714 cfg_store_put(__cfg_global_store); 715 716 return QDF_STATUS_SUCCESS; 717 718 put_store: 719 cfg_store_put(store); 720 721 return status; 722 } 723 724