/* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "cfg_all.h" #include "cfg_define.h" #include "cfg_dispatcher.h" #include "cfg_ucfg_api.h" #include "i_cfg.h" #include "i_cfg_objmgr.h" #include "qdf_atomic.h" #include "qdf_list.h" #include "qdf_mem.h" #include "qdf_module.h" #include "qdf_parse.h" #include "qdf_status.h" #include "qdf_str.h" #include "qdf_trace.h" #include "qdf_types.h" #include "wlan_objmgr_psoc_obj.h" /** * struct cfg_value_store - backing store for an ini file * @path: file path of the ini file * @node: internal list node for keeping track of all the allocated stores * @users: number of references on the store * @values: a values struct containing the parsed values from the ini file */ struct cfg_value_store { char *path; qdf_list_node_t node; qdf_atomic_t users; struct cfg_values values; }; /** * enum cfg_type - Enum for CFG/INI types * @CFG_INT_ITEM: Integer CFG/INI * @CFG_UINT_ITEM: Unsigned integer CFG/INI * @CFG_BOOL_ITEM: Boolean CFG/INI * @CFG_STRING_ITEM: String CFG/INI * @CFG_MAC_ITEM: Mac address CFG/INI * @CFG_IPV4_ITEM: IPV4 address CFG/INI * @CFG_IPV6_ITEM: IPV6 address CFG/INI * @CFG_MAX_ITEM: Max CFG type */ enum cfg_type { CFG_INT_ITEM, CFG_UINT_ITEM, CFG_BOOL_ITEM, CFG_STRING_ITEM, CFG_MAC_ITEM, CFG_IPV4_ITEM, CFG_IPV6_ITEM, CFG_MAX_ITEM, }; #define CFG_META_NAME_LENGTH_MAX 256 #define CFG_INI_LENGTH_MAX 128 /* define/populate dynamic metadata lookup table */ /** * struct cfg_meta - configuration item metadata for dynamic lookup during parse * @name: name of the config item used in the ini file (i.e. "gScanDwellTime") * @item_handler: parsing callback based on the type of the config item * @min: minimum value for use in bounds checking (min_len for strings) * @max: maximum value for use in bounds checking (max_len for strings) * @fallback: the fallback behavior to use when configured values are invalid */ struct cfg_meta { const char *name; const uint32_t field_offset; const enum cfg_type cfg_type; void (*const item_handler)(struct cfg_value_store *store, const struct cfg_meta *meta, const char *value); const int32_t min; const int32_t max; const enum cfg_fallback_behavior fallback; }; /* ini item handler functions */ #define cfg_value_ptr(store, meta) \ ((void *)&(store)->values + (meta)->field_offset) static __attribute__((unused)) void cfg_int_item_handler(struct cfg_value_store *store, const struct cfg_meta *meta, const char *str_value) { QDF_STATUS status; int32_t *store_value = cfg_value_ptr(store, meta); int32_t value; status = qdf_int32_parse(str_value, &value); if (QDF_IS_STATUS_ERROR(status)) { cfg_err("%s=%s - Invalid format (status %d); Using default %d", meta->name, str_value, status, *store_value); return; } QDF_BUG(meta->min <= meta->max); if (meta->min > meta->max) { cfg_err("Invalid config item meta for %s", meta->name); return; } if (value >= meta->min && value <= meta->max) { *store_value = value; return; } switch (meta->fallback) { default: QDF_DEBUG_PANIC("Unknown fallback method %d for cfg item '%s'", meta->fallback, meta->name); fallthrough; case CFG_VALUE_OR_DEFAULT: /* store already contains default */ break; case CFG_VALUE_OR_CLAMP: *store_value = __cfg_clamp(value, meta->min, meta->max); break; } cfg_err("%s=%d - Out of range [%d, %d]; Using %d", meta->name, value, meta->min, meta->max, *store_value); } static __attribute__((unused)) void cfg_uint_item_handler(struct cfg_value_store *store, const struct cfg_meta *meta, const char *str_value) { QDF_STATUS status; uint32_t *store_value = cfg_value_ptr(store, meta); uint32_t value; uint32_t min; uint32_t max; /** * Since meta min and max are of type int32_t * We need explicit type casting to avoid * implicit wrap around for uint32_t type cfg data. */ min = (uint32_t)meta->min; max = (uint32_t)meta->max; status = qdf_uint32_parse(str_value, &value); if (QDF_IS_STATUS_ERROR(status)) { cfg_err("%s=%s - Invalid format (status %d); Using default %u", meta->name, str_value, status, *store_value); return; } QDF_BUG(min <= max); if (min > max) { cfg_err("Invalid config item meta for %s", meta->name); return; } if (value >= min && value <= max) { *store_value = value; return; } switch (meta->fallback) { default: QDF_DEBUG_PANIC("Unknown fallback method %d for cfg item '%s'", meta->fallback, meta->name); fallthrough; case CFG_VALUE_OR_DEFAULT: /* store already contains default */ break; case CFG_VALUE_OR_CLAMP: *store_value = __cfg_clamp(value, min, max); break; } cfg_err("%s=%u - Out of range [%d, %d]; Using %u", meta->name, value, min, max, *store_value); } static __attribute__((unused)) void cfg_bool_item_handler(struct cfg_value_store *store, const struct cfg_meta *meta, const char *str_value) { QDF_STATUS status; bool *store_value = cfg_value_ptr(store, meta); status = qdf_bool_parse(str_value, store_value); if (QDF_IS_STATUS_SUCCESS(status)) return; cfg_err("%s=%s - Invalid format (status %d); Using default '%s'", meta->name, str_value, status, *store_value ? "true" : "false"); } static __attribute__((unused)) void cfg_string_item_handler(struct cfg_value_store *store, const struct cfg_meta *meta, const char *str_value) { char *store_value = cfg_value_ptr(store, meta); qdf_size_t len; QDF_BUG(meta->min >= 0); QDF_BUG(meta->min <= meta->max); if (meta->min < 0 || meta->min > meta->max) { cfg_err("Invalid config item meta for %s", meta->name); return; } /* ensure min length */ len = qdf_str_nlen(str_value, meta->min); if (len < meta->min) { cfg_err("%s=%s - Too short; Using default '%s'", meta->name, str_value, store_value); return; } /* check max length */ len += qdf_str_nlen(str_value + meta->min, meta->max - meta->min + 1); if (len > meta->max) { cfg_err("%s=%s - Too long; Using default '%s'", meta->name, str_value, store_value); return; } qdf_str_lcopy(store_value, str_value, meta->max + 1); } static __attribute__((unused)) void cfg_mac_item_handler(struct cfg_value_store *store, const struct cfg_meta *meta, const char *str_value) { QDF_STATUS status; struct qdf_mac_addr *store_value = cfg_value_ptr(store, meta); status = qdf_mac_parse(str_value, store_value); if (QDF_IS_STATUS_SUCCESS(status)) return; cfg_err("%s=%s - Invalid format (status %d); Using default " QDF_MAC_ADDR_FMT, meta->name, str_value, status, QDF_MAC_ADDR_REF(store_value->bytes)); } static __attribute__((unused)) void cfg_ipv4_item_handler(struct cfg_value_store *store, const struct cfg_meta *meta, const char *str_value) { QDF_STATUS status; struct qdf_ipv4_addr *store_value = cfg_value_ptr(store, meta); status = qdf_ipv4_parse(str_value, store_value); if (QDF_IS_STATUS_SUCCESS(status)) return; cfg_err("%s=%s - Invalid format (status %d); Using default " QDF_IPV4_ADDR_STR, meta->name, str_value, status, QDF_IPV4_ADDR_ARRAY(store_value->bytes)); } static __attribute__((unused)) void cfg_ipv6_item_handler(struct cfg_value_store *store, const struct cfg_meta *meta, const char *str_value) { QDF_STATUS status; struct qdf_ipv6_addr *store_value = cfg_value_ptr(store, meta); status = qdf_ipv6_parse(str_value, store_value); if (QDF_IS_STATUS_SUCCESS(status)) return; cfg_err("%s=%s - Invalid format (status %d); Using default " QDF_IPV6_ADDR_STR, meta->name, str_value, status, QDF_IPV6_ADDR_ARRAY(store_value->bytes)); } /* populate metadata lookup table */ #undef __CFG_INI #define __CFG_INI(_id, _mtype, _ctype, _name, _min, _max, _fallback, ...) \ { \ .name = _name, \ .field_offset = qdf_offsetof(struct cfg_values, _id##_internal), \ .cfg_type = CFG_ ##_mtype ## _ITEM, \ .item_handler = cfg_ ## _mtype ## _item_handler, \ .min = _min, \ .max = _max, \ .fallback = _fallback, \ }, #define cfg_INT_item_handler cfg_int_item_handler #define cfg_UINT_item_handler cfg_uint_item_handler #define cfg_BOOL_item_handler cfg_bool_item_handler #define cfg_STRING_item_handler cfg_string_item_handler #define cfg_MAC_item_handler cfg_mac_item_handler #define cfg_IPV4_item_handler cfg_ipv4_item_handler #define cfg_IPV6_item_handler cfg_ipv6_item_handler static const struct cfg_meta cfg_meta_lookup_table[] = { CFG_ALL }; /* default store initializer */ static void cfg_store_set_defaults(struct cfg_value_store *store) { #undef __CFG_INI #define __CFG_INI(id, mtype, ctype, name, min, max, fallback, desc, def...) \ ctype id = def; CFG_ALL #undef __CFG_INI_STRING #define __CFG_INI_STRING(id, mtype, ctype, name, min_len, max_len, ...) \ qdf_str_lcopy((char *)&store->values.id##_internal, id, (max_len) + 1); #undef __CFG_INI #define __CFG_INI(id, mtype, ctype, name, min, max, fallback, desc, def...) \ *(ctype *)&store->values.id##_internal = id; CFG_ALL } static const struct cfg_meta *cfg_lookup_meta(const char *name) { int i; char *param1; char param[CFG_META_NAME_LENGTH_MAX]; uint8_t ini_name[CFG_INI_LENGTH_MAX]; QDF_BUG(name); if (!name) return NULL; /* linear search for now; optimize in the future if needed */ for (i = 0; i < QDF_ARRAY_SIZE(cfg_meta_lookup_table); i++) { const struct cfg_meta *meta = &cfg_meta_lookup_table[i]; qdf_mem_zero(ini_name, CFG_INI_LENGTH_MAX); qdf_mem_zero(param, CFG_META_NAME_LENGTH_MAX); if (strlen(meta->name) >= CFG_META_NAME_LENGTH_MAX) { cfg_err("Invalid meta name %s", meta->name); continue; } qdf_mem_copy(param, meta->name, strlen(meta->name)); param[strlen(meta->name)] = '\0'; param1 = param; if (!sscanf(param1, "%s", ini_name)) { cfg_err("Cannot get ini name %s", param1); return NULL; } if (qdf_str_eq(name, ini_name)) return meta; param1 = strpbrk(param, " "); while (param1) { param1++; if (!sscanf(param1, "%s ", ini_name)) { cfg_err("Invalid ini name %s", meta->name); return NULL; } if (qdf_str_eq(name, ini_name)) return meta; param1 = strpbrk(param1, " "); } } return NULL; } static QDF_STATUS cfg_ini_item_handler(void *context, const char *key, const char *value) { struct cfg_value_store *store = context; const struct cfg_meta *meta; meta = cfg_lookup_meta(key); if (!meta) { /* TODO: promote to 'err' or 'warn' once legacy is ported */ cfg_debug("Unknown config item '%s'", key); return QDF_STATUS_SUCCESS; } QDF_BUG(meta->item_handler); if (!meta->item_handler) return QDF_STATUS_SUCCESS; meta->item_handler(store, meta, value); return QDF_STATUS_SUCCESS; } static QDF_STATUS cfg_ini_section_handler(void *context, const char *name) { cfg_err("Unexpected section '%s'. Sections are not supported.", name); return QDF_STATUS_SUCCESS; } #define cfg_assert_success(expr) \ do { \ QDF_STATUS __assert_status = (expr); \ QDF_BUG(QDF_IS_STATUS_SUCCESS(__assert_status)); \ } while (0) static bool __cfg_is_init; static struct cfg_value_store *__cfg_global_store; static qdf_list_t __cfg_stores_list; static qdf_spinlock_t __cfg_stores_lock; struct cfg_psoc_ctx { struct cfg_value_store *store; }; static QDF_STATUS cfg_store_alloc(const char *path, struct cfg_value_store **out_store) { QDF_STATUS status; struct cfg_value_store *store; cfg_enter(); store = qdf_mem_malloc(sizeof(*store)); if (!store) return QDF_STATUS_E_NOMEM; status = qdf_str_dup(&store->path, path); if (QDF_IS_STATUS_ERROR(status)) goto free_store; status = qdf_atomic_init(&store->users); if (QDF_IS_STATUS_ERROR(status)) goto free_path; qdf_atomic_inc(&store->users); qdf_spin_lock_bh(&__cfg_stores_lock); status = qdf_list_insert_back(&__cfg_stores_list, &store->node); qdf_spin_unlock_bh(&__cfg_stores_lock); if (QDF_IS_STATUS_ERROR(status)) goto free_path; *out_store = store; return QDF_STATUS_SUCCESS; free_path: qdf_mem_free(store->path); free_store: qdf_mem_free(store); return status; } static void cfg_store_free(struct cfg_value_store *store) { QDF_STATUS status; cfg_enter(); qdf_spin_lock_bh(&__cfg_stores_lock); status = qdf_list_remove_node(&__cfg_stores_list, &store->node); qdf_spin_unlock_bh(&__cfg_stores_lock); if (QDF_IS_STATUS_ERROR(status)) QDF_DEBUG_PANIC("Failed config store list removal; status:%d", status); qdf_mem_free(store->path); qdf_mem_free(store); } static QDF_STATUS cfg_store_get(const char *path, struct cfg_value_store **out_store) { QDF_STATUS status; qdf_list_node_t *node; *out_store = NULL; qdf_spin_lock_bh(&__cfg_stores_lock); status = qdf_list_peek_front(&__cfg_stores_list, &node); while (QDF_IS_STATUS_SUCCESS(status)) { struct cfg_value_store *store = qdf_container_of(node, struct cfg_value_store, node); if (qdf_str_eq(path, store->path)) { qdf_atomic_inc(&store->users); *out_store = store; break; } status = qdf_list_peek_next(&__cfg_stores_list, node, &node); } qdf_spin_unlock_bh(&__cfg_stores_lock); return status; } static void cfg_store_put(struct cfg_value_store *store) { if (qdf_atomic_dec_and_test(&store->users)) cfg_store_free(store); } static struct cfg_psoc_ctx *cfg_psoc_get_ctx(struct wlan_objmgr_psoc *psoc) { struct cfg_psoc_ctx *psoc_ctx; psoc_ctx = cfg_psoc_get_priv(psoc); QDF_BUG(psoc_ctx); return psoc_ctx; } struct cfg_values *cfg_psoc_get_values(struct wlan_objmgr_psoc *psoc) { return &cfg_psoc_get_ctx(psoc)->store->values; } qdf_export_symbol(cfg_psoc_get_values); static QDF_STATUS cfg_ini_parse_to_store(const char *path, struct cfg_value_store *store) { QDF_STATUS status; status = qdf_ini_parse(path, store, cfg_ini_item_handler, cfg_ini_section_handler); if (QDF_IS_STATUS_ERROR(status)) cfg_err("Failed to parse *.ini file @ %s; status:%d", path, status); return status; } static QDF_STATUS cfg_ini_section_parse_to_store(const char *path, const char *section_name, struct cfg_value_store *store) { QDF_STATUS status; status = qdf_ini_section_parse(path, store, cfg_ini_item_handler, section_name); if (QDF_IS_STATUS_ERROR(status)) cfg_err("Failed to parse *.ini file @ %s; status:%d", path, status); return status; } QDF_STATUS cfg_parse_to_psoc_store(struct wlan_objmgr_psoc *psoc, const char *path) { return cfg_ini_parse_to_store(path, cfg_psoc_get_ctx(psoc)->store); } qdf_export_symbol(cfg_parse_to_psoc_store); QDF_STATUS cfg_section_parse_to_psoc_store(struct wlan_objmgr_psoc *psoc, const char *path, const char *section_name) { return cfg_ini_section_parse_to_store(path, section_name, cfg_psoc_get_ctx(psoc)->store); } qdf_export_symbol(cfg_section_parse_to_psoc_store); QDF_STATUS cfg_parse_to_global_store(const char *path) { if (!__cfg_global_store) { cfg_err("Global INI store is not valid"); return QDF_STATUS_E_NOMEM; } return cfg_ini_parse_to_store(path, __cfg_global_store); } qdf_export_symbol(cfg_parse_to_global_store); static QDF_STATUS cfg_store_print(struct wlan_objmgr_psoc *psoc) { struct cfg_value_store *store; struct cfg_psoc_ctx *psoc_ctx; void *offset; uint32_t i; cfg_enter(); psoc_ctx = cfg_psoc_get_ctx(psoc); if (!psoc_ctx) return QDF_STATUS_E_FAILURE; store = psoc_ctx->store; if (!store) return QDF_STATUS_E_FAILURE; for (i = 0; i < QDF_ARRAY_SIZE(cfg_meta_lookup_table); i++) { const struct cfg_meta *meta = &cfg_meta_lookup_table[i]; offset = cfg_value_ptr(store, meta); switch (meta->cfg_type) { case CFG_INT_ITEM: cfg_nofl_debug("%pK %s %d", offset, meta->name, *((int32_t *)offset)); break; case CFG_UINT_ITEM: cfg_nofl_debug("%pK %s %d", offset, meta->name, *((uint32_t *)offset)); break; case CFG_BOOL_ITEM: cfg_nofl_debug("%pK %s %d", offset, meta->name, *((bool *)offset)); break; case CFG_STRING_ITEM: cfg_nofl_debug("%pK %s %s", offset, meta->name, (char *)offset); break; case CFG_MAC_ITEM: cfg_nofl_debug("%pK %s " QDF_MAC_ADDR_FMT, offset, meta->name, QDF_MAC_ADDR_REF((uint8_t *)offset)); break; case CFG_IPV4_ITEM: cfg_nofl_debug("%pK %s %pI4", offset, meta->name, offset); break; case CFG_IPV6_ITEM: cfg_nofl_debug("%pK %s %pI6c", offset, meta->name, offset); break; default: continue; } } cfg_exit(); return QDF_STATUS_SUCCESS; } static QDF_STATUS cfg_ini_config_print(struct wlan_objmgr_psoc *psoc, uint8_t *buf, ssize_t *plen, ssize_t buflen) { struct cfg_value_store *store; struct cfg_psoc_ctx *psoc_ctx; ssize_t len; ssize_t total_len = buflen; uint32_t i; void *offset; cfg_enter(); psoc_ctx = cfg_psoc_get_ctx(psoc); if (!psoc_ctx) return QDF_STATUS_E_FAILURE; store = psoc_ctx->store; if (!store) return QDF_STATUS_E_FAILURE; for (i = 0; i < QDF_ARRAY_SIZE(cfg_meta_lookup_table); i++) { const struct cfg_meta *meta = &cfg_meta_lookup_table[i]; offset = cfg_value_ptr(store, meta); switch (meta->cfg_type) { case CFG_INT_ITEM: len = qdf_scnprintf(buf, buflen, "%s %d\n", meta->name, *((int32_t *)offset)); buf += len; break; case CFG_UINT_ITEM: len = qdf_scnprintf(buf, buflen, "%s %d\n", meta->name, *((uint32_t *)offset)); buf += len; buflen -= len; break; case CFG_BOOL_ITEM: len = qdf_scnprintf(buf, buflen, "%s %d\n", meta->name, *((bool *)offset)); buf += len; buflen -= len; break; case CFG_STRING_ITEM: len = qdf_scnprintf(buf, buflen, "%s %s\n", meta->name, (char *)offset); buf += len; buflen -= len; break; case CFG_MAC_ITEM: len = qdf_scnprintf(buf, buflen, "%s " QDF_MAC_ADDR_FMT "\n", meta->name, QDF_MAC_ADDR_REF( (uint8_t *)offset)); buf += len; buflen -= len; break; case CFG_IPV4_ITEM: len = qdf_scnprintf(buf, buflen, "%s %pI4\n", meta->name, offset); buf += len; buflen -= len; break; case CFG_IPV6_ITEM: len = qdf_scnprintf(buf, buflen, "%s %pI6c\n", meta->name, offset); buf += len; buflen -= len; break; default: continue; } } *plen = total_len - buflen; cfg_exit(); return QDF_STATUS_SUCCESS; } QDF_STATUS ucfg_cfg_store_print(struct wlan_objmgr_psoc *psoc) { return cfg_store_print(psoc); } qdf_export_symbol(ucfg_cfg_store_print); QDF_STATUS ucfg_cfg_ini_config_print(struct wlan_objmgr_psoc *psoc, uint8_t *buf, ssize_t *plen, ssize_t buflen) { return cfg_ini_config_print(psoc, buf, plen, buflen); } static QDF_STATUS cfg_on_psoc_create(struct wlan_objmgr_psoc *psoc, void *context) { QDF_STATUS status; struct cfg_psoc_ctx *psoc_ctx; cfg_enter(); QDF_BUG(__cfg_global_store); if (!__cfg_global_store) return QDF_STATUS_E_FAILURE; psoc_ctx = qdf_mem_malloc(sizeof(*psoc_ctx)); if (!psoc_ctx) return QDF_STATUS_E_NOMEM; qdf_atomic_inc(&__cfg_global_store->users); psoc_ctx->store = __cfg_global_store; status = cfg_psoc_set_priv(psoc, psoc_ctx); if (QDF_IS_STATUS_ERROR(status)) goto put_store; return QDF_STATUS_SUCCESS; put_store: cfg_store_put(__cfg_global_store); qdf_mem_free(psoc_ctx); return status; } static QDF_STATUS cfg_on_psoc_destroy(struct wlan_objmgr_psoc *psoc, void *context) { QDF_STATUS status; struct cfg_psoc_ctx *psoc_ctx; cfg_enter(); psoc_ctx = cfg_psoc_get_ctx(psoc); status = cfg_psoc_unset_priv(psoc, psoc_ctx); cfg_store_put(psoc_ctx->store); qdf_mem_free(psoc_ctx); return status; } QDF_STATUS cfg_dispatcher_init(void) { QDF_STATUS status; cfg_enter(); QDF_BUG(!__cfg_is_init); if (__cfg_is_init) return QDF_STATUS_E_INVAL; qdf_list_create(&__cfg_stores_list, 0); qdf_spinlock_create(&__cfg_stores_lock); status = cfg_psoc_register_create(cfg_on_psoc_create); if (QDF_IS_STATUS_ERROR(status)) return status; status = cfg_psoc_register_destroy(cfg_on_psoc_destroy); if (QDF_IS_STATUS_ERROR(status)) goto unreg_create; __cfg_is_init = true; return QDF_STATUS_SUCCESS; unreg_create: cfg_assert_success(cfg_psoc_unregister_create(cfg_on_psoc_create)); return status; } QDF_STATUS cfg_dispatcher_deinit(void) { cfg_enter(); QDF_BUG(__cfg_is_init); if (!__cfg_is_init) return QDF_STATUS_E_INVAL; __cfg_is_init = false; cfg_assert_success(cfg_psoc_unregister_create(cfg_on_psoc_create)); cfg_assert_success(cfg_psoc_unregister_destroy(cfg_on_psoc_destroy)); qdf_spin_lock_bh(&__cfg_stores_lock); QDF_BUG(qdf_list_empty(&__cfg_stores_list)); qdf_spin_unlock_bh(&__cfg_stores_lock); qdf_spinlock_destroy(&__cfg_stores_lock); qdf_list_destroy(&__cfg_stores_list); return QDF_STATUS_SUCCESS; } QDF_STATUS cfg_parse(const char *path) { QDF_STATUS status; struct cfg_value_store *store; cfg_enter(); if (!__cfg_global_store) { status = cfg_store_alloc(path, &store); if (QDF_IS_STATUS_ERROR(status)) return status; cfg_store_set_defaults(store); status = cfg_ini_parse_to_store(path, store); if (QDF_IS_STATUS_ERROR(status)) goto free_store; __cfg_global_store = store; return QDF_STATUS_SUCCESS; } store = __cfg_global_store; status = cfg_ini_parse_to_store(path, store); return status; free_store: cfg_store_free(store); return status; } void cfg_release(void) { cfg_enter(); QDF_BUG(__cfg_global_store); if (!__cfg_global_store) return; cfg_store_put(__cfg_global_store); __cfg_global_store = NULL; } QDF_STATUS cfg_psoc_parse(struct wlan_objmgr_psoc *psoc, const char *path) { QDF_STATUS status; struct cfg_value_store *store; struct cfg_psoc_ctx *psoc_ctx; cfg_enter(); QDF_BUG(__cfg_global_store); if (!__cfg_global_store) return QDF_STATUS_E_INVAL; QDF_BUG(__cfg_is_init); if (!__cfg_is_init) return QDF_STATUS_E_INVAL; QDF_BUG(psoc); if (!psoc) return QDF_STATUS_E_INVAL; QDF_BUG(path); if (!path) return QDF_STATUS_E_INVAL; psoc_ctx = cfg_psoc_get_ctx(psoc); QDF_BUG(psoc_ctx->store == __cfg_global_store); if (psoc_ctx->store != __cfg_global_store) return QDF_STATUS_SUCCESS; /* check if @path has been parsed before */ status = cfg_store_get(path, &store); if (QDF_IS_STATUS_ERROR(status)) { status = cfg_store_alloc(path, &store); if (QDF_IS_STATUS_ERROR(status)) return status; /* inherit global configuration */ qdf_mem_copy(&store->values, &__cfg_global_store->values, sizeof(store->values)); status = cfg_ini_parse_to_store(path, store); if (QDF_IS_STATUS_ERROR(status)) goto put_store; } psoc_ctx->store = store; cfg_store_put(__cfg_global_store); return QDF_STATUS_SUCCESS; put_store: cfg_store_put(store); return status; } qdf_export_symbol(cfg_psoc_parse);