1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 /**
8  * @brief File containing API definitions for the
9  * FMAC IF Layer of the Wi-Fi driver.
10  */
11 
12 #include "host_rpu_umac_if.h"
13 #include "common/fmac_api_common.h"
14 #include "common/fmac_util.h"
15 #include "common/fmac_cmd_common.h"
16 #include "util.h"
17 
18 struct nrf_wifi_proc {
19 	const enum RPU_PROC_TYPE type;
20 	const char *name;
21 	bool is_patch_present;
22 };
23 
24 struct nrf_wifi_proc wifi_proc[] = {
25 	{RPU_PROC_TYPE_MCU_LMAC, "LMAC", true},
26 	{RPU_PROC_TYPE_MCU_UMAC, "UMAC", true},
27 };
28 
nrf_wifi_patch_version_compat(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,const unsigned int version)29 static int nrf_wifi_patch_version_compat(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
30 				const unsigned int version)
31 {
32 	unsigned int family, major, minor, patch;
33 
34 	family = (version >> 24) & 0xff;
35 	major = (version >> 16) & 0xff;
36 	minor = (version >> 8) & 0xff;
37 	patch = (version >> 0) & 0xff;
38 
39 	if (family != RPU_FAMILY) {
40 		nrf_wifi_osal_log_err("Incompatible RPU version: %d, expected: %d",
41 				      family, RPU_FAMILY);
42 		return -1;
43 	}
44 
45 	if (major != RPU_MAJOR_VERSION) {
46 		nrf_wifi_osal_log_err("Incompatible RPU major version: %d, expected: %d",
47 				      major, RPU_MAJOR_VERSION);
48 		return -1;
49 	}
50 
51 	/* TODO: Allow minor version to be different */
52 	if (minor != RPU_MINOR_VERSION) {
53 		nrf_wifi_osal_log_err("Incompatible RPU minor version: %d, expected: %d",
54 				      minor, RPU_MINOR_VERSION);
55 		return -1;
56 	}
57 
58 	/* TODO: Allow patch version to be different */
59 	if (patch != RPU_PATCH_VERSION) {
60 		nrf_wifi_osal_log_err("Incompatible RPU patch version: %d, expected: %d",
61 				      patch, RPU_PATCH_VERSION);
62 		return -1;
63 	}
64 
65 	return 0;
66 }
67 
nrf_wifi_patch_feature_flags_compat(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,const unsigned int feature_flags)68 static int nrf_wifi_patch_feature_flags_compat(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
69 				const unsigned int feature_flags)
70 {
71 	if (fmac_dev_ctx->op_mode == NRF_WIFI_OP_MODE_RT) {
72 		if (!(feature_flags & NRF70_FEAT_RADIO_TEST)) {
73 			nrf_wifi_osal_log_err("Radio test feature flag not set");
74 			return -1;
75 		}
76 	} else if (fmac_dev_ctx->op_mode == NRF_WIFI_OP_MODE_SYS) {
77 #ifdef NRF70_SCAN_ONLY
78 	if (!(feature_flags & NRF70_FEAT_SCAN_ONLY)) {
79 		nrf_wifi_osal_log_err("Scan only feature flag not set");
80 		return -1;
81 	}
82 #elif defined(NRF70_SYSTEM_MODE)
83 	if (!(feature_flags & NRF70_FEAT_SYSTEM_MODE)) {
84 		nrf_wifi_osal_log_err("System mode feature flag not set");
85 		return -1;
86 	}
87 #elif defined(NRF70_SYSTEM_WITH_RAW_MODES)
88 	if (!(feature_flags & NRF70_FEAT_SYSTEM_WITH_RAW_MODES)) {
89 		nrf_wifi_osal_log_err("System with raw modes feature flag not set");
90 		return -1;
91 	}
92 #else
93 	nrf_wifi_osal_log_err("Invalid feature flags: 0x%x or build configuration",
94 			      feature_flags);
95 #endif
96 	} else if (fmac_dev_ctx->op_mode == NRF_WIFI_OP_MODE_OFF_RAW_TX) {
97 		if (!(feature_flags & NRF70_FEAT_OFFLOADED_RAW_TX)) {
98 			nrf_wifi_osal_log_err("Offloaded raw tx feature flag not set");
99 			return -1;
100 		}
101 	} else {
102 		nrf_wifi_osal_log_err("Invalid op_mode: %d", fmac_dev_ctx->op_mode);
103 		return -1;
104 	}
105 
106 	return 0;
107 }
108 
109 
nrf_wifi_fmac_deinit(struct nrf_wifi_fmac_priv * fpriv)110 void nrf_wifi_fmac_deinit(struct nrf_wifi_fmac_priv *fpriv)
111 {
112 	nrf_wifi_hal_deinit(fpriv->hpriv);
113 
114 	nrf_wifi_osal_mem_free(fpriv);
115 }
116 
117 
nrf_wifi_fmac_dev_rem(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx)118 void nrf_wifi_fmac_dev_rem(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx)
119 {
120 	nrf_wifi_hal_dev_rem(fmac_dev_ctx->hal_dev_ctx);
121 
122 	nrf_wifi_osal_mem_free(fmac_dev_ctx);
123 }
124 
125 
nrf_wifi_validate_fw_header(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,struct nrf70_fw_image_info * info)126 enum nrf_wifi_status nrf_wifi_validate_fw_header(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
127 						 struct nrf70_fw_image_info *info)
128 {
129 
130 	nrf_wifi_osal_log_dbg("Signature: 0x%x", info->signature);
131 	nrf_wifi_osal_log_dbg("num_images: %d", info->num_images);
132 	nrf_wifi_osal_log_dbg("version: 0x%x", info->version);
133 	nrf_wifi_osal_log_dbg("feature_flags: %d", info->feature_flags);
134 
135 	if (info->signature != NRF_WIFI_PATCH_SIGNATURE) {
136 		nrf_wifi_osal_log_err("Invalid patch signature: 0x%x, expected: 0x%x",
137 				      info->signature, NRF_WIFI_PATCH_SIGNATURE);
138 		return NRF_WIFI_STATUS_FAIL;
139 	}
140 
141 	if (info->num_images != NRF_WIFI_PATCH_NUM_IMAGES) {
142 		nrf_wifi_osal_log_err("Invalid number of images, expected %d, got %d",
143 				      NRF_WIFI_PATCH_NUM_IMAGES, info->num_images);
144 		return NRF_WIFI_STATUS_FAIL;
145 	}
146 
147 	if (nrf_wifi_patch_version_compat(fmac_dev_ctx, info->version) != 0) {
148 		nrf_wifi_osal_log_err("Incompatible patch");
149 		return NRF_WIFI_STATUS_FAIL;
150 	}
151 
152 	if (nrf_wifi_patch_feature_flags_compat(fmac_dev_ctx, info->feature_flags) != 0) {
153 		nrf_wifi_osal_log_err("Incompatible feature flags");
154 		return NRF_WIFI_STATUS_FAIL;
155 	}
156 
157 	return NRF_WIFI_STATUS_SUCCESS;
158 }
159 
nrf_wifi_fmac_fw_parse(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,const void * fw_data,unsigned int fw_size,struct nrf_wifi_fmac_fw_info * fw_info)160 enum nrf_wifi_status nrf_wifi_fmac_fw_parse(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
161 					   const void *fw_data,
162 					   unsigned int fw_size,
163 					   struct nrf_wifi_fmac_fw_info *fw_info)
164 {
165 	struct nrf70_fw_image_info *info = (struct nrf70_fw_image_info *)fw_data;
166 	unsigned int offset;
167 	unsigned int image_id;
168 
169 	if (!fw_data || !fw_size || !fw_info) {
170 		nrf_wifi_osal_log_err("Invalid parameters");
171 		return NRF_WIFI_STATUS_FAIL;
172 	}
173 
174 	if (fw_size < sizeof(struct nrf70_fw_image_info)) {
175 		nrf_wifi_osal_log_err("Invalid fw_size: %d, minimum size: %d",
176 			fw_size, sizeof(struct nrf70_fw_image_info));
177 		return NRF_WIFI_STATUS_FAIL;
178 	}
179 
180 
181 	if (nrf_wifi_validate_fw_header(fmac_dev_ctx, info) != NRF_WIFI_STATUS_SUCCESS) {
182 		nrf_wifi_osal_log_err("Invalid fw header");
183 		return NRF_WIFI_STATUS_FAIL;
184 	}
185 
186 	offset = sizeof(struct nrf70_fw_image_info);
187 
188 	nrf_wifi_osal_log_dbg("====");
189 	for (image_id = 0; image_id < info->num_images; image_id++) {
190 		struct nrf70_fw_image *image =
191 			(struct nrf70_fw_image *)((char *)fw_data + offset);
192 		const void *data = (char *)fw_data + offset + sizeof(struct nrf70_fw_image);
193 
194 		if (offset + sizeof(struct nrf70_fw_image) + image->len > fw_size) {
195 			nrf_wifi_osal_log_err("Invalid fw_size: %d for image[%d] len: %d",
196 					      fw_size, image_id, image->len);
197 			return NRF_WIFI_STATUS_FAIL;
198 		}
199 
200 		nrf_wifi_osal_log_dbg("image[%d] type: %d", image_id, image->type);
201 		nrf_wifi_osal_log_dbg("image[%d] len: %d", image_id, image->len);
202 		nrf_wifi_osal_log_dbg("====");
203 
204 		switch (image_id) {
205 		case NRF70_IMAGE_LMAC_PRI:
206 			fw_info->lmac_patch_pri.data = data;
207 			fw_info->lmac_patch_pri.size = image->len;
208 			break;
209 		case NRF70_IMAGE_LMAC_SEC:
210 			fw_info->lmac_patch_sec.data = data;
211 			fw_info->lmac_patch_sec.size = image->len;
212 			break;
213 		case NRF70_IMAGE_UMAC_PRI:
214 			fw_info->umac_patch_pri.data = data;
215 			fw_info->umac_patch_pri.size = image->len;
216 			break;
217 		case NRF70_IMAGE_UMAC_SEC:
218 			fw_info->umac_patch_sec.data = data;
219 			fw_info->umac_patch_sec.size = image->len;
220 			break;
221 		default:
222 			nrf_wifi_osal_log_err("Invalid image id: %d", image_id);
223 			break;
224 		}
225 
226 		offset += sizeof(struct nrf70_fw_image) + image->len;
227 	}
228 
229 	return NRF_WIFI_STATUS_SUCCESS;
230 }
231 
nrf_wifi_fmac_fw_reset(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx)232 enum nrf_wifi_status nrf_wifi_fmac_fw_reset(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx)
233 {
234 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
235 	int i = 0;
236 
237 	for (i = 0; i < ARRAY_SIZE(wifi_proc); i++) {
238 		status = nrf_wifi_hal_proc_reset(fmac_dev_ctx->hal_dev_ctx,
239 						 wifi_proc[i].type);
240 
241 		if (status != NRF_WIFI_STATUS_SUCCESS) {
242 			nrf_wifi_osal_log_err("%s: %s processor reset failed\n",
243 					      __func__, wifi_proc[i].name);
244 			return NRF_WIFI_STATUS_FAIL;
245 		}
246 	}
247 
248 	return NRF_WIFI_STATUS_SUCCESS;
249 }
250 
nrf_wifi_fmac_fw_boot(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx)251 enum nrf_wifi_status nrf_wifi_fmac_fw_boot(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx)
252 {
253 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
254 	int i = 0;
255 
256 	for (i = 0; i < ARRAY_SIZE(wifi_proc); i++) {
257 		status = nrf_wifi_hal_fw_patch_boot(fmac_dev_ctx->hal_dev_ctx,
258 						    wifi_proc[i].type,
259 						    wifi_proc[i].is_patch_present);
260 
261 		if (status != NRF_WIFI_STATUS_SUCCESS) {
262 			nrf_wifi_osal_log_err("%s: %s processor ROM boot failed\n",
263 					      __func__, wifi_proc[i].name);
264 			return NRF_WIFI_STATUS_FAIL;
265 		}
266 
267 		status = nrf_wifi_hal_fw_chk_boot(fmac_dev_ctx->hal_dev_ctx,
268 						  wifi_proc[i].type);
269 
270 		if (status != NRF_WIFI_STATUS_SUCCESS) {
271 			nrf_wifi_osal_log_err("%s: %s processor ROM boot check failed\n",
272 					      __func__, wifi_proc[i].name);
273 			return NRF_WIFI_STATUS_FAIL;
274 		}
275 	}
276 
277 	return NRF_WIFI_STATUS_SUCCESS;
278 }
279 
280 
nrf_wifi_fmac_fw_chunk_load(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,enum RPU_PROC_TYPE rpu_proc,struct nrf_wifi_fmac_fw_chunk_info * fw_chunk)281 enum nrf_wifi_status nrf_wifi_fmac_fw_chunk_load(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
282 					   enum RPU_PROC_TYPE rpu_proc,
283 					   struct nrf_wifi_fmac_fw_chunk_info *fw_chunk)
284 {
285 	return hal_fw_patch_chunk_load(fmac_dev_ctx->hal_dev_ctx,
286 				       rpu_proc,
287 				       fw_chunk->dest_addr,
288 				       fw_chunk->data,
289 				       fw_chunk->size);
290 }
291 
nrf_wifi_fmac_fw_load(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,struct nrf_wifi_fmac_fw_info * fmac_fw)292 enum nrf_wifi_status nrf_wifi_fmac_fw_load(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
293 					   struct nrf_wifi_fmac_fw_info *fmac_fw)
294 {
295 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
296 
297 	status = nrf_wifi_fmac_fw_reset(fmac_dev_ctx);
298 	if (status != NRF_WIFI_STATUS_SUCCESS) {
299 		nrf_wifi_osal_log_err("%s: FW reset failed\n",
300 				      __func__);
301 		goto out;
302 	}
303 
304 	/* Load the UMAC patches if available */
305 	if (fmac_fw->umac_patch_pri.data && fmac_fw->umac_patch_pri.size &&
306 	    fmac_fw->umac_patch_sec.data && fmac_fw->umac_patch_sec.size) {
307 		status = nrf_wifi_hal_fw_patch_load(fmac_dev_ctx->hal_dev_ctx,
308 						    RPU_PROC_TYPE_MCU_UMAC,
309 						    fmac_fw->umac_patch_pri.data,
310 						    fmac_fw->umac_patch_pri.size,
311 						    fmac_fw->umac_patch_sec.data,
312 						    fmac_fw->umac_patch_sec.size);
313 
314 		if (status != NRF_WIFI_STATUS_SUCCESS) {
315 			nrf_wifi_osal_log_err("%s: UMAC patch load failed\n",
316 					      __func__);
317 			goto out;
318 		} else {
319 			nrf_wifi_osal_log_dbg("%s: UMAC patches loaded\n",
320 					      __func__);
321 		}
322 	} else {
323 		wifi_proc[1].is_patch_present = false;
324 	}
325 
326 	/* Load the LMAC patches if available */
327 	if (fmac_fw->lmac_patch_pri.data && fmac_fw->lmac_patch_pri.size &&
328 	    fmac_fw->lmac_patch_sec.data && fmac_fw->lmac_patch_sec.size) {
329 		status = nrf_wifi_hal_fw_patch_load(fmac_dev_ctx->hal_dev_ctx,
330 						    RPU_PROC_TYPE_MCU_LMAC,
331 						    fmac_fw->lmac_patch_pri.data,
332 						    fmac_fw->lmac_patch_pri.size,
333 						    fmac_fw->lmac_patch_sec.data,
334 						    fmac_fw->lmac_patch_sec.size);
335 
336 		if (status != NRF_WIFI_STATUS_SUCCESS) {
337 			nrf_wifi_osal_log_err("%s: LMAC patch load failed\n",
338 					      __func__);
339 			goto out;
340 		} else {
341 			nrf_wifi_osal_log_dbg("%s: LMAC patches loaded\n",
342 					      __func__);
343 		}
344 	} else {
345 		wifi_proc[0].is_patch_present = false;
346 	}
347 
348 	status = nrf_wifi_fmac_fw_boot(fmac_dev_ctx);
349 	if (status != NRF_WIFI_STATUS_SUCCESS) {
350 		nrf_wifi_osal_log_err("%s: FW boot failed\n",
351 				      __func__);
352 		goto out;
353 	}
354 
355 	fmac_dev_ctx->fw_boot_done = true;
356 
357 out:
358 	return status;
359 }
360 
361 
nrf_wifi_fmac_ver_get(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,unsigned int * fw_ver)362 enum nrf_wifi_status nrf_wifi_fmac_ver_get(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
363 					  unsigned int *fw_ver)
364 {
365 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
366 
367 	status = hal_rpu_mem_read(fmac_dev_ctx->hal_dev_ctx,
368 				  fw_ver,
369 				  RPU_MEM_UMAC_VER,
370 				  sizeof(*fw_ver));
371 
372 	if (status != NRF_WIFI_STATUS_SUCCESS) {
373 		nrf_wifi_osal_log_err("%s: Unable to read UMAC ver",
374 				      __func__);
375 		goto out;
376 	}
377 
378 out:
379 	return status;
380 }
381 
382 
nrf_wifi_fmac_otp_mac_addr_get(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,unsigned char vif_idx,unsigned char * mac_addr)383 enum nrf_wifi_status nrf_wifi_fmac_otp_mac_addr_get(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
384 						    unsigned char vif_idx,
385 						    unsigned char *mac_addr)
386 {
387 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
388 	struct nrf_wifi_fmac_otp_info otp_info;
389 	unsigned char *otp_mac_addr = NULL;
390 	unsigned int otp_mac_addr_flag_mask = 0;
391 
392 	if (!fmac_dev_ctx || !mac_addr || (vif_idx >= MAX_NUM_VIFS)) {
393 		nrf_wifi_osal_log_err("%s: Invalid parameters",
394 				      __func__);
395 		goto out;
396 	}
397 
398 	nrf_wifi_osal_mem_set(&otp_info,
399 			      0xFF,
400 			      sizeof(otp_info));
401 
402 	status = nrf_wifi_hal_otp_info_get(fmac_dev_ctx->hal_dev_ctx,
403 					   &otp_info.info,
404 					   &otp_info.flags);
405 
406 	if (status != NRF_WIFI_STATUS_SUCCESS) {
407 		nrf_wifi_osal_log_err("%s: Fetching of RPU OTP information failed",
408 				      __func__);
409 		goto out;
410 	}
411 
412 	if (vif_idx == 0) {
413 		otp_mac_addr = (unsigned char *)otp_info.info.mac_address0;
414 		otp_mac_addr_flag_mask = (~MAC0_ADDR_FLAG_MASK);
415 
416 	} else if (vif_idx == 1) {
417 		otp_mac_addr = (unsigned char *)otp_info.info.mac_address1;
418 		otp_mac_addr_flag_mask = (~MAC1_ADDR_FLAG_MASK);
419 	}
420 
421 	/* Check if a valid MAC address has been programmed in the OTP */
422 
423 	if (otp_info.flags & otp_mac_addr_flag_mask) {
424 		nrf_wifi_osal_log_info("%s: MAC addr not programmed in OTP",
425 				       __func__);
426 
427 	} else {
428 		nrf_wifi_osal_mem_cpy(mac_addr,
429 				      otp_mac_addr,
430 				      NRF_WIFI_ETH_ADDR_LEN);
431 
432 		if (!nrf_wifi_utils_is_mac_addr_valid((const char *)mac_addr)) {
433 			nrf_wifi_osal_log_info("%s:  Invalid OTP MA: %02X%02X%02X%02X%02X%02X",
434 					       __func__,
435 					       (*(mac_addr + 0)),
436 					       (*(mac_addr + 1)),
437 					       (*(mac_addr + 2)),
438 					       (*(mac_addr + 3)),
439 					       (*(mac_addr + 4)),
440 					       (*(mac_addr + 5)));
441 
442 		}
443 	}
444 out:
445 	return status;
446 }
447 
nrf_wifi_fmac_get_reg(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,struct nrf_wifi_fmac_reg_info * reg_info)448 enum nrf_wifi_status nrf_wifi_fmac_get_reg(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
449 					   struct nrf_wifi_fmac_reg_info *reg_info)
450 {
451 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
452 	struct nrf_wifi_umac_cmd_get_reg *get_reg_cmd = NULL;
453 	unsigned int count = 0;
454 
455 	if (!fmac_dev_ctx || !reg_info) {
456 		nrf_wifi_osal_log_err("%s: Invalid parameters",
457 				      __func__);
458 		goto err;
459 	}
460 
461 	nrf_wifi_osal_log_dbg("%s: Get regulatory information", __func__);
462 
463 	get_reg_cmd = nrf_wifi_osal_mem_zalloc(sizeof(*get_reg_cmd));
464 
465 	if (!get_reg_cmd) {
466 		nrf_wifi_osal_log_err("%s: Unable to allocate memory",
467 				      __func__);
468 		goto err;
469 	}
470 
471 	get_reg_cmd->umac_hdr.cmd_evnt = NRF_WIFI_UMAC_CMD_GET_REG;
472 	get_reg_cmd->umac_hdr.ids.valid_fields = 0;
473 
474 	fmac_dev_ctx->alpha2_valid = false;
475 	fmac_dev_ctx->reg_chan_info = reg_info->reg_chan_info;
476 
477 	status = umac_cmd_cfg(fmac_dev_ctx,
478 			      get_reg_cmd,
479 			      sizeof(*get_reg_cmd));
480 
481 	if (status != NRF_WIFI_STATUS_SUCCESS) {
482 		nrf_wifi_osal_log_err("%s: Failed to get regulatory information",	__func__);
483 		goto err;
484 	}
485 
486 	do {
487 		nrf_wifi_osal_sleep_ms(100);
488 	} while (count++ < 100 && !fmac_dev_ctx->alpha2_valid);
489 
490 	if (!fmac_dev_ctx->alpha2_valid) {
491 		nrf_wifi_osal_log_err("%s: Failed to get regulatory information",
492 				      __func__);
493 		goto err;
494 	}
495 
496 	nrf_wifi_osal_mem_cpy(reg_info->alpha2,
497 			      fmac_dev_ctx->alpha2,
498 			      sizeof(reg_info->alpha2));
499 
500 	reg_info->reg_chan_count = fmac_dev_ctx->reg_chan_count;
501 
502 	status = NRF_WIFI_STATUS_SUCCESS;
503 err:
504 	if (get_reg_cmd) {
505 		nrf_wifi_osal_mem_free(get_reg_cmd);
506 	}
507 	return status;
508 }
509 
nrf_wifi_fmac_stats_reset(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx)510 enum nrf_wifi_status nrf_wifi_fmac_stats_reset(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx)
511 {
512 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
513 	unsigned char count = 0;
514 
515 	status = umac_cmd_prog_stats_reset(fmac_dev_ctx);
516 	if (status != NRF_WIFI_STATUS_SUCCESS) {
517 		goto out;
518 	}
519 
520 	do {
521 		nrf_wifi_osal_sleep_ms(1);
522 	} while ((fmac_dev_ctx->stats_req == true) &&
523 		 (count++ < NRF_WIFI_FMAC_STATS_RECV_TIMEOUT));
524 
525 	if (count == NRF_WIFI_FMAC_STATS_RECV_TIMEOUT) {
526 		nrf_wifi_osal_log_err("%s: Timed out",
527 				      __func__);
528 		goto out;
529 	}
530 
531 	status = NRF_WIFI_STATUS_SUCCESS;
532 
533 out:
534 	return status;
535 }
536 
537 
nrf_wifi_fmac_conf_srcoex(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,void * cmd,unsigned int cmd_len)538 enum nrf_wifi_status nrf_wifi_fmac_conf_srcoex(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
539 					       void *cmd, unsigned int cmd_len)
540 {
541 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
542 
543 	status = umac_cmd_srcoex(fmac_dev_ctx, cmd, cmd_len);
544 
545 	return status;
546 }
547 
nrf_wifi_fmac_set_reg(struct nrf_wifi_fmac_dev_ctx * fmac_dev_ctx,struct nrf_wifi_fmac_reg_info * reg_info)548 enum nrf_wifi_status nrf_wifi_fmac_set_reg(struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx,
549 					   struct nrf_wifi_fmac_reg_info *reg_info)
550 {
551 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
552 	struct nrf_wifi_cmd_req_set_reg *set_reg_cmd = NULL;
553 	unsigned int count = 0, max_count = NRF_WIFI_FMAC_REG_SET_TIMEOUT_MS / 20;
554 	enum nrf_wifi_reg_initiator exp_initiator = NRF_WIFI_REGDOM_SET_BY_USER;
555 	enum nrf_wifi_reg_type exp_reg_type = NRF_WIFI_REGDOM_TYPE_COUNTRY;
556 	char exp_alpha2[NRF_WIFI_COUNTRY_CODE_LEN] = {0};
557 	struct nrf_wifi_fmac_reg_info cur_reg_info = {0};
558 	struct nrf_wifi_event_regulatory_change *reg_change = NULL;
559 
560 	if (!fmac_dev_ctx || !reg_info) {
561 		nrf_wifi_osal_log_err("%s: Invalid parameters",
562 				      __func__);
563 		goto out;
564 	}
565 
566 	nrf_wifi_osal_log_dbg("%s: Setting regulatory information: %c%c", __func__,
567 	                      reg_info->alpha2[0],
568 	                      reg_info->alpha2[1]);
569 
570 	/* No change event from UMAC for same regd */
571 	status = nrf_wifi_fmac_get_reg(fmac_dev_ctx, &cur_reg_info);
572 	if (status != NRF_WIFI_STATUS_SUCCESS) {
573 		nrf_wifi_osal_log_err("%s: Failed to get current regulatory information",
574 				      __func__);
575 		goto out;
576 	}
577 
578 	if (nrf_wifi_osal_mem_cmp(cur_reg_info.alpha2,
579 				  reg_info->alpha2,
580 				  NRF_WIFI_COUNTRY_CODE_LEN) == 0) {
581 		nrf_wifi_osal_log_dbg("%s: Regulatory domain already set to %c%c",
582 				      __func__,
583 				      reg_info->alpha2[0],
584 				      reg_info->alpha2[1]);
585 		status = NRF_WIFI_STATUS_SUCCESS;
586 		goto out;
587 	}
588 
589 	set_reg_cmd = nrf_wifi_osal_mem_zalloc(sizeof(*set_reg_cmd));
590 
591 	if (!set_reg_cmd) {
592 		nrf_wifi_osal_log_err("%s: Unable to allocate memory",
593 				      __func__);
594 		goto out;
595 	}
596 
597 	set_reg_cmd->umac_hdr.cmd_evnt = NRF_WIFI_UMAC_CMD_REQ_SET_REG;
598 	set_reg_cmd->umac_hdr.ids.valid_fields = 0;
599 
600 	nrf_wifi_osal_mem_cpy(set_reg_cmd->nrf_wifi_alpha2,
601 			      reg_info->alpha2,
602 			      NRF_WIFI_COUNTRY_CODE_LEN);
603 
604 	exp_alpha2[0] = reg_info->alpha2[0];
605 	exp_alpha2[1] = reg_info->alpha2[1];
606 
607 	if (reg_info->alpha2[0] == '0' && reg_info->alpha2[1] == '0') {
608 		exp_reg_type = NRF_WIFI_REGDOM_TYPE_WORLD;
609 	}
610 
611 	set_reg_cmd->valid_fields = NRF_WIFI_CMD_REQ_SET_REG_ALPHA2_VALID;
612 
613 	/* New feature in rev B patch */
614 	if (reg_info->force) {
615 		set_reg_cmd->valid_fields |= NRF_WIFI_CMD_REQ_SET_REG_USER_REG_FORCE;
616 	}
617 
618 	fmac_dev_ctx->reg_set_status = false;
619 	fmac_dev_ctx->waiting_for_reg_event = true;
620 
621 	status = umac_cmd_cfg(fmac_dev_ctx,
622 			      set_reg_cmd,
623 			      sizeof(*set_reg_cmd));
624 	if (status != NRF_WIFI_STATUS_SUCCESS) {
625 		nrf_wifi_osal_log_err("%s: Failed to set regulatory information",
626 				      __func__);
627 		goto out;
628 	}
629 
630 	fmac_dev_ctx->reg_set_status = false;
631 	nrf_wifi_osal_log_dbg("%s: Waiting for regulatory domain change event", __func__);
632 	while (!fmac_dev_ctx->reg_set_status && count++ <= max_count) {
633 		nrf_wifi_osal_sleep_ms(100);
634 	}
635 
636 	if (!fmac_dev_ctx->reg_set_status) {
637 		nrf_wifi_osal_log_err("%s: Failed to set regulatory information",
638 				      __func__);
639 		status = NRF_WIFI_STATUS_FAIL;
640 		goto out;
641 	}
642 
643 	fmac_dev_ctx->waiting_for_reg_event = false;
644 	reg_change = fmac_dev_ctx->reg_change;
645 
646 	if (reg_change->intr != exp_initiator) {
647 		nrf_wifi_osal_log_err("%s: Non-user initiated reg domain change: exp: %d, got: %d",
648 				      __func__,
649 				      exp_initiator,
650 				      reg_change->intr);
651 		status = NRF_WIFI_STATUS_FAIL;
652 		goto out;
653 	}
654 
655 	if (reg_change->regulatory_type != exp_reg_type) {
656 		nrf_wifi_osal_log_err("%s: Unexpected reg domain change: exp: %d, got: %d",
657 				      __func__,
658 				      exp_reg_type,
659 				      reg_change->regulatory_type);
660 		status = NRF_WIFI_STATUS_FAIL;
661 		goto out;
662 	}
663 
664 	if ((reg_change->regulatory_type == NRF_WIFI_REGDOM_TYPE_COUNTRY) &&
665 		 nrf_wifi_osal_mem_cmp(reg_change->nrf_wifi_alpha2,
666 				       exp_alpha2,
667 				       NRF_WIFI_COUNTRY_CODE_LEN) != 0) {
668 		nrf_wifi_osal_log_err("%s: Unexpected alpha2 reg domain change: "
669 				      "exp: %c%c, got: %c%c",
670 				      __func__,
671 				      exp_alpha2[0],
672 				      exp_alpha2[1],
673 				      reg_change->nrf_wifi_alpha2[0],
674 				      reg_change->nrf_wifi_alpha2[1]);
675 		status = NRF_WIFI_STATUS_FAIL;
676 		goto out;
677 	}
678 
679 out:
680 	if (set_reg_cmd) {
681 		nrf_wifi_osal_mem_free(set_reg_cmd);
682 	}
683 
684 	if (reg_change) {
685 		nrf_wifi_osal_mem_free(reg_change);
686 		fmac_dev_ctx->reg_change = NULL;
687 	}
688 
689 	return status;
690 }