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 /* fall through */ 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 /* fall through */ 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 §ion_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 §ion_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