1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @brief File containing WiFi management operation implementations
9 * for the Zephyr OS.
10 */
11
12 #include <stdlib.h>
13
14 #include <zephyr/kernel.h>
15 #include <zephyr/logging/log.h>
16
17 #include "util.h"
18 #include "fmac_api.h"
19 #include "fmac_tx.h"
20 #include "fmac_util.h"
21 #include "fmac_main.h"
22 #include "wifi_mgmt.h"
23
24 LOG_MODULE_DECLARE(wifi_nrf, CONFIG_WIFI_NRF70_LOG_LEVEL);
25
26 extern struct nrf_wifi_drv_priv_zep rpu_drv_priv_zep;
27
nrf_wifi_set_power_save(const struct device * dev,struct wifi_ps_params * params)28 int nrf_wifi_set_power_save(const struct device *dev,
29 struct wifi_ps_params *params)
30 {
31 enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
32 struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
33 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
34 int ret = -1;
35 unsigned int uapsd_queue = UAPSD_Q_MIN; /* Legacy mode */
36
37 if (!dev || !params) {
38 LOG_ERR("%s: dev or params is NULL", __func__);
39 return ret;
40 }
41
42 vif_ctx_zep = dev->data;
43
44 if (!vif_ctx_zep) {
45 LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
46 return ret;
47 }
48
49 rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
50
51 if (!rpu_ctx_zep) {
52 LOG_ERR("%s: rpu_ctx_zep is NULL", __func__);
53 return ret;
54 }
55
56 k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
57 if (!rpu_ctx_zep->rpu_ctx) {
58 LOG_DBG("%s: RPU context not initialized", __func__);
59 goto out;
60 }
61
62 switch (params->type) {
63 case WIFI_PS_PARAM_LISTEN_INTERVAL:
64 if ((params->listen_interval <
65 NRF_WIFI_LISTEN_INTERVAL_MIN) ||
66 (params->listen_interval >
67 WIFI_LISTEN_INTERVAL_MAX)) {
68 params->fail_reason =
69 WIFI_PS_PARAM_LISTEN_INTERVAL_RANGE_INVALID;
70 return -EINVAL;
71 }
72 status = nrf_wifi_fmac_set_listen_interval(
73 rpu_ctx_zep->rpu_ctx,
74 vif_ctx_zep->vif_idx,
75 params->listen_interval);
76 break;
77 case WIFI_PS_PARAM_TIMEOUT:
78 if ((vif_ctx_zep->if_type != NRF_WIFI_IFTYPE_STATION)
79 #ifdef CONFIG_NRF70_RAW_DATA_TX
80 && (vif_ctx_zep->if_type != NRF_WIFI_STA_TX_INJECTOR)
81 #endif /* CONFIG_NRF70_RAW_DATA_TX */
82 #ifdef CONFIG_NRF70_PROMISC_DATA_RX
83 && (vif_ctx_zep->if_type != NRF_WIFI_STA_PROMISC_TX_INJECTOR)
84 #endif /* CONFIG_NRF70_PROMISC_DATA_RX */
85 ) {
86 LOG_ERR("%s: Operation supported only in STA enabled mode",
87 __func__);
88 params->fail_reason =
89 WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL;
90 goto out;
91 }
92
93 status = nrf_wifi_fmac_set_power_save_timeout(
94 rpu_ctx_zep->rpu_ctx,
95 vif_ctx_zep->vif_idx,
96 params->timeout_ms);
97 break;
98 case WIFI_PS_PARAM_MODE:
99 if (params->mode == WIFI_PS_MODE_WMM) {
100 uapsd_queue = UAPSD_Q_MAX; /* WMM mode */
101 }
102
103 status = nrf_wifi_fmac_set_uapsd_queue(rpu_ctx_zep->rpu_ctx,
104 vif_ctx_zep->vif_idx,
105 uapsd_queue);
106 break;
107 case WIFI_PS_PARAM_STATE:
108 status = nrf_wifi_fmac_set_power_save(rpu_ctx_zep->rpu_ctx,
109 vif_ctx_zep->vif_idx,
110 params->enabled);
111 break;
112 case WIFI_PS_PARAM_WAKEUP_MODE:
113 status = nrf_wifi_fmac_set_ps_wakeup_mode(
114 rpu_ctx_zep->rpu_ctx,
115 vif_ctx_zep->vif_idx,
116 params->wakeup_mode);
117 break;
118 case WIFI_PS_PARAM_EXIT_STRATEGY:
119 unsigned int exit_strategy;
120
121 if (params->exit_strategy == WIFI_PS_EXIT_EVERY_TIM) {
122 exit_strategy = EVERY_TIM;
123 } else if (params->exit_strategy == WIFI_PS_EXIT_CUSTOM_ALGO) {
124 exit_strategy = INT_PS;
125 } else {
126 params->fail_reason =
127 WIFI_PS_PARAM_FAIL_INVALID_EXIT_STRATEGY;
128 return -EINVAL;
129 }
130
131 status = nrf_wifi_fmac_set_ps_exit_strategy(
132 rpu_ctx_zep->rpu_ctx,
133 vif_ctx_zep->vif_idx,
134 exit_strategy);
135 break;
136 default:
137 params->fail_reason =
138 WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL;
139 return -ENOTSUP;
140 }
141
142 if (status != NRF_WIFI_STATUS_SUCCESS) {
143 LOG_ERR("%s: Confiuring PS param %d failed",
144 __func__, params->type);
145 params->fail_reason =
146 WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL;
147 goto out;
148 }
149
150
151 ret = 0;
152 out:
153 k_mutex_unlock(&vif_ctx_zep->vif_lock);
154 return ret;
155 }
156
nrf_wifi_get_power_save_config(const struct device * dev,struct wifi_ps_config * ps_config)157 int nrf_wifi_get_power_save_config(const struct device *dev,
158 struct wifi_ps_config *ps_config)
159 {
160 enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
161 struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
162 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
163 struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
164 int ret = -1;
165 int count = 0;
166
167 if (!dev || !ps_config) {
168 return ret;
169 }
170
171 vif_ctx_zep = dev->data;
172
173 if (!vif_ctx_zep) {
174 LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
175 return ret;
176 }
177
178 if ((vif_ctx_zep->if_type != NRF_WIFI_IFTYPE_STATION)
179 #ifdef CONFIG_NRF70_RAW_DATA_TX
180 && (vif_ctx_zep->if_type != NRF_WIFI_STA_TX_INJECTOR)
181 #endif /* CONFIG_NRF70_RAW_DATA_TX */
182 #ifdef CONFIG_NRF70_PROMISC_DATA_RX
183 && (vif_ctx_zep->if_type != NRF_WIFI_STA_PROMISC_TX_INJECTOR)
184 #endif /* CONFIG_NRF70_PROMISC_DATA_RX */
185 ) {
186 LOG_ERR("%s: Operation supported only in STA enabled mode",
187 __func__);
188 return ret;
189 }
190
191 rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
192 if (!rpu_ctx_zep) {
193 LOG_ERR("%s: rpu_ctx_zep is NULL", __func__);
194 return ret;
195 }
196
197 k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
198 if (!rpu_ctx_zep->rpu_ctx) {
199 LOG_DBG("%s: RPU context not initialized", __func__);
200 goto out;
201 }
202
203 fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
204
205 if (!rpu_ctx_zep) {
206 LOG_ERR("%s: rpu_ctx_zep is NULL", __func__);
207 goto out;
208 }
209
210 vif_ctx_zep->ps_info = ps_config;
211
212 vif_ctx_zep->ps_config_info_evnt = false;
213
214 status = nrf_wifi_fmac_get_power_save_info(rpu_ctx_zep->rpu_ctx,
215 vif_ctx_zep->vif_idx);
216
217 if (status != NRF_WIFI_STATUS_SUCCESS) {
218 LOG_ERR("%s: nrf_wifi_fmac_get_power_save_info failed",
219 __func__);
220 goto out;
221 }
222
223 do {
224 nrf_wifi_osal_sleep_ms(1);
225 count++;
226 } while ((vif_ctx_zep->ps_config_info_evnt == false) &&
227 (count < NRF_WIFI_FMAC_PS_CONF_EVNT_RECV_TIMEOUT));
228
229 if (count == NRF_WIFI_FMAC_PS_CONF_EVNT_RECV_TIMEOUT) {
230 nrf_wifi_osal_log_err("%s: Timed out",
231 __func__);
232 goto out;
233 }
234
235 ret = 0;
236 out:
237 k_mutex_unlock(&vif_ctx_zep->vif_lock);
238 return ret;
239 }
240
241 /* TWT interval conversion helpers: User <-> Protocol */
nrf_wifi_twt_us_to_float(uint32_t twt_interval)242 static struct twt_interval_float nrf_wifi_twt_us_to_float(uint32_t twt_interval)
243 {
244 double mantissa = 0.0;
245 int exponent = 0;
246 struct twt_interval_float twt_interval_fp;
247
248 double twt_interval_ms = twt_interval / 1000.0;
249
250 mantissa = frexp(twt_interval_ms, &exponent);
251 /* Ceiling and conversion to milli seconds */
252 twt_interval_fp.mantissa = ceil(mantissa * 1000);
253 twt_interval_fp.exponent = exponent;
254
255 return twt_interval_fp;
256 }
257
nrf_wifi_twt_float_to_us(struct twt_interval_float twt_interval_fp)258 static uint64_t nrf_wifi_twt_float_to_us(struct twt_interval_float twt_interval_fp)
259 {
260 /* Conversion to micro-seconds */
261 return floor(ldexp(twt_interval_fp.mantissa, twt_interval_fp.exponent) / (1000)) *
262 1000;
263 }
264
twt_wifi_mgmt_to_rpu_neg_type(enum wifi_twt_negotiation_type neg_type)265 static unsigned char twt_wifi_mgmt_to_rpu_neg_type(enum wifi_twt_negotiation_type neg_type)
266 {
267 unsigned char rpu_neg_type = 0;
268
269 switch (neg_type) {
270 case WIFI_TWT_INDIVIDUAL:
271 rpu_neg_type = NRF_WIFI_TWT_NEGOTIATION_TYPE_INDIVIDUAL;
272 break;
273 case WIFI_TWT_BROADCAST:
274 rpu_neg_type = NRF_WIFI_TWT_NEGOTIATION_TYPE_BROADCAST;
275 break;
276 default:
277 LOG_ERR("%s: Invalid negotiation type: %d",
278 __func__, neg_type);
279 break;
280 }
281
282 return rpu_neg_type;
283 }
284
twt_rpu_to_wifi_mgmt_neg_type(unsigned char neg_type)285 static enum wifi_twt_negotiation_type twt_rpu_to_wifi_mgmt_neg_type(unsigned char neg_type)
286 {
287 enum wifi_twt_negotiation_type wifi_neg_type = WIFI_TWT_INDIVIDUAL;
288
289 switch (neg_type) {
290 case NRF_WIFI_TWT_NEGOTIATION_TYPE_INDIVIDUAL:
291 wifi_neg_type = WIFI_TWT_INDIVIDUAL;
292 break;
293 case NRF_WIFI_TWT_NEGOTIATION_TYPE_BROADCAST:
294 wifi_neg_type = WIFI_TWT_BROADCAST;
295 break;
296 default:
297 LOG_ERR("%s: Invalid negotiation type: %d",
298 __func__, neg_type);
299 break;
300 }
301
302 return wifi_neg_type;
303 }
304
305 /* Though setup_cmd enums have 1-1 mapping but due to data type different need these */
twt_rpu_to_wifi_mgmt_setup_cmd(signed int setup_cmd)306 static enum wifi_twt_setup_cmd twt_rpu_to_wifi_mgmt_setup_cmd(signed int setup_cmd)
307 {
308 enum wifi_twt_setup_cmd wifi_setup_cmd = WIFI_TWT_SETUP_CMD_REQUEST;
309
310 switch (setup_cmd) {
311 case NRF_WIFI_REQUEST_TWT:
312 wifi_setup_cmd = WIFI_TWT_SETUP_CMD_REQUEST;
313 break;
314 case NRF_WIFI_SUGGEST_TWT:
315 wifi_setup_cmd = WIFI_TWT_SETUP_CMD_SUGGEST;
316 break;
317 case NRF_WIFI_DEMAND_TWT:
318 wifi_setup_cmd = WIFI_TWT_SETUP_CMD_DEMAND;
319 break;
320 case NRF_WIFI_GROUPING_TWT:
321 wifi_setup_cmd = WIFI_TWT_SETUP_CMD_GROUPING;
322 break;
323 case NRF_WIFI_ACCEPT_TWT:
324 wifi_setup_cmd = WIFI_TWT_SETUP_CMD_ACCEPT;
325 break;
326 case NRF_WIFI_ALTERNATE_TWT:
327 wifi_setup_cmd = WIFI_TWT_SETUP_CMD_ALTERNATE;
328 break;
329 case NRF_WIFI_DICTATE_TWT:
330 wifi_setup_cmd = WIFI_TWT_SETUP_CMD_DICTATE;
331 break;
332 case NRF_WIFI_REJECT_TWT:
333 wifi_setup_cmd = WIFI_TWT_SETUP_CMD_REJECT;
334 break;
335 default:
336 LOG_ERR("%s: Invalid setup command: %d",
337 __func__, setup_cmd);
338 break;
339 }
340
341 return wifi_setup_cmd;
342 }
343
twt_wifi_mgmt_to_rpu_setup_cmd(enum wifi_twt_setup_cmd setup_cmd)344 static signed int twt_wifi_mgmt_to_rpu_setup_cmd(enum wifi_twt_setup_cmd setup_cmd)
345 {
346 signed int rpu_setup_cmd = NRF_WIFI_REQUEST_TWT;
347
348 switch (setup_cmd) {
349 case WIFI_TWT_SETUP_CMD_REQUEST:
350 rpu_setup_cmd = NRF_WIFI_REQUEST_TWT;
351 break;
352 case WIFI_TWT_SETUP_CMD_SUGGEST:
353 rpu_setup_cmd = NRF_WIFI_SUGGEST_TWT;
354 break;
355 case WIFI_TWT_SETUP_CMD_DEMAND:
356 rpu_setup_cmd = NRF_WIFI_DEMAND_TWT;
357 break;
358 case WIFI_TWT_SETUP_CMD_GROUPING:
359 rpu_setup_cmd = NRF_WIFI_GROUPING_TWT;
360 break;
361 case WIFI_TWT_SETUP_CMD_ACCEPT:
362 rpu_setup_cmd = NRF_WIFI_ACCEPT_TWT;
363 break;
364 case WIFI_TWT_SETUP_CMD_ALTERNATE:
365 rpu_setup_cmd = NRF_WIFI_ALTERNATE_TWT;
366 break;
367 case WIFI_TWT_SETUP_CMD_DICTATE:
368 rpu_setup_cmd = NRF_WIFI_DICTATE_TWT;
369 break;
370 case WIFI_TWT_SETUP_CMD_REJECT:
371 rpu_setup_cmd = NRF_WIFI_REJECT_TWT;
372 break;
373 default:
374 LOG_ERR("%s: Invalid setup command: %d",
375 __func__, setup_cmd);
376 break;
377 }
378
379 return rpu_setup_cmd;
380 }
381
nrf_wifi_event_proc_get_power_save_info(void * vif_ctx,struct nrf_wifi_umac_event_power_save_info * ps_info,unsigned int event_len)382 void nrf_wifi_event_proc_get_power_save_info(void *vif_ctx,
383 struct nrf_wifi_umac_event_power_save_info *ps_info,
384 unsigned int event_len)
385 {
386 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
387
388 if (!vif_ctx || !ps_info) {
389 return;
390 }
391
392 vif_ctx_zep = vif_ctx;
393
394 vif_ctx_zep->ps_info->ps_params.mode = ps_info->ps_mode;
395 vif_ctx_zep->ps_info->ps_params.enabled = ps_info->enabled;
396 vif_ctx_zep->ps_info->num_twt_flows = ps_info->num_twt_flows;
397 vif_ctx_zep->ps_info->ps_params.timeout_ms = ps_info->ps_timeout;
398 vif_ctx_zep->ps_info->ps_params.listen_interval = ps_info->listen_interval;
399 vif_ctx_zep->ps_info->ps_params.wakeup_mode = ps_info->extended_ps;
400 if (ps_info->ps_exit_strategy == EVERY_TIM) {
401 vif_ctx_zep->ps_info->ps_params.exit_strategy = WIFI_PS_EXIT_EVERY_TIM;
402 } else if (ps_info->ps_exit_strategy == INT_PS) {
403 vif_ctx_zep->ps_info->ps_params.exit_strategy = WIFI_PS_EXIT_CUSTOM_ALGO;
404 }
405
406 for (int i = 0; i < ps_info->num_twt_flows; i++) {
407 struct twt_interval_float twt_interval_fp;
408 struct wifi_twt_flow_info *twt_zep = &vif_ctx_zep->ps_info->twt_flows[i];
409 struct nrf_wifi_umac_config_twt_info *twt_rpu = &ps_info->twt_flow_info[i];
410
411 memset(twt_zep, 0, sizeof(struct wifi_twt_flow_info));
412
413 twt_zep->flow_id = twt_rpu->twt_flow_id;
414 twt_zep->implicit = twt_rpu->is_implicit ? 1 : 0;
415 twt_zep->trigger = twt_rpu->ap_trigger_frame ? 1 : 0;
416 twt_zep->announce = twt_rpu->twt_flow_type == NRF_WIFI_TWT_FLOW_TYPE_ANNOUNCED;
417 twt_zep->negotiation_type = twt_rpu_to_wifi_mgmt_neg_type(twt_rpu->neg_type);
418 twt_zep->dialog_token = twt_rpu->dialog_token;
419 twt_interval_fp.mantissa = twt_rpu->twt_target_wake_interval_mantissa;
420 twt_interval_fp.exponent = twt_rpu->twt_target_wake_interval_exponent;
421 twt_zep->twt_interval = nrf_wifi_twt_float_to_us(twt_interval_fp);
422 twt_zep->twt_wake_interval = twt_rpu->nominal_min_twt_wake_duration;
423 }
424
425 vif_ctx_zep->ps_config_info_evnt = true;
426 }
427
nrf_wifi_twt_update_internal_state(struct nrf_wifi_vif_ctx_zep * vif_ctx_zep,bool setup,unsigned char flow_id)428 static void nrf_wifi_twt_update_internal_state(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep,
429 bool setup, unsigned char flow_id)
430 {
431 if (setup) {
432 vif_ctx_zep->twt_flows_map |= BIT(flow_id);
433 vif_ctx_zep->twt_flow_in_progress_map &= ~BIT(flow_id);
434 } else {
435 vif_ctx_zep->twt_flows_map &= ~BIT(flow_id);
436 }
437 }
438
nrf_wifi_twt_teardown_flows(struct nrf_wifi_vif_ctx_zep * vif_ctx_zep,unsigned char start_flow_id,unsigned char end_flow_id)439 int nrf_wifi_twt_teardown_flows(struct nrf_wifi_vif_ctx_zep *vif_ctx_zep,
440 unsigned char start_flow_id, unsigned char end_flow_id)
441 {
442 struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
443 struct nrf_wifi_umac_config_twt_info twt_info = {0};
444 enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
445 int ret = 0;
446 struct wifi_twt_params twt_params = {0};
447
448 if (!vif_ctx_zep) {
449 LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
450 return ret;
451 }
452
453 rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
454
455 if (!rpu_ctx_zep) {
456 LOG_ERR("%s: rpu_ctx_zep is NULL", __func__);
457 return ret;
458 }
459
460 k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
461 if (!rpu_ctx_zep->rpu_ctx) {
462 LOG_DBG("%s: RPU context not initialized", __func__);
463 goto out;
464 }
465
466 for (int flow_id = start_flow_id; flow_id < end_flow_id; flow_id++) {
467 if (!(vif_ctx_zep->twt_flows_map & BIT(flow_id))) {
468 continue;
469 }
470 twt_info.twt_flow_id = flow_id;
471 status = nrf_wifi_fmac_twt_teardown(rpu_ctx_zep->rpu_ctx,
472 vif_ctx_zep->vif_idx,
473 &twt_info);
474 if (status != NRF_WIFI_STATUS_SUCCESS) {
475 LOG_ERR("%s: TWT teardown for flow id %d failed",
476 __func__, flow_id);
477 ret = -1;
478 continue;
479 }
480 /* UMAC doesn't send TWT teardown event for host initiated teardown */
481 nrf_wifi_twt_update_internal_state(vif_ctx_zep, false, flow_id);
482 /* TODO: Remove this once UMAC sends the status */
483 twt_params.operation = WIFI_TWT_TEARDOWN;
484 twt_params.flow_id = flow_id;
485 twt_params.teardown_status = WIFI_TWT_TEARDOWN_SUCCESS;
486 wifi_mgmt_raise_twt_event(vif_ctx_zep->zep_net_if_ctx, &twt_params);
487 }
488
489 out:
490 k_mutex_unlock(&vif_ctx_zep->vif_lock);
491 return ret;
492 }
493
nrf_wifi_set_twt(const struct device * dev,struct wifi_twt_params * twt_params)494 int nrf_wifi_set_twt(const struct device *dev,
495 struct wifi_twt_params *twt_params)
496 {
497 enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
498 struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
499 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
500 struct nrf_wifi_umac_config_twt_info twt_info = {0};
501 int ret = -1;
502
503 if (!dev || !twt_params) {
504 LOG_ERR("%s: dev or twt_params is NULL", __func__);
505 return ret;
506 }
507
508 vif_ctx_zep = dev->data;
509
510 if (!vif_ctx_zep) {
511 LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
512 return ret;
513 }
514
515 rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
516
517 if (!rpu_ctx_zep) {
518 LOG_ERR("%s: rpu_ctx_zep is NULL", __func__);
519 return ret;
520 }
521
522 k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
523 if (!rpu_ctx_zep->rpu_ctx) {
524 LOG_DBG("%s: RPU context not initialized", __func__);
525 goto out;
526 }
527
528 if (!(twt_params->operation == WIFI_TWT_TEARDOWN && twt_params->teardown.teardown_all) &&
529 twt_params->flow_id >= WIFI_MAX_TWT_FLOWS) {
530 LOG_ERR("%s: Invalid flow id: %d",
531 __func__, twt_params->flow_id);
532 twt_params->fail_reason = WIFI_TWT_FAIL_INVALID_FLOW_ID;
533 goto out;
534 }
535
536 switch (twt_params->operation) {
537 case WIFI_TWT_SETUP:
538 if (vif_ctx_zep->twt_flow_in_progress_map & BIT(twt_params->flow_id)) {
539 twt_params->fail_reason = WIFI_TWT_FAIL_OPERATION_IN_PROGRESS;
540 goto out;
541 }
542
543 if (twt_params->setup_cmd == WIFI_TWT_SETUP_CMD_REQUEST) {
544 if (vif_ctx_zep->twt_flows_map & BIT(twt_params->flow_id)) {
545 twt_params->fail_reason = WIFI_TWT_FAIL_FLOW_ALREADY_EXISTS;
546 goto out;
547 }
548 }
549
550 struct twt_interval_float twt_interval_fp =
551 nrf_wifi_twt_us_to_float(twt_params->setup.twt_interval);
552
553 twt_info.twt_flow_id = twt_params->flow_id;
554 twt_info.neg_type = twt_wifi_mgmt_to_rpu_neg_type(twt_params->negotiation_type);
555 twt_info.setup_cmd = twt_wifi_mgmt_to_rpu_setup_cmd(twt_params->setup_cmd);
556 twt_info.ap_trigger_frame = twt_params->setup.trigger;
557 twt_info.is_implicit = twt_params->setup.implicit;
558 if (twt_params->setup.announce) {
559 twt_info.twt_flow_type = NRF_WIFI_TWT_FLOW_TYPE_ANNOUNCED;
560 } else {
561 twt_info.twt_flow_type = NRF_WIFI_TWT_FLOW_TYPE_UNANNOUNCED;
562 }
563
564 twt_info.nominal_min_twt_wake_duration =
565 twt_params->setup.twt_wake_interval;
566 twt_info.twt_target_wake_interval_mantissa = twt_interval_fp.mantissa;
567 twt_info.twt_target_wake_interval_exponent = twt_interval_fp.exponent;
568
569 twt_info.dialog_token = twt_params->dialog_token;
570 twt_info.twt_wake_ahead_duration = twt_params->setup.twt_wake_ahead_duration;
571
572 status = nrf_wifi_fmac_twt_setup(rpu_ctx_zep->rpu_ctx,
573 vif_ctx_zep->vif_idx,
574 &twt_info);
575
576 break;
577 case WIFI_TWT_TEARDOWN:
578 unsigned char start_flow_id = 0;
579 unsigned char end_flow_id = WIFI_MAX_TWT_FLOWS;
580
581 if (!twt_params->teardown.teardown_all) {
582 if (!(vif_ctx_zep->twt_flows_map & BIT(twt_params->flow_id))) {
583 twt_params->fail_reason = WIFI_TWT_FAIL_INVALID_FLOW_ID;
584 goto out;
585 }
586 start_flow_id = twt_params->flow_id;
587 end_flow_id = twt_params->flow_id + 1;
588 twt_info.twt_flow_id = twt_params->flow_id;
589 }
590
591 status = nrf_wifi_twt_teardown_flows(vif_ctx_zep,
592 start_flow_id,
593 end_flow_id);
594 if (status != NRF_WIFI_STATUS_SUCCESS) {
595 LOG_ERR("%s: TWT teardown failed: start_flow_id: %d, end_flow_id: %d",
596 __func__, start_flow_id, end_flow_id);
597 goto out;
598 }
599 break;
600
601 default:
602 LOG_ERR("Unknown TWT operation");
603 status = NRF_WIFI_STATUS_FAIL;
604 break;
605 }
606
607 if (status != NRF_WIFI_STATUS_SUCCESS) {
608 LOG_ERR("%s: Failed", __func__);
609 goto out;
610 }
611
612 ret = 0;
613 out:
614 k_mutex_unlock(&vif_ctx_zep->vif_lock);
615 return ret;
616 }
617
nrf_wifi_event_proc_twt_setup_zep(void * vif_ctx,struct nrf_wifi_umac_cmd_config_twt * twt_setup_info,unsigned int event_len)618 void nrf_wifi_event_proc_twt_setup_zep(void *vif_ctx,
619 struct nrf_wifi_umac_cmd_config_twt *twt_setup_info,
620 unsigned int event_len)
621 {
622 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
623 struct wifi_twt_params twt_params = { 0 };
624 struct twt_interval_float twt_interval_fp = { 0 };
625
626 if (!vif_ctx || !twt_setup_info) {
627 return;
628 }
629
630 vif_ctx_zep = vif_ctx;
631
632 twt_params.operation = WIFI_TWT_SETUP;
633 twt_params.flow_id = twt_setup_info->info.twt_flow_id;
634 twt_params.negotiation_type = twt_rpu_to_wifi_mgmt_neg_type(twt_setup_info->info.neg_type);
635 twt_params.setup_cmd = twt_rpu_to_wifi_mgmt_setup_cmd(twt_setup_info->info.setup_cmd);
636 twt_params.setup.trigger = twt_setup_info->info.ap_trigger_frame ? 1 : 0;
637 twt_params.setup.implicit = twt_setup_info->info.is_implicit ? 1 : 0;
638 twt_params.setup.announce =
639 twt_setup_info->info.twt_flow_type == NRF_WIFI_TWT_FLOW_TYPE_ANNOUNCED;
640 twt_params.setup.twt_wake_interval =
641 twt_setup_info->info.nominal_min_twt_wake_duration;
642 twt_interval_fp.mantissa = twt_setup_info->info.twt_target_wake_interval_mantissa;
643 twt_interval_fp.exponent = twt_setup_info->info.twt_target_wake_interval_exponent;
644 twt_params.setup.twt_interval = nrf_wifi_twt_float_to_us(twt_interval_fp);
645 twt_params.dialog_token = twt_setup_info->info.dialog_token;
646 twt_params.resp_status = twt_setup_info->info.twt_resp_status;
647
648 if ((twt_setup_info->info.twt_resp_status == 0) ||
649 (twt_setup_info->info.neg_type == NRF_WIFI_ACCEPT_TWT)) {
650 nrf_wifi_twt_update_internal_state(vif_ctx_zep, true, twt_params.flow_id);
651 }
652
653 wifi_mgmt_raise_twt_event(vif_ctx_zep->zep_net_if_ctx, &twt_params);
654 }
655
656
nrf_wifi_event_proc_twt_teardown_zep(void * vif_ctx,struct nrf_wifi_umac_cmd_teardown_twt * twt_teardown_info,unsigned int event_len)657 void nrf_wifi_event_proc_twt_teardown_zep(void *vif_ctx,
658 struct nrf_wifi_umac_cmd_teardown_twt *twt_teardown_info,
659 unsigned int event_len)
660 {
661 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
662 struct wifi_twt_params twt_params = {0};
663
664 if (!vif_ctx || !twt_teardown_info) {
665 return;
666 }
667
668 vif_ctx_zep = vif_ctx;
669
670 twt_params.operation = WIFI_TWT_TEARDOWN;
671 twt_params.flow_id = twt_teardown_info->info.twt_flow_id;
672 /* TODO: ADD reason code in the twt_params structure */
673 nrf_wifi_twt_update_internal_state(vif_ctx_zep, false, twt_params.flow_id);
674
675 wifi_mgmt_raise_twt_event(vif_ctx_zep->zep_net_if_ctx, &twt_params);
676 }
677
nrf_wifi_event_proc_twt_sleep_zep(void * vif_ctx,struct nrf_wifi_umac_event_twt_sleep * sleep_evnt,unsigned int event_len)678 void nrf_wifi_event_proc_twt_sleep_zep(void *vif_ctx,
679 struct nrf_wifi_umac_event_twt_sleep *sleep_evnt,
680 unsigned int event_len)
681 {
682 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
683 struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
684 struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
685 struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
686 struct nrf_wifi_fmac_priv_def *def_priv = NULL;
687 #ifdef CONFIG_NRF70_DATA_TX
688 int desc = 0;
689 int ac = 0;
690 #endif
691 vif_ctx_zep = vif_ctx;
692
693 if (!vif_ctx_zep) {
694 LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
695 return;
696 }
697
698 rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
699 if (!rpu_ctx_zep) {
700 LOG_ERR("%s: rpu_ctx_zep is NULL", __func__);
701 return;
702 }
703
704 k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
705 if (!rpu_ctx_zep->rpu_ctx) {
706 LOG_DBG("%s: RPU context not initialized", __func__);
707 goto out;
708 }
709
710 fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
711 def_dev_ctx = wifi_dev_priv(fmac_dev_ctx);
712 def_priv = wifi_fmac_priv(fmac_dev_ctx->fpriv);
713
714 if (!sleep_evnt) {
715 LOG_ERR("%s: sleep_evnt is NULL", __func__);
716 return;
717 }
718
719 switch (sleep_evnt->info.type) {
720 case TWT_BLOCK_TX:
721 nrf_wifi_osal_spinlock_take(def_dev_ctx->tx_config.tx_lock);
722
723 def_dev_ctx->twt_sleep_status = NRF_WIFI_FMAC_TWT_STATE_SLEEP;
724
725 wifi_mgmt_raise_twt_sleep_state(vif_ctx_zep->zep_net_if_ctx,
726 WIFI_TWT_STATE_SLEEP);
727 nrf_wifi_osal_spinlock_rel(def_dev_ctx->tx_config.tx_lock);
728 break;
729 case TWT_UNBLOCK_TX:
730 nrf_wifi_osal_spinlock_take(def_dev_ctx->tx_config.tx_lock);
731 def_dev_ctx->twt_sleep_status = NRF_WIFI_FMAC_TWT_STATE_AWAKE;
732 wifi_mgmt_raise_twt_sleep_state(vif_ctx_zep->zep_net_if_ctx,
733 WIFI_TWT_STATE_AWAKE);
734 #ifdef CONFIG_NRF70_DATA_TX
735 for (ac = NRF_WIFI_FMAC_AC_BE;
736 ac <= NRF_WIFI_FMAC_AC_MAX; ++ac) {
737 desc = tx_desc_get(fmac_dev_ctx, ac);
738 if (desc < def_priv->num_tx_tokens) {
739 tx_pending_process(fmac_dev_ctx, desc, ac);
740 }
741 }
742 #endif
743 nrf_wifi_osal_spinlock_rel(def_dev_ctx->tx_config.tx_lock);
744 break;
745 default:
746 break;
747 }
748 out:
749 k_mutex_unlock(&vif_ctx_zep->vif_lock);
750 }
751
752 #ifdef CONFIG_NRF70_SYSTEM_WITH_RAW_MODES
nrf_wifi_mode(const struct device * dev,struct wifi_mode_info * mode)753 int nrf_wifi_mode(const struct device *dev,
754 struct wifi_mode_info *mode)
755 {
756 enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
757 struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
758 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
759 struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
760 struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
761 int ret = -1;
762
763 if (!dev || !mode) {
764 LOG_ERR("%s: illegal input parameters", __func__);
765 return ret;
766 }
767
768 vif_ctx_zep = dev->data;
769 if (!vif_ctx_zep) {
770 LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
771 return ret;
772 }
773
774 rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
775 if (!rpu_ctx_zep) {
776 LOG_ERR("%s: rpu_ctx_zep is NULL", __func__);
777 return ret;
778 }
779
780 k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
781 if (!rpu_ctx_zep->rpu_ctx) {
782 LOG_DBG("%s: RPU context not initialized", __func__);
783 goto out;
784 }
785
786 fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
787 def_dev_ctx = wifi_dev_priv(fmac_dev_ctx);
788
789 if (!device_is_ready(dev)) {
790 LOG_ERR("%s: Device %s is not ready",
791 __func__, dev->name);
792 goto out;
793 }
794
795 if (mode->oper == WIFI_MGMT_SET) {
796 status = nrf_wifi_check_mode_validity(mode->mode);
797 if (status != NRF_WIFI_STATUS_SUCCESS) {
798 LOG_ERR("%s: mode setting is not valid", __func__);
799 goto out;
800 }
801
802 if (vif_ctx_zep->authorized && (mode->mode == NRF_WIFI_MONITOR_MODE)) {
803 LOG_ERR("%s: Cannot set monitor mode when station is connected",
804 __func__);
805 goto out;
806 }
807
808 /**
809 * Send the driver vif_idx instead of upper layer sent if_index.
810 * we map network if_index 1 to vif_idx of 0 and so on. The vif_ctx_zep
811 * context maps the correct network interface index to current driver
812 * interface index.
813 */
814 status = nrf_wifi_fmac_set_mode(rpu_ctx_zep->rpu_ctx,
815 vif_ctx_zep->vif_idx, mode->mode);
816 if (status != NRF_WIFI_STATUS_SUCCESS) {
817 LOG_ERR("%s: mode set operation failed", __func__);
818 goto out;
819 }
820
821 } else {
822 mode->mode = def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode;
823 /**
824 * This is a work-around to handle current UMAC mode handling.
825 * This might be removed in future versions when UMAC has more space.
826 */
827 #ifdef CONFIG_NRF70_RAW_DATA_TX
828 if (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->txinjection_mode == true) {
829 mode->mode ^= NRF_WIFI_TX_INJECTION_MODE;
830 }
831 #endif /* CONFIG_NRF70_RAW_DATA_TX */
832 #ifdef CONFIG_NRF70_PROMISC_DATA_RX
833 if (def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->promisc_mode == true) {
834 mode->mode ^= NRF_WIFI_PROMISCUOUS_MODE;
835 }
836 #endif /* CONFIG_NRF70_PROMISC_DATA_RX */
837 }
838 ret = 0;
839 out:
840 k_mutex_unlock(&vif_ctx_zep->vif_lock);
841 return ret;
842 }
843 #endif /* CONFIG_NRF70_SYSTEM_WITH_RAW_MODES */
844
845 #if defined(CONFIG_NRF70_RAW_DATA_TX) || defined(CONFIG_NRF70_RAW_DATA_RX)
nrf_wifi_channel(const struct device * dev,struct wifi_channel_info * channel)846 int nrf_wifi_channel(const struct device *dev,
847 struct wifi_channel_info *channel)
848 {
849 enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
850 struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
851 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
852 struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
853 struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
854 int ret = -1;
855
856 if (!dev || !channel) {
857 LOG_ERR("%s: illegal input parameters", __func__);
858 return ret;
859 }
860
861 vif_ctx_zep = dev->data;
862 if (!vif_ctx_zep) {
863 LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
864 return ret;
865 }
866
867 if (vif_ctx_zep->authorized) {
868 LOG_ERR("%s: Cannot change channel when in station connected mode", __func__);
869 return ret;
870 }
871
872 rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
873 if (!rpu_ctx_zep) {
874 LOG_ERR("%s: rpu_ctx_zep is NULL", __func__);
875 return ret;
876 }
877
878 k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
879 if (!rpu_ctx_zep->rpu_ctx) {
880 LOG_DBG("%s: RPU context not initialized", __func__);
881 goto out;
882 }
883
884 fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
885 def_dev_ctx = wifi_dev_priv(fmac_dev_ctx);
886
887 if (channel->oper == WIFI_MGMT_SET) {
888 /**
889 * Send the driver vif_idx instead of upper layer sent if_index.
890 * we map network if_index 1 to vif_idx of 0 and so on. The vif_ctx_zep
891 * context maps the correct network interface index to current driver
892 * interface index.
893 */
894 status = nrf_wifi_fmac_set_channel(rpu_ctx_zep->rpu_ctx, vif_ctx_zep->vif_idx,
895 channel->channel);
896
897 if (status != NRF_WIFI_STATUS_SUCCESS) {
898 LOG_ERR("%s: set channel failed", __func__);
899 goto out;
900 }
901 } else {
902 channel->channel = def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->channel;
903 }
904 ret = 0;
905 out:
906 k_mutex_unlock(&vif_ctx_zep->vif_lock);
907 return ret;
908 }
909 #endif /* CONFIG_NRF70_RAW_DATA_TX || CONFIG_NRF70_RAW_DATA_RX */
910
911 #if defined(CONFIG_NRF70_RAW_DATA_RX) || defined(CONFIG_NRF70_PROMISC_DATA_RX)
nrf_wifi_filter(const struct device * dev,struct wifi_filter_info * filter)912 int nrf_wifi_filter(const struct device *dev,
913 struct wifi_filter_info *filter)
914 {
915 enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
916 struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
917 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
918 struct nrf_wifi_fmac_dev_ctx *fmac_dev_ctx = NULL;
919 struct nrf_wifi_fmac_dev_ctx_def *def_dev_ctx = NULL;
920 int ret = -1;
921
922 if (!dev || !filter) {
923 LOG_ERR("%s: Illegal input parameters", __func__);
924 goto out;
925 }
926
927 vif_ctx_zep = dev->data;
928 if (!vif_ctx_zep) {
929 LOG_ERR("%s: vif_ctx_zep is NULL\n", __func__);
930 goto out;
931 }
932
933 rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
934 fmac_dev_ctx = rpu_ctx_zep->rpu_ctx;
935 def_dev_ctx = wifi_dev_priv(fmac_dev_ctx);
936
937 if (filter->oper == WIFI_MGMT_SET) {
938 /**
939 * If promiscuous mode is enabled, filter settings
940 * cannot be plumbed to the lower layers as that might
941 * affect connectivity. Save the filter settings in the
942 * driver and filter packet type on packet receive by
943 * checking the 802.11 header in the packet
944 */
945 if (((def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->mode) &
946 (NRF_WIFI_PROMISCUOUS_MODE)) == NRF_WIFI_PROMISCUOUS_MODE) {
947 def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->packet_filter =
948 filter->filter;
949 ret = 0;
950 goto out;
951 }
952
953 /**
954 * In case a user sets data + management + ctrl bits
955 * or all the filter bits. Map it to bit 0 set to
956 * enable "all" packet filter bit setting.
957 * In case only filter packet size is configured and filter
958 * setting is sent as zero, set the filter value to
959 * previously configured value.
960 */
961 if (filter->filter == WIFI_MGMT_DATA_CTRL_FILTER_SETTING
962 || filter->filter == WIFI_ALL_FILTER_SETTING) {
963 filter->filter = 1;
964 } else if (filter->filter == 0) {
965 filter->filter =
966 def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->packet_filter;
967 }
968
969 /**
970 * Send the driver vif_idx instead of upper layer sent if_index.
971 * we map network if_index 1 to vif_idx of 0 and so on. The vif_ctx_zep
972 * context maps the correct network interface index to current driver
973 * interface index
974 */
975 status = nrf_wifi_fmac_set_packet_filter(rpu_ctx_zep->rpu_ctx, filter->filter,
976 vif_ctx_zep->vif_idx, filter->buffer_size);
977 if (status != NRF_WIFI_STATUS_SUCCESS) {
978 LOG_ERR("%s: Set filter operation failed\n", __func__);
979 goto out;
980 }
981 } else {
982 filter->filter = def_dev_ctx->vif_ctx[vif_ctx_zep->vif_idx]->packet_filter;
983 }
984 ret = 0;
985 out:
986 return ret;
987 }
988 #endif /* CONFIG_NRF70_RAW_DATA_RX || CONFIG_NRF70_PROMISC_DATA_RX */
989
nrf_wifi_set_rts_threshold(const struct device * dev,unsigned int rts_threshold)990 int nrf_wifi_set_rts_threshold(const struct device *dev,
991 unsigned int rts_threshold)
992 {
993 enum nrf_wifi_status status = NRF_WIFI_STATUS_FAIL;
994 struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL;
995 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
996 struct nrf_wifi_umac_set_wiphy_info wiphy_info;
997 int ret = -1;
998
999 if (!dev) {
1000 LOG_ERR("%s: dev is NULL", __func__);
1001 return ret;
1002 }
1003
1004 vif_ctx_zep = dev->data;
1005
1006 if (!vif_ctx_zep) {
1007 LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
1008 return ret;
1009 }
1010
1011 rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep;
1012
1013 if (!rpu_ctx_zep) {
1014 LOG_ERR("%s: rpu_ctx_zep is NULL", __func__);
1015 return ret;
1016 }
1017
1018
1019 if (!rpu_ctx_zep->rpu_ctx) {
1020 LOG_ERR("%s: RPU context not initialized", __func__);
1021 return ret;
1022 }
1023
1024 if ((int)rts_threshold < -1) {
1025 /* 0 or any positive value is passed to f/w.
1026 * For RTS off, -1 is passed to f/w.
1027 * All other negative values considered as invalid.
1028 */
1029 LOG_ERR("%s: Invalid threshold value : %d", __func__, (int)rts_threshold);
1030 return ret;
1031 }
1032
1033 memset(&wiphy_info, 0, sizeof(struct nrf_wifi_umac_set_wiphy_info));
1034
1035 wiphy_info.rts_threshold = (int)rts_threshold;
1036
1037 k_mutex_lock(&vif_ctx_zep->vif_lock, K_FOREVER);
1038
1039 status = nrf_wifi_fmac_set_wiphy_params(rpu_ctx_zep->rpu_ctx,
1040 vif_ctx_zep->vif_idx,
1041 &wiphy_info);
1042
1043 if (status != NRF_WIFI_STATUS_SUCCESS) {
1044 LOG_ERR("%s: Configuring rts threshold failed\n", __func__);
1045 goto out;
1046 }
1047
1048 vif_ctx_zep->rts_threshold_value = (int)rts_threshold;
1049
1050 ret = 0;
1051 out:
1052 k_mutex_unlock(&vif_ctx_zep->vif_lock);
1053
1054 return ret;
1055 }
1056
nrf_wifi_get_rts_threshold(const struct device * dev,unsigned int * rts_threshold)1057 int nrf_wifi_get_rts_threshold(const struct device *dev,
1058 unsigned int *rts_threshold)
1059 {
1060 struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL;
1061 int ret = -1;
1062
1063 if (!dev) {
1064 LOG_ERR("%s: dev is NULL", __func__);
1065 return ret;
1066 }
1067
1068 vif_ctx_zep = dev->data;
1069 if (!vif_ctx_zep) {
1070 LOG_ERR("%s: vif_ctx_zep is NULL", __func__);
1071 return ret;
1072 }
1073
1074 *rts_threshold = vif_ctx_zep->rts_threshold_value;
1075 ret = 0;
1076
1077 return ret;
1078 }
1079