xref: /wlan-dirver/qca-wifi-host-cmn/qdf/src/qdf_parse.c (revision 901120c066e139c7f8a2c8e4820561fdd83c67ef)
1 /*
2  * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "qdf_file.h"
21 #include "qdf_module.h"
22 #include "qdf_parse.h"
23 #include "qdf_status.h"
24 #include "qdf_str.h"
25 #include "qdf_trace.h"
26 #include "qdf_types.h"
27 
28 #ifdef WLAN_USE_CONFIG_PARAMS
29 #define QDF_SECTION_FOUND break
30 #else
31 #define QDF_SECTION_FOUND continue
32 #endif
33 
34 static QDF_STATUS qdf_ini_read_values(char **main_cursor,
35 				      char **read_key, char **read_value,
36 				      bool *section_item)
37 {
38 	char *cursor = *main_cursor;
39 
40 	/* foreach line */
41 	while (*cursor != '\0') {
42 		char *key = cursor;
43 		char *value = NULL;
44 		bool comment = false;
45 		bool eol = false;
46 
47 		/*
48 		 * Look for the end of the line, while noting any
49 		 * value ('=') or comment ('#') indicators
50 		 */
51 		while (!eol) {
52 			switch (*cursor) {
53 			case '\r':
54 			case '\n':
55 				*cursor = '\0';
56 				cursor++;
57 				fallthrough;
58 			case '\0':
59 				eol = true;
60 				break;
61 
62 			case '=':
63 				/*
64 				 * The first '=' is the value indicator.
65 				 * Subsequent '=' are valid value characters.
66 				 */
67 				if (!value && !comment) {
68 					value = cursor + 1;
69 					*cursor = '\0';
70 				}
71 
72 				cursor++;
73 				break;
74 
75 			case '#':
76 				/*
77 				 * We don't process comments, so we can null-
78 				 * terminate unconditionally here (unlike '=').
79 				 */
80 				comment = true;
81 				*cursor = '\0';
82 				fallthrough;
83 			default:
84 				cursor++;
85 				break;
86 			}
87 		}
88 
89 		key = qdf_str_trim(key);
90 		/*
91 		 * Ignoring comments, a valid ini line contains one of:
92 		 *	1) some 'key=value' config item
93 		 *	2) section header
94 		 *	3) a line containing whitespace
95 		 */
96 		if (value) {
97 			*read_key = key;
98 			*read_value = value;
99 			*section_item = 0;
100 			*main_cursor = cursor;
101 			return QDF_STATUS_SUCCESS;
102 		} else if (key[0] == '[') {
103 			qdf_size_t len = qdf_str_len(key);
104 
105 			if (key[len - 1] != ']') {
106 				qdf_err("Invalid *.ini syntax '%s'", key);
107 				return QDF_STATUS_E_INVAL;
108 			} else {
109 				key[len - 1] = '\0';
110 				*read_key = key + 1;
111 				*section_item = 1;
112 				*main_cursor = cursor;
113 				return QDF_STATUS_SUCCESS;
114 			}
115 		} else if (key[0] != '\0') {
116 			qdf_err("Invalid *.ini syntax '%s'", key);
117 			return QDF_STATUS_E_INVAL;
118 		}
119 
120 		/* skip remaining EoL characters */
121 		while (*cursor == '\n' || *cursor == '\r')
122 			cursor++;
123 	}
124 
125 	return QDF_STATUS_E_INVAL;
126 }
127 
128 QDF_STATUS qdf_ini_parse(const char *ini_path, void *context,
129 			 qdf_ini_item_cb item_cb, qdf_ini_section_cb section_cb)
130 {
131 	QDF_STATUS status;
132 	char *read_key;
133 	char *read_value;
134 	bool section_item;
135 	int ini_read_count = 0;
136 	char *fbuf;
137 	char *cursor;
138 
139 	if (qdf_str_eq(QDF_WIFI_MODULE_PARAMS_FILE, ini_path))
140 		status = qdf_module_param_file_read(ini_path, &fbuf);
141 	else
142 		status = qdf_file_read(ini_path, &fbuf);
143 	if (QDF_IS_STATUS_ERROR(status)) {
144 		qdf_err("Failed to read *.ini file @ %s", ini_path);
145 		return status;
146 	}
147 
148 	/* foreach line */
149 	cursor = fbuf;
150 
151 	while (qdf_ini_read_values(&cursor, &read_key, &read_value,
152 				   &section_item) == QDF_STATUS_SUCCESS) {
153 		if (!section_item) {
154 			status = item_cb(context, read_key, read_value);
155 			if (QDF_IS_STATUS_ERROR(status))
156 				break;
157 			else
158 				ini_read_count++;
159 		} else  {
160 			qdf_debug("Section started in global file");
161 		/* Currently AP Platforms supports and uses Sections,
162 		 * hence break the loop, sections will be parsed separately,
163 		 * in case of non AP platforms, sections are used as
164 		 * logical separators hence continue reading the values.
165 		 */
166 			QDF_SECTION_FOUND;
167 		}
168 	}
169 
170 	qdf_info("INI values read: %d", ini_read_count);
171 	if (ini_read_count != 0) {
172 		qdf_info("INI file parse successful");
173 		status = QDF_STATUS_SUCCESS;
174 	} else {
175 		qdf_info("INI file parse fail: invalid file format");
176 		status = QDF_STATUS_E_INVAL;
177 	}
178 
179 	if (qdf_str_eq(QDF_WIFI_MODULE_PARAMS_FILE, ini_path))
180 		qdf_module_param_file_free(fbuf);
181 	else
182 		qdf_file_buf_free(fbuf);
183 
184 	return status;
185 }
186 
187 qdf_export_symbol(qdf_ini_parse);
188 
189 QDF_STATUS qdf_ini_section_parse(const char *ini_path, void *context,
190 				 qdf_ini_item_cb item_cb,
191 				 const char *section_name)
192 {
193 	QDF_STATUS status;
194 	char *read_key;
195 	char *read_value;
196 	bool section_item;
197 	bool section_found = 0;
198 	bool section_complete = 0;
199 	int ini_read_count = 0;
200 	char *fbuf;
201 	char *cursor;
202 
203 	if (qdf_str_eq(QDF_WIFI_MODULE_PARAMS_FILE, ini_path))
204 		status = qdf_module_param_file_read(ini_path, &fbuf);
205 	else
206 		status = qdf_file_read(ini_path, &fbuf);
207 	if (QDF_IS_STATUS_ERROR(status)) {
208 		qdf_err("Failed to read *.ini file @ %s", ini_path);
209 		return status;
210 	}
211 
212 	/* foreach line */
213 	cursor = fbuf;
214 
215 	while (qdf_ini_read_values(&cursor, &read_key, &read_value,
216 				   &section_item) == QDF_STATUS_SUCCESS) {
217 		if (section_item) {
218 			if (qdf_str_cmp(read_key, section_name) == 0) {
219 				section_found = 1;
220 				section_complete = 0;
221 			} else {
222 				if (section_found == 1)
223 					section_complete = 1;
224 				section_found = 0;
225 			}
226 		} else if (section_found) {
227 			status = item_cb(context, read_key, read_value);
228 			if (QDF_IS_STATUS_ERROR(status))
229 				break;
230 			else
231 				ini_read_count++;
232 		} else if (section_complete) {
233 			break;
234 		}
235 	}
236 
237 	qdf_info("INI values parse successful read: %d from section %s",
238 		 ini_read_count, section_name);
239 
240 	if (ini_read_count != 0) {
241 		status = QDF_STATUS_SUCCESS;
242 	} else {
243 		qdf_debug("INI file parse fail: Section not found %s",
244 			  section_name);
245 		status = QDF_STATUS_SUCCESS;
246 	}
247 
248 	if (qdf_str_eq(QDF_WIFI_MODULE_PARAMS_FILE, ini_path))
249 		qdf_module_param_file_free(fbuf);
250 	else
251 		qdf_file_buf_free(fbuf);
252 
253 	return status;
254 }
255 
256 qdf_export_symbol(qdf_ini_section_parse);
257 
258