xref: /wlan-dirver/qca-wifi-host-cmn/cfg/src/cfg.c (revision 6ecd284e5a94a1c96e26d571dd47419ac305990d)
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