1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @brief File containing API definitions for the Offloaded raw TX feature.
9  */
10 
11 #include <ctype.h>
12 #include <stdlib.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/drivers/wifi/nrf_wifi/off_raw_tx/off_raw_tx_api.h>
16 #include <off_raw_tx.h>
17 #include <offload_raw_tx/fmac_api.h>
18 
19 #define DT_DRV_COMPAT nordic_wlan
20 LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL);
21 
22 struct nrf_wifi_off_raw_tx_drv_priv off_raw_tx_drv_priv;
23 extern const struct nrf_wifi_osal_ops nrf_wifi_os_zep_ops;
24 
25 static const int valid_data_rates[] = { 1, 2, 55, 11, 6, 9, 12, 18, 24, 36, 48, 54,
26 				  0, 1, 2, 3, 4, 5, 6, 7, -1 };
27 
28 /* DTS uses 1dBm as the unit for TX power, while the RPU uses 0.25dBm */
29 #define MAX_TX_PWR(label) DT_PROP(DT_NODELABEL(nrf70), label) * 4
configure_tx_pwr_settings(struct nrf_wifi_tx_pwr_ctrl_params * ctrl_params,struct nrf_wifi_tx_pwr_ceil_params * ceil_params)30 static void configure_tx_pwr_settings(struct nrf_wifi_tx_pwr_ctrl_params *ctrl_params,
31 				      struct nrf_wifi_tx_pwr_ceil_params *ceil_params)
32 {
33 	ctrl_params->ant_gain_2g = CONFIG_NRF70_ANT_GAIN_2G;
34 	ctrl_params->ant_gain_5g_band1 = CONFIG_NRF70_ANT_GAIN_5G_BAND1;
35 	ctrl_params->ant_gain_5g_band2 = CONFIG_NRF70_ANT_GAIN_5G_BAND2;
36 	ctrl_params->ant_gain_5g_band3 = CONFIG_NRF70_ANT_GAIN_5G_BAND3;
37 	ctrl_params->band_edge_2g_lo_dss = CONFIG_NRF70_BAND_2G_LOWER_EDGE_BACKOFF_DSSS;
38 	ctrl_params->band_edge_2g_lo_ht = CONFIG_NRF70_BAND_2G_LOWER_EDGE_BACKOFF_HT;
39 	ctrl_params->band_edge_2g_lo_he = CONFIG_NRF70_BAND_2G_LOWER_EDGE_BACKOFF_HE;
40 	ctrl_params->band_edge_2g_hi_dsss = CONFIG_NRF70_BAND_2G_UPPER_EDGE_BACKOFF_DSSS;
41 	ctrl_params->band_edge_2g_hi_ht = CONFIG_NRF70_BAND_2G_UPPER_EDGE_BACKOFF_HT;
42 	ctrl_params->band_edge_2g_hi_he = CONFIG_NRF70_BAND_2G_UPPER_EDGE_BACKOFF_HE;
43 	ctrl_params->band_edge_5g_unii_1_lo_ht =
44 		CONFIG_NRF70_BAND_UNII_1_LOWER_EDGE_BACKOFF_HT;
45 	ctrl_params->band_edge_5g_unii_1_lo_he =
46 		CONFIG_NRF70_BAND_UNII_1_LOWER_EDGE_BACKOFF_HE;
47 	ctrl_params->band_edge_5g_unii_1_hi_ht =
48 		CONFIG_NRF70_BAND_UNII_1_UPPER_EDGE_BACKOFF_HT;
49 	ctrl_params->band_edge_5g_unii_1_hi_he =
50 		CONFIG_NRF70_BAND_UNII_1_UPPER_EDGE_BACKOFF_HE;
51 	ctrl_params->band_edge_5g_unii_2a_lo_ht =
52 		CONFIG_NRF70_BAND_UNII_2A_LOWER_EDGE_BACKOFF_HT;
53 	ctrl_params->band_edge_5g_unii_2a_lo_he =
54 		CONFIG_NRF70_BAND_UNII_2A_LOWER_EDGE_BACKOFF_HE;
55 	ctrl_params->band_edge_5g_unii_2a_hi_ht =
56 		CONFIG_NRF70_BAND_UNII_2A_UPPER_EDGE_BACKOFF_HT;
57 	ctrl_params->band_edge_5g_unii_2a_hi_he =
58 		CONFIG_NRF70_BAND_UNII_2A_UPPER_EDGE_BACKOFF_HE;
59 	ctrl_params->band_edge_5g_unii_2c_lo_ht =
60 		CONFIG_NRF70_BAND_UNII_2C_LOWER_EDGE_BACKOFF_HT;
61 	ctrl_params->band_edge_5g_unii_2c_lo_he =
62 		CONFIG_NRF70_BAND_UNII_2C_LOWER_EDGE_BACKOFF_HE;
63 	ctrl_params->band_edge_5g_unii_2c_hi_ht =
64 		CONFIG_NRF70_BAND_UNII_2C_UPPER_EDGE_BACKOFF_HT;
65 	ctrl_params->band_edge_5g_unii_2c_hi_he =
66 		CONFIG_NRF70_BAND_UNII_2C_UPPER_EDGE_BACKOFF_HE;
67 	ctrl_params->band_edge_5g_unii_3_lo_ht =
68 		CONFIG_NRF70_BAND_UNII_3_LOWER_EDGE_BACKOFF_HT;
69 	ctrl_params->band_edge_5g_unii_3_lo_he =
70 		CONFIG_NRF70_BAND_UNII_3_LOWER_EDGE_BACKOFF_HE;
71 	ctrl_params->band_edge_5g_unii_3_hi_ht =
72 		CONFIG_NRF70_BAND_UNII_3_UPPER_EDGE_BACKOFF_HT;
73 	ctrl_params->band_edge_5g_unii_3_hi_he =
74 		CONFIG_NRF70_BAND_UNII_3_UPPER_EDGE_BACKOFF_HE;
75 	ctrl_params->band_edge_5g_unii_4_lo_ht =
76 		CONFIG_NRF70_BAND_UNII_4_LOWER_EDGE_BACKOFF_HT;
77 	ctrl_params->band_edge_5g_unii_4_lo_he =
78 		CONFIG_NRF70_BAND_UNII_4_LOWER_EDGE_BACKOFF_HE;
79 	ctrl_params->band_edge_5g_unii_4_hi_ht =
80 		CONFIG_NRF70_BAND_UNII_4_UPPER_EDGE_BACKOFF_HT;
81 	ctrl_params->band_edge_5g_unii_4_hi_he =
82 		CONFIG_NRF70_BAND_UNII_4_UPPER_EDGE_BACKOFF_HE;
83 	ceil_params->max_pwr_2g_dsss = MAX_TX_PWR(wifi_max_tx_pwr_2g_dsss);
84 	ceil_params->max_pwr_2g_mcs7 = MAX_TX_PWR(wifi_max_tx_pwr_2g_mcs7);
85 	ceil_params->max_pwr_2g_mcs0 = MAX_TX_PWR(wifi_max_tx_pwr_2g_mcs0);
86 #ifndef CONFIG_NRF70_2_4G_ONLY
87 	ceil_params->max_pwr_5g_low_mcs7 = MAX_TX_PWR(wifi_max_tx_pwr_5g_low_mcs7);
88 	ceil_params->max_pwr_5g_mid_mcs7 = MAX_TX_PWR(wifi_max_tx_pwr_5g_mid_mcs7);
89 	ceil_params->max_pwr_5g_high_mcs7 = MAX_TX_PWR(wifi_max_tx_pwr_5g_high_mcs7);
90 	ceil_params->max_pwr_5g_low_mcs0 = MAX_TX_PWR(wifi_max_tx_pwr_5g_low_mcs0);
91 	ceil_params->max_pwr_5g_mid_mcs0 = MAX_TX_PWR(wifi_max_tx_pwr_5g_mid_mcs0);
92 	ceil_params->max_pwr_5g_high_mcs0 = MAX_TX_PWR(wifi_max_tx_pwr_5g_high_mcs0);
93 #endif /* CONFIG_NRF70_2_4G_ONLY */
94 }
95 
configure_board_dep_params(struct nrf_wifi_board_params * board_params)96 static void configure_board_dep_params(struct nrf_wifi_board_params *board_params)
97 {
98 	board_params->pcb_loss_2g = CONFIG_NRF70_PCB_LOSS_2G;
99 #ifndef CONFIG_NRF70_2_4G_ONLY
100 	board_params->pcb_loss_5g_band1 = CONFIG_NRF70_PCB_LOSS_5G_BAND1;
101 	board_params->pcb_loss_5g_band2 = CONFIG_NRF70_PCB_LOSS_5G_BAND2;
102 	board_params->pcb_loss_5g_band3 = CONFIG_NRF70_PCB_LOSS_5G_BAND3;
103 #endif /* CONFIG_NRF70_2_4G_ONLY */
104 }
105 
106 #ifdef CONFIG_WIFI_FIXED_MAC_ADDRESS_ENABLED
bytes_from_str(uint8_t * buf,int buf_len,const char * src)107 static int bytes_from_str(uint8_t *buf, int buf_len, const char *src)
108 {
109 	size_t i;
110 	size_t src_len = strlen(src);
111 	char *endptr;
112 
113 	for (i = 0U; i < src_len; i++) {
114 		if (!isxdigit((unsigned char)src[i]) &&
115 		    src[i] != ':') {
116 			return -EINVAL;
117 		}
118 	}
119 
120 	(void)memset(buf, 0, buf_len);
121 
122 	for (i = 0U; i < (size_t)buf_len; i++) {
123 		buf[i] = (uint8_t)strtol(src, &endptr, 16);
124 		src = ++endptr;
125 	}
126 
127 	return 0;
128 }
129 #endif /* CONFIG_WIFI_FIXED_MAC_ADDRESS_ENABLED */
130 
nrf70_off_raw_tx_init(uint8_t * mac_addr,unsigned char * country_code)131 int nrf70_off_raw_tx_init(uint8_t *mac_addr, unsigned char *country_code)
132 {
133 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
134 	struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
135 	void *rpu_ctx = NULL;
136 	struct nrf_wifi_tx_pwr_ctrl_params ctrl_params;
137 	struct nrf_wifi_tx_pwr_ceil_params ceil_params;
138 	struct nrf_wifi_board_params board_params;
139 	unsigned int fw_ver = 0;
140 	k_spinlock_key_t key;
141 
142 	/* The OSAL layer needs to be initialized before any other initialization
143 	 * so that other layers (like FW IF,HW IF etc) have access to OS ops
144 	 */
145 	nrf_wifi_osal_init(&nrf_wifi_os_zep_ops);
146 
147 	key = k_spin_lock(&off_raw_tx_drv_priv.lock);
148 
149 	off_raw_tx_drv_priv.fmac_priv = nrf_wifi_off_raw_tx_fmac_init();
150 
151 	if (off_raw_tx_drv_priv.fmac_priv == NULL) {
152 		LOG_ERR("%s: Failed to initialize nRF70 driver",
153 			__func__);
154 		goto err;
155 	}
156 
157 	rpu_ctx_zep = &off_raw_tx_drv_priv.rpu_ctx_zep;
158 
159 	rpu_ctx_zep->drv_priv_zep = &off_raw_tx_drv_priv;
160 
161 	rpu_ctx = nrf_wifi_off_raw_tx_fmac_dev_add(off_raw_tx_drv_priv.fmac_priv,
162 						   rpu_ctx_zep);
163 	if (!rpu_ctx) {
164 		LOG_ERR("%s: Failed to add nRF70 device", __func__);
165 		rpu_ctx_zep = NULL;
166 		goto err;
167 	}
168 
169 	rpu_ctx_zep->rpu_ctx = rpu_ctx;
170 
171 	status = nrf_wifi_fw_load(rpu_ctx);
172 	if (status != NRF_WIFI_STATUS_SUCCESS) {
173 		LOG_ERR("%s: Failed to load the nRF70 firmware patch", __func__);
174 		goto err;
175 	}
176 
177 	status = nrf_wifi_fmac_ver_get(rpu_ctx,
178 				       &fw_ver);
179 	if (status != NRF_WIFI_STATUS_SUCCESS) {
180 		LOG_ERR("%s: Failed to read the nRF70 firmware version", __func__);
181 		goto err;
182 	}
183 
184 	LOG_DBG("nRF70 firmware (v%d.%d.%d.%d) booted successfully",
185 		NRF_WIFI_UMAC_VER(fw_ver),
186 		NRF_WIFI_UMAC_VER_MAJ(fw_ver),
187 		NRF_WIFI_UMAC_VER_MIN(fw_ver),
188 		NRF_WIFI_UMAC_VER_EXTRA(fw_ver));
189 
190 	memset(&ctrl_params, 0, sizeof(ctrl_params));
191 	memset(&ceil_params, 0, sizeof(ceil_params));
192 
193 	configure_tx_pwr_settings(&ctrl_params,
194 				  &ceil_params);
195 
196 	memset(&board_params, 0, sizeof(board_params));
197 
198 	configure_board_dep_params(&board_params);
199 
200 	status = nrf_wifi_off_raw_tx_fmac_dev_init(rpu_ctx_zep->rpu_ctx,
201 #ifdef CONFIG_NRF_WIFI_LOW_POWER
202 						   HW_SLEEP_ENABLE,
203 #endif /* CONFIG_NRF_WIFI_LOW_POWER */
204 						   NRF_WIFI_DEF_PHY_CALIB,
205 						   CONFIG_NRF_WIFI_OP_BAND,
206 						   IS_ENABLED(CONFIG_NRF_WIFI_BEAMFORMING),
207 						   &ctrl_params,
208 						   &ceil_params,
209 						   &board_params,
210 						   country_code);
211 	if (status != NRF_WIFI_STATUS_SUCCESS) {
212 		LOG_ERR("%s: nRF70 firmware initialization failed", __func__);
213 		goto err;
214 	}
215 
216 	if (mac_addr) {
217 		memcpy(rpu_ctx_zep->mac_addr, mac_addr, 6);
218 	} else {
219 #ifdef CONFIG_WIFI_FIXED_MAC_ADDRESS_ENABLED
220 		int ret = -1;
221 
222 		ret = bytes_from_str(rpu_ctx_zep->mac_addr,
223 				     6,
224 				     CONFIG_WIFI_FIXED_MAC_ADDRESS);
225 		if (ret < 0) {
226 			LOG_ERR("%s: Failed to parse MAC address: %s",
227 				__func__,
228 				CONFIG_WIFI_FIXED_MAC_ADDRESS);
229 			goto err;
230 		}
231 #elif CONFIG_WIFI_OTP_MAC_ADDRESS
232 		status = nrf_wifi_fmac_otp_mac_addr_get(off_raw_tx_drv_priv.rpu_ctx_zep.rpu_ctx,
233 							0,
234 							rpu_ctx_zep->mac_addr);
235 		if (status != NRF_WIFI_STATUS_SUCCESS) {
236 			LOG_ERR("%s: Fetching of MAC address from OTP failed",
237 				__func__);
238 			goto err;
239 		}
240 #endif /* CONFIG_WIFI_FIXED_MAC_ADDRESS_ENABLED */
241 
242 		if (!nrf_wifi_utils_is_mac_addr_valid(rpu_ctx_zep->mac_addr)) {
243 			LOG_ERR("%s: Invalid MAC address: %02X:%02X:%02X:%02X:%02X:%02X",
244 				__func__,
245 				rpu_ctx_zep->mac_addr[0],
246 				rpu_ctx_zep->mac_addr[1],
247 				rpu_ctx_zep->mac_addr[2],
248 				rpu_ctx_zep->mac_addr[3],
249 				rpu_ctx_zep->mac_addr[4],
250 				rpu_ctx_zep->mac_addr[5]);
251 			goto err;
252 		}
253 	}
254 
255 	k_spin_unlock(&off_raw_tx_drv_priv.lock, key);
256 
257 	return 0;
258 err:
259 	if (rpu_ctx) {
260 		nrf_wifi_fmac_dev_rem(rpu_ctx);
261 		rpu_ctx_zep->rpu_ctx = NULL;
262 	}
263 
264 	k_spin_unlock(&off_raw_tx_drv_priv.lock, key);
265 	nrf70_off_raw_tx_deinit();
266 	return -1;
267 }
268 
269 
nrf70_off_raw_tx_deinit(void)270 void nrf70_off_raw_tx_deinit(void)
271 {
272 	k_spinlock_key_t key;
273 
274 	key = k_spin_lock(&off_raw_tx_drv_priv.lock);
275 
276 	if (!off_raw_tx_drv_priv.fmac_priv) {
277 		k_spin_unlock(&off_raw_tx_drv_priv.lock, key);
278 		return;
279 	}
280 
281 	nrf_wifi_fmac_deinit(off_raw_tx_drv_priv.fmac_priv);
282 
283 	k_spin_unlock(&off_raw_tx_drv_priv.lock, key);
284 }
285 
validate_rate(enum nrf_wifi_off_raw_tx_tput_mode tput_mode,enum nrf_wifi_off_raw_tx_rate rate)286 static bool validate_rate(enum nrf_wifi_off_raw_tx_tput_mode tput_mode,
287 			enum nrf_wifi_off_raw_tx_rate rate)
288 {
289 	if (tput_mode == TPUT_MODE_LEGACY) {
290 		if (rate > RATE_54M) {
291 			return false;
292 		}
293 	} else {
294 		if (rate <= RATE_54M) {
295 			return false;
296 		}
297 	}
298 
299 	return true;
300 }
301 
nrf70_off_raw_tx_conf_update(struct nrf_wifi_off_raw_tx_conf * conf)302 int nrf70_off_raw_tx_conf_update(struct nrf_wifi_off_raw_tx_conf *conf)
303 {
304 	int ret = -1;
305 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
306 	struct nrf_wifi_offload_ctrl_params *off_ctrl_params = NULL;
307 	struct nrf_wifi_offload_tx_ctrl *off_tx_params = NULL;
308 	struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
309 	k_spinlock_key_t key;
310 
311 	if (!conf) {
312 		LOG_ERR("%s: Config params is NULL", __func__);
313 		goto out;
314 	}
315 
316 	off_ctrl_params = nrf_wifi_osal_mem_zalloc(sizeof(*off_ctrl_params));
317 	if (!off_ctrl_params) {
318 		LOG_ERR("%s: Failed to allocate memory for off_ctrl_params", __func__);
319 		goto out;
320 	}
321 
322 	key = k_spin_lock(&off_raw_tx_drv_priv.lock);
323 
324 	fmac_dev_ctx = off_raw_tx_drv_priv.rpu_ctx_zep.rpu_ctx;
325 
326 	if (!fmac_dev_ctx) {
327 		LOG_ERR("%s: FMAC device context is NULL", __func__);
328 		goto out;
329 	}
330 
331 	off_tx_params = nrf_wifi_osal_mem_zalloc(sizeof(*off_tx_params));
332 	if (!off_tx_params) {
333 		LOG_ERR("%s Failed to allocate memory for off_tx_params: ", __func__);
334 		goto out;
335 	}
336 
337 	if (!validate_rate(conf->tput_mode, conf->rate)) {
338 		LOG_ERR("%s Invalid rate. Throughput mode: %d, rate: %d\n", __func__,
339 				      conf->tput_mode, conf->rate);
340 		goto out;
341 	}
342 
343 	off_ctrl_params->channel_no = conf->chan;
344 	off_ctrl_params->period_in_us = conf->period_us;
345 	off_ctrl_params->tx_pwr = conf->tx_pwr;
346 	off_tx_params->he_gi_type = conf->he_gi;
347 	off_tx_params->he_ltf = conf->he_ltf;
348 	off_tx_params->pkt_ram_ptr = RPU_MEM_PKT_BASE;
349 	off_tx_params->pkt_length = conf->pkt_len;
350 	off_tx_params->rate_flags = conf->tput_mode;
351 	off_tx_params->rate = valid_data_rates[conf->rate];
352 	off_tx_params->rate_preamble_type = conf->short_preamble;
353 	off_tx_params->rate_retries = conf->num_retries;
354 
355 	status = hal_rpu_mem_write(fmac_dev_ctx->hal_dev_ctx,
356 				   RPU_MEM_PKT_BASE,
357 				   conf->pkt,
358 				   conf->pkt_len);
359 	if (status != NRF_WIFI_STATUS_SUCCESS) {
360 		LOG_ERR("%s: hal_rpu_mem_write failed", __func__);
361 		goto out;
362 	}
363 
364 	status = nrf_wifi_off_raw_tx_fmac_conf(fmac_dev_ctx,
365 					       off_ctrl_params,
366 					       off_tx_params);
367 	if (status != NRF_WIFI_STATUS_SUCCESS) {
368 		LOG_ERR("%s: nRF70 offloaded raw TX configuration failed",
369 				      __func__);
370 		goto out;
371 	}
372 
373 	ret = 0;
374 out:
375 	nrf_wifi_osal_mem_free(off_ctrl_params);
376 	nrf_wifi_osal_mem_free(off_tx_params);
377 	k_spin_unlock(&off_raw_tx_drv_priv.lock, key);
378 	return ret;
379 }
380 
381 
nrf70_off_raw_tx_start(struct nrf_wifi_off_raw_tx_conf * conf)382 int nrf70_off_raw_tx_start(struct nrf_wifi_off_raw_tx_conf *conf)
383 {
384 	int ret = -1;
385 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
386 	k_spinlock_key_t key;
387 
388 	status = nrf70_off_raw_tx_conf_update(conf);
389 	if (status != NRF_WIFI_STATUS_SUCCESS) {
390 		LOG_ERR("%s: nRF70 offloaded raw TX configuration failed",
391 				      __func__);
392 		goto out;
393 	}
394 
395 	key = k_spin_lock(&off_raw_tx_drv_priv.lock);
396 	if (!off_raw_tx_drv_priv.rpu_ctx_zep.rpu_ctx) {
397 		LOG_ERR("%s: FMAC device context is NULL", __func__);
398 		goto out;
399 	}
400 
401 	status = nrf_wifi_off_raw_tx_fmac_start(off_raw_tx_drv_priv.rpu_ctx_zep.rpu_ctx);
402 	if (status != NRF_WIFI_STATUS_SUCCESS) {
403 		LOG_ERR("%s: nRF70 offloaded raw TX start failed",
404 				      __func__);
405 		goto out;
406 	}
407 
408 	ret = 0;
409 out:
410 	k_spin_unlock(&off_raw_tx_drv_priv.lock, key);
411 	return ret;
412 }
413 
414 
nrf70_off_raw_tx_stop(void)415 int nrf70_off_raw_tx_stop(void)
416 {
417 	int ret = -1;
418 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
419 	k_spinlock_key_t key;
420 
421 	key = k_spin_lock(&off_raw_tx_drv_priv.lock);
422 
423 	if (!off_raw_tx_drv_priv.rpu_ctx_zep.rpu_ctx) {
424 		LOG_ERR("%s: FMAC device context is NULL", __func__);
425 		goto out;
426 	}
427 
428 	status = nrf_wifi_off_raw_tx_fmac_stop(off_raw_tx_drv_priv.rpu_ctx_zep.rpu_ctx);
429 	if (status != NRF_WIFI_STATUS_SUCCESS) {
430 		LOG_ERR("%s: nRF70 offloaded raw TX stop failed",
431 				      __func__);
432 		goto out;
433 	}
434 
435 	ret = 0;
436 out:
437 	k_spin_unlock(&off_raw_tx_drv_priv.lock, key);
438 	return ret;
439 }
440 
441 
nrf70_off_raw_tx_mac_addr_get(uint8_t * mac_addr)442 int nrf70_off_raw_tx_mac_addr_get(uint8_t *mac_addr)
443 {
444 	if (!mac_addr) {
445 		LOG_ERR("%s: Invalid param", __func__);
446 		return -EINVAL;
447 	}
448 
449 	memcpy(mac_addr, off_raw_tx_drv_priv.rpu_ctx_zep.mac_addr, 6);
450 	return 0;
451 }
452 
nrf70_off_raw_tx_stats(struct nrf_wifi_off_raw_tx_stats * off_raw_tx_stats)453 int nrf70_off_raw_tx_stats(struct nrf_wifi_off_raw_tx_stats *off_raw_tx_stats)
454 {
455 	int ret = -1;
456 	enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
457 	struct rpu_off_raw_tx_op_stats stats;
458 	k_spinlock_key_t key;
459 
460 	memset(&stats, 0, sizeof(stats));
461 
462 	key = k_spin_lock(&off_raw_tx_drv_priv.lock);
463 
464 	if (!off_raw_tx_drv_priv.rpu_ctx_zep.rpu_ctx) {
465 		LOG_ERR("%s: FMAC device context is NULL", __func__);
466 		goto out;
467 	}
468 
469 	status = nrf_wifi_off_raw_tx_fmac_stats_get(off_raw_tx_drv_priv.rpu_ctx_zep.rpu_ctx,
470 						    0,
471 						    &stats);
472 	if (status != NRF_WIFI_STATUS_SUCCESS) {
473 		LOG_ERR("%s: nRF70 offloaded raw TX stats failed",
474 				      __func__);
475 		goto out;
476 	}
477 
478 	off_raw_tx_stats->off_raw_tx_pkt_sent = stats.fw.offload_raw_tx_cnt;
479 
480 	ret = 0;
481 out:
482 	k_spin_unlock(&off_raw_tx_drv_priv.lock, key);
483 	return ret;
484 }
485