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